18
18
//! This module contains the iceberg REST catalog implementation.
19
19
20
20
use std:: collections:: HashMap ;
21
+ use std:: future:: Future ;
21
22
use std:: str:: FromStr ;
22
23
23
24
use async_trait:: async_trait;
24
25
use iceberg:: io:: FileIO ;
25
26
use iceberg:: table:: Table ;
26
27
use iceberg:: {
27
- Catalog , Error , ErrorKind , Namespace , NamespaceIdent , Result , TableCommit , TableCreation ,
28
- TableIdent ,
28
+ Catalog , CatalogBuilder , Error , ErrorKind , Namespace , NamespaceIdent , Result , TableCommit ,
29
+ TableCreation , TableIdent ,
29
30
} ;
30
31
use itertools:: Itertools ;
31
32
use reqwest:: header:: {
@@ -51,6 +52,8 @@ const PATH_V1: &str = "v1";
51
52
/// Rest catalog configuration.
52
53
#[ derive( Clone , Debug , TypedBuilder ) ]
53
54
pub struct RestCatalogConfig {
55
+ #[ builder( default , setter( strip_option) ) ]
56
+ name : Option < String > ,
54
57
uri : String ,
55
58
56
59
#[ builder( default , setter( strip_option( fallback = warehouse_opt) ) ) ]
@@ -223,6 +226,74 @@ impl RestCatalogConfig {
223
226
}
224
227
}
225
228
229
+ /// Builder for [`RestCatalog`].
230
+ #[ derive( Debug ) ]
231
+ pub struct RestCatalogBuilder ( RestCatalogConfig ) ;
232
+
233
+ impl Default for RestCatalogBuilder {
234
+ fn default ( ) -> Self {
235
+ Self ( RestCatalogConfig {
236
+ name : None ,
237
+ uri : "" . to_string ( ) ,
238
+ warehouse : None ,
239
+ props : HashMap :: new ( ) ,
240
+ client : None ,
241
+ } )
242
+ }
243
+ }
244
+
245
+ impl CatalogBuilder for RestCatalogBuilder {
246
+ type C = RestCatalog ;
247
+
248
+ fn name ( mut self , name : impl Into < String > ) -> Self {
249
+ self . 0 . name = Some ( name. into ( ) ) ;
250
+ self
251
+ }
252
+
253
+ fn uri ( mut self , uri : impl Into < String > ) -> Self {
254
+ self . 0 . uri = uri. into ( ) ;
255
+ self
256
+ }
257
+
258
+ fn warehouse ( mut self , warehouse : impl Into < String > ) -> Self {
259
+ self . 0 . warehouse = Some ( warehouse. into ( ) ) ;
260
+ self
261
+ }
262
+
263
+ fn with_prop ( mut self , key : impl Into < String > , value : impl Into < String > ) -> Self {
264
+ self . 0 . props . insert ( key. into ( ) , value. into ( ) ) ;
265
+ self
266
+ }
267
+
268
+ fn build ( self ) -> impl Future < Output = Result < Self :: C > > {
269
+ let result = {
270
+ if self . 0 . name . is_none ( ) {
271
+ Err ( Error :: new (
272
+ ErrorKind :: DataInvalid ,
273
+ "Catalog name is required" ,
274
+ ) )
275
+ } else if self . 0 . uri . is_empty ( ) {
276
+ Err ( Error :: new (
277
+ ErrorKind :: DataInvalid ,
278
+ "Catalog uri is required" ,
279
+ ) )
280
+ } else {
281
+ Ok ( RestCatalog :: new ( self . 0 ) )
282
+ }
283
+ } ;
284
+
285
+ std:: future:: ready ( result)
286
+ }
287
+ }
288
+
289
+ impl RestCatalogBuilder {
290
+ /// Configures the catalog with a custom HTTP client.
291
+ pub fn with_client ( mut self , client : Client ) -> Self {
292
+ self . 0 . client = Some ( client) ;
293
+ self
294
+ }
295
+ }
296
+
226
297
#[ derive( Debug ) ]
227
298
struct RestContext {
228
299
client : HttpClient ,
@@ -2257,4 +2328,17 @@ mod tests {
2257
2328
config_mock. assert_async ( ) . await ;
2258
2329
update_table_mock. assert_async ( ) . await ;
2259
2330
}
2331
+
2332
+ #[ tokio:: test]
2333
+ async fn test_create_rest_catalog ( ) {
2334
+ let catalog = RestCatalogBuilder :: default ( )
2335
+ . name ( "test" )
2336
+ . uri ( "http://localhost:8080" )
2337
+ . with_client ( Client :: new ( ) )
2338
+ . with_prop ( "a" , "b" )
2339
+ . build ( )
2340
+ . await ;
2341
+
2342
+ assert ! ( catalog. is_ok( ) ) ;
2343
+ }
2260
2344
}
0 commit comments