@@ -309,10 +309,14 @@ impl AsyncListableStorageTraits for AsyncIcechunkStore {
309309 . map ( |item| {
310310 match item? {
311311 icechunk:: store:: ListDirItem :: Key ( key) => {
312- keys. push ( StoreKey :: new ( & key) ?) ;
312+ keys. push ( StoreKey :: new ( format ! ( "{}{}" , prefix . as_str ( ) , & key) ) ?) ;
313313 }
314- icechunk:: store:: ListDirItem :: Prefix ( prefix) => {
315- prefixes. push ( StorePrefix :: new ( prefix + "/" ) ?) ;
314+ icechunk:: store:: ListDirItem :: Prefix ( prefix_inner) => {
315+ prefixes. push ( StorePrefix :: new ( format ! (
316+ "{}{}/" ,
317+ prefix. as_str( ) ,
318+ & prefix_inner
319+ ) ) ?) ;
316320 }
317321 }
318322 Ok :: < _ , StorageError > ( ( ) )
@@ -426,4 +430,189 @@ mod tests {
426430
427431 Ok ( ( ) )
428432 }
433+
434+ #[ tokio:: test]
435+ async fn list_dir_and_list_prefix_nested ( ) -> Result < ( ) , Box < dyn Error > > {
436+ // Create an icechunk repository with a deeply nested zarr hierarchy
437+ let storage = icechunk:: new_in_memory_storage ( ) . await ?;
438+ let config = RepositoryConfig :: default ( ) ;
439+ let repo = Repository :: create ( Some ( config) , storage, HashMap :: new ( ) ) . await ?;
440+ let store = AsyncIcechunkStore :: new ( repo. writable_session ( "main" ) . await ?) ;
441+
442+ let group_json = r#"{"zarr_format":3,"node_type":"group"}"# ;
443+ let array_json = r#"{"zarr_format":3,"node_type":"array","shape":[10, 10],"data_type":"int32","chunk_grid":{"name":"regular","configuration":{"chunk_shape":[5, 5]}},"chunk_key_encoding":{"name":"default","configuration":{"separator":"/"}},"fill_value":0,"codecs":[{"name":"bytes","configuration":{"endian":"little"}}]}"# ;
444+
445+ // Create a deeply nested hierarchy:
446+ // root/
447+ // zarr.json
448+ // group0/
449+ // zarr.json
450+ // group1/
451+ // zarr.json
452+ // array0/
453+ // zarr.json
454+ // c/0/0
455+ // c/0/1
456+ // array1/
457+ // zarr.json
458+ // c/0/0
459+
460+ // Create groups
461+ store
462+ . set ( & StoreKey :: new ( "zarr.json" ) ?, group_json. into ( ) )
463+ . await ?;
464+ store
465+ . set ( & StoreKey :: new ( "group0/zarr.json" ) ?, group_json. into ( ) )
466+ . await ?;
467+ store
468+ . set (
469+ & StoreKey :: new ( "group0/group1/zarr.json" ) ?,
470+ group_json. into ( ) ,
471+ )
472+ . await ?;
473+
474+ // Create arrays
475+ store
476+ . set (
477+ & StoreKey :: new ( "group0/group1/array0/zarr.json" ) ?,
478+ array_json. into ( ) ,
479+ )
480+ . await ?;
481+ store
482+ . set (
483+ & StoreKey :: new ( "group0/group1/array0/c/0/0" ) ?,
484+ vec ! [ 0u8 ; 20 ] . into ( ) ,
485+ )
486+ . await ?;
487+ store
488+ . set (
489+ & StoreKey :: new ( "group0/group1/array0/c/0/1" ) ?,
490+ vec ! [ 1u8 ; 20 ] . into ( ) ,
491+ )
492+ . await ?;
493+
494+ store
495+ . set (
496+ & StoreKey :: new ( "group0/group1/array1/zarr.json" ) ?,
497+ array_json. into ( ) ,
498+ )
499+ . await ?;
500+ store
501+ . set (
502+ & StoreKey :: new ( "group0/group1/array1/c/0/0" ) ?,
503+ vec ! [ 2u8 ; 20 ] . into ( ) ,
504+ )
505+ . await ?;
506+
507+ // Commit the data
508+ store
509+ . session ( )
510+ . write ( )
511+ . await
512+ . commit ( "Create nested hierarchy" , None )
513+ . await ?;
514+
515+ // Test list_dir at root
516+ let root_items = store. list_dir ( & StorePrefix :: root ( ) ) . await ?;
517+ assert_eq ! ( root_items. keys( ) . len( ) , 1 ) ; // zarr.json
518+ assert_eq ! ( root_items. prefixes( ) . len( ) , 1 ) ; // group0/
519+ assert ! ( root_items. keys( ) . contains( & StoreKey :: new( "zarr.json" ) ?) ) ;
520+ assert ! ( root_items
521+ . prefixes( )
522+ . contains( & StorePrefix :: new( "group0/" ) ?) ) ;
523+
524+ // Test list_dir at group0
525+ let group0_items = store. list_dir ( & StorePrefix :: new ( "group0/" ) ?) . await ?;
526+ assert_eq ! ( group0_items. keys( ) . len( ) , 1 ) ; // zarr.json
527+ assert_eq ! ( group0_items. prefixes( ) . len( ) , 1 ) ; // group1/
528+ assert ! ( group0_items
529+ . keys( )
530+ . contains( & StoreKey :: new( "group0/zarr.json" ) ?) ) ;
531+ assert ! ( group0_items
532+ . prefixes( )
533+ . contains( & StorePrefix :: new( "group0/group1/" ) ?) ) ;
534+
535+ // Test list_dir at group1
536+ let group1_items = store. list_dir ( & StorePrefix :: new ( "group0/group1/" ) ?) . await ?;
537+ assert_eq ! ( group1_items. keys( ) . len( ) , 1 ) ; // zarr.json
538+ assert_eq ! ( group1_items. prefixes( ) . len( ) , 2 ) ; // array0/, array1/
539+ assert ! ( group1_items
540+ . keys( )
541+ . contains( & StoreKey :: new( "group0/group1/zarr.json" ) ?) ) ;
542+ assert ! ( group1_items
543+ . prefixes( )
544+ . contains( & StorePrefix :: new( "group0/group1/array0/" ) ?) ) ;
545+ assert ! ( group1_items
546+ . prefixes( )
547+ . contains( & StorePrefix :: new( "group0/group1/array1/" ) ?) ) ;
548+
549+ // Test list_dir inside array0
550+ let array0_items = store
551+ . list_dir ( & StorePrefix :: new ( "group0/group1/array0/" ) ?)
552+ . await ?;
553+ assert_eq ! ( array0_items. keys( ) . len( ) , 1 ) ; // zarr.json
554+ assert_eq ! ( array0_items. prefixes( ) . len( ) , 1 ) ; // c/
555+ assert ! ( array0_items
556+ . keys( )
557+ . contains( & StoreKey :: new( "group0/group1/array0/zarr.json" ) ?) ) ;
558+ assert ! ( array0_items
559+ . prefixes( )
560+ . contains( & StorePrefix :: new( "group0/group1/array0/c/" ) ?) ) ;
561+
562+ // Test list_dir inside array0/c/ directory
563+ let array0_c_items = store
564+ . list_dir ( & StorePrefix :: new ( "group0/group1/array0/c/" ) ?)
565+ . await ?;
566+ assert_eq ! ( array0_c_items. keys( ) . len( ) , 0 ) ; // no direct keys
567+ assert_eq ! ( array0_c_items. prefixes( ) . len( ) , 1 ) ; // 0/
568+ assert ! ( array0_c_items
569+ . prefixes( )
570+ . contains( & StorePrefix :: new( "group0/group1/array0/c/0/" ) ?) ) ;
571+
572+ // Test list_dir inside array0/c/0/ directory
573+ let array0_c0_items = store
574+ . list_dir ( & StorePrefix :: new ( "group0/group1/array0/c/0/" ) ?)
575+ . await ?;
576+ assert_eq ! ( array0_c0_items. keys( ) . len( ) , 2 ) ; // 0, 1
577+ assert_eq ! ( array0_c0_items. prefixes( ) . len( ) , 0 ) ; // no subdirectories
578+ assert ! ( array0_c0_items
579+ . keys( )
580+ . contains( & StoreKey :: new( "group0/group1/array0/c/0/0" ) ?) ) ;
581+ assert ! ( array0_c0_items
582+ . keys( )
583+ . contains( & StoreKey :: new( "group0/group1/array0/c/0/1" ) ?) ) ;
584+
585+ // Test list_prefix at root (should get all keys recursively)
586+ let all_keys = store. list_prefix ( & StorePrefix :: root ( ) ) . await ?;
587+ assert_eq ! ( all_keys. len( ) , 8 ) ; // Total of 8 keys in the hierarchy
588+
589+ // Test list_prefix at group0
590+ let group0_keys = store. list_prefix ( & StorePrefix :: new ( "group0/" ) ?) . await ?;
591+ assert_eq ! ( group0_keys. len( ) , 7 ) ; // All keys under group0/
592+
593+ // Test list_prefix at group1
594+ let group1_keys = store
595+ . list_prefix ( & StorePrefix :: new ( "group0/group1/" ) ?)
596+ . await ?;
597+ assert_eq ! ( group1_keys. len( ) , 6 ) ; // zarr.json + 2 arrays with their chunks
598+
599+ // Test list_prefix for array0 (should get all chunks recursively)
600+ let array0_keys = store
601+ . list_prefix ( & StorePrefix :: new ( "group0/group1/array0/" ) ?)
602+ . await ?;
603+ assert_eq ! ( array0_keys. len( ) , 3 ) ; // zarr.json + 2 chunks
604+ assert ! ( array0_keys. contains( & StoreKey :: new( "group0/group1/array0/zarr.json" ) ?) ) ;
605+ assert ! ( array0_keys. contains( & StoreKey :: new( "group0/group1/array0/c/0/0" ) ?) ) ;
606+ assert ! ( array0_keys. contains( & StoreKey :: new( "group0/group1/array0/c/0/1" ) ?) ) ;
607+
608+ // Test list_prefix for array1 (should get all chunks recursively)
609+ let array1_keys = store
610+ . list_prefix ( & StorePrefix :: new ( "group0/group1/array1/" ) ?)
611+ . await ?;
612+ assert_eq ! ( array1_keys. len( ) , 2 ) ; // zarr.json + 1 chunk
613+ assert ! ( array1_keys. contains( & StoreKey :: new( "group0/group1/array1/zarr.json" ) ?) ) ;
614+ assert ! ( array1_keys. contains( & StoreKey :: new( "group0/group1/array1/c/0/0" ) ?) ) ;
615+
616+ Ok ( ( ) )
617+ }
429618}
0 commit comments