@@ -22,7 +22,7 @@ use serde::Serialize;
2222use super :: HUGGINGFACE_SCHEME ;
2323use opendal_core:: raw:: * ;
2424
25- /// Repository type of Huggingface. Supports `model`, `dataset`, and `space `.
25+ /// Repository type of Huggingface. Supports `model`, `dataset`, `space`, and `bucket `.
2626/// [Reference](https://huggingface.co/docs/hub/repositories)
2727#[ derive( Debug , Clone , Copy , PartialEq , Eq , Serialize , Deserialize , Default ) ]
2828#[ serde( rename_all = "lowercase" ) ]
@@ -31,6 +31,7 @@ pub enum RepoType {
3131 Model ,
3232 Dataset ,
3333 Space ,
34+ Bucket ,
3435}
3536
3637impl RepoType {
@@ -39,6 +40,7 @@ impl RepoType {
3940 "model" | "models" => Ok ( Self :: Model ) ,
4041 "dataset" | "datasets" => Ok ( Self :: Dataset ) ,
4142 "space" | "spaces" => Ok ( Self :: Space ) ,
43+ "bucket" | "buckets" => Ok ( Self :: Bucket ) ,
4244 other => Err ( opendal_core:: Error :: new (
4345 opendal_core:: ErrorKind :: ConfigInvalid ,
4446 format ! ( "unknown repo type: {other}" ) ,
@@ -52,6 +54,7 @@ impl RepoType {
5254 Self :: Model => "model" ,
5355 Self :: Dataset => "dataset" ,
5456 Self :: Space => "space" ,
57+ Self :: Bucket => "bucket" ,
5558 }
5659 }
5760
@@ -60,6 +63,7 @@ impl RepoType {
6063 Self :: Model => "models" ,
6164 Self :: Dataset => "datasets" ,
6265 Self :: Space => "spaces" ,
66+ Self :: Bucket => "buckets" ,
6367 }
6468 }
6569}
@@ -98,42 +102,49 @@ impl HfRepo {
98102
99103 /// Build the paths-info API URL for this repository.
100104 pub fn paths_info_url ( & self , endpoint : & str ) -> String {
101- format ! (
102- "{}/api/{}/{}/paths-info/{}" ,
103- endpoint,
104- self . repo_type. as_plural_str( ) ,
105- & self . repo_id,
106- percent_encode_revision( self . revision( ) ) ,
107- )
108- }
109-
110- /// Build the Git LFS batch API URL for this repository.
111- ///
112- /// Pattern: `{endpoint}/{type_prefix}{repo_id}.git/info/lfs/objects/batch`
113- /// where type_prefix is "" for models, "datasets/" for datasets, "spaces/" for spaces.
114- pub fn lfs_batch_url ( & self , endpoint : & str ) -> String {
115- let type_prefix = match self . repo_type {
116- RepoType :: Model => "" ,
117- RepoType :: Dataset => "datasets/" ,
118- RepoType :: Space => "spaces/" ,
119- } ;
120- format ! (
121- "{}/{}{}.git/info/lfs/objects/batch" ,
122- endpoint, type_prefix, & self . repo_id,
123- )
105+ match self . repo_type {
106+ RepoType :: Bucket => {
107+ format ! ( "{}/api/buckets/{}/paths-info" , endpoint, & self . repo_id)
108+ }
109+ _ => {
110+ format ! (
111+ "{}/api/{}/{}/paths-info/{}" ,
112+ endpoint,
113+ self . repo_type. as_plural_str( ) ,
114+ & self . repo_id,
115+ percent_encode_revision( self . revision( ) ) ,
116+ )
117+ }
118+ }
124119 }
125120
126121 /// Build the XET token API URL for this repository.
127122 #[ cfg( feature = "xet" ) ]
128123 pub fn xet_token_url ( & self , endpoint : & str , token_type : & str ) -> String {
129- format ! (
130- "{}/api/{}/{}/xet-{}-token/{}" ,
131- endpoint,
132- self . repo_type. as_plural_str( ) ,
133- & self . repo_id,
134- token_type,
135- self . revision( ) ,
136- )
124+ match self . repo_type {
125+ RepoType :: Bucket => {
126+ format ! (
127+ "{}/api/buckets/{}/xet-{}-token" ,
128+ endpoint, & self . repo_id, token_type
129+ )
130+ }
131+ _ => {
132+ format ! (
133+ "{}/api/{}/{}/xet-{}-token/{}" ,
134+ endpoint,
135+ self . repo_type. as_plural_str( ) ,
136+ & self . repo_id,
137+ token_type,
138+ self . revision( ) ,
139+ )
140+ }
141+ }
142+ }
143+
144+ /// Build the bucket batch API URL for this repository.
145+ #[ cfg( feature = "xet" ) ]
146+ pub fn bucket_batch_url ( & self , endpoint : & str ) -> String {
147+ format ! ( "{}/api/buckets/{}/batch" , endpoint, & self . repo_id)
137148 }
138149}
139150
@@ -275,6 +286,12 @@ impl HfUri {
275286 endpoint, & self . repo. repo_id, revision, path
276287 )
277288 }
289+ RepoType :: Bucket => {
290+ format ! (
291+ "{}/buckets/{}/resolve/{}" ,
292+ endpoint, & self . repo. repo_id, path
293+ )
294+ }
278295 }
279296 }
280297
@@ -535,4 +552,56 @@ mod tests {
535552 assert ! ( p. repo. revision. is_none( ) ) ;
536553 assert_eq ! ( p. path, "" ) ;
537554 }
555+
556+ #[ test]
557+ fn resolve_buckets_prefix ( ) {
558+ let p = resolve ( "buckets/username/my_bucket" ) ;
559+ assert_eq ! ( p. repo. repo_type, RepoType :: Bucket ) ;
560+ assert_eq ! ( p. repo. repo_id, "username/my_bucket" ) ;
561+ assert ! ( p. repo. revision. is_none( ) ) ;
562+ assert_eq ! ( p. path, "" ) ;
563+ }
564+
565+ #[ test]
566+ fn resolve_buckets_with_path ( ) {
567+ let p = resolve ( "buckets/username/my_bucket/data/file.txt" ) ;
568+ assert_eq ! ( p. repo. repo_type, RepoType :: Bucket ) ;
569+ assert_eq ! ( p. repo. repo_id, "username/my_bucket" ) ;
570+ assert ! ( p. repo. revision. is_none( ) ) ;
571+ assert_eq ! ( p. path, "data/file.txt" ) ;
572+ }
573+
574+ #[ test]
575+ fn test_bucket_resolve_url ( ) {
576+ let p = resolve ( "buckets/user/bucket/file.txt" ) ;
577+ let url = p. resolve_url ( "https://huggingface.co" ) ;
578+ assert_eq ! (
579+ url,
580+ "https://huggingface.co/buckets/user/bucket/resolve/file.txt"
581+ ) ;
582+ }
583+
584+ #[ test]
585+ #[ cfg( feature = "xet" ) ]
586+ fn test_bucket_xet_token_urls ( ) {
587+ let p = resolve ( "buckets/user/bucket" ) ;
588+ let read_url = p. repo . xet_token_url ( "https://huggingface.co" , "read" ) ;
589+ let write_url = p. repo . xet_token_url ( "https://huggingface.co" , "write" ) ;
590+ assert_eq ! (
591+ read_url,
592+ "https://huggingface.co/api/buckets/user/bucket/xet-read-token"
593+ ) ;
594+ assert_eq ! (
595+ write_url,
596+ "https://huggingface.co/api/buckets/user/bucket/xet-write-token"
597+ ) ;
598+ }
599+
600+ #[ test]
601+ #[ cfg( feature = "xet" ) ]
602+ fn test_bucket_batch_url ( ) {
603+ let p = resolve ( "buckets/user/bucket" ) ;
604+ let url = p. repo . bucket_batch_url ( "https://huggingface.co" ) ;
605+ assert_eq ! ( url, "https://huggingface.co/api/buckets/user/bucket/batch" ) ;
606+ }
538607}
0 commit comments