@@ -17,6 +17,7 @@ use locality_store::{
1717 ProjectionMode , ShadowRepository , StoreError , VirtualMutationRepository ,
1818} ;
1919use serde:: { Deserialize , Serialize } ;
20+ use std:: collections:: { BTreeSet , VecDeque } ;
2021use std:: path:: { Path , PathBuf } ;
2122use std:: sync:: atomic:: { AtomicU64 , Ordering } ;
2223use std:: time:: { SystemTime , UNIX_EPOCH } ;
@@ -109,6 +110,56 @@ where
109110 } )
110111}
111112
113+ pub fn file_provider_domain_working_set < S > (
114+ store : & S ,
115+ state_root : & Path ,
116+ domain_id : & str ,
117+ ) -> LocalityResult < FileProviderDomainChildrenReport >
118+ where
119+ S : MountRepository + EntityRepository + VirtualMutationRepository ,
120+ {
121+ let domain = file_provider_domain_children ( store, domain_id) ?;
122+ let mut children = Vec :: new ( ) ;
123+
124+ for mount_root in domain. children {
125+ let mount_id = MountId :: new ( mount_root. mount_id . clone ( ) ) ;
126+ let content_root = virtual_fs:: virtual_fs_content_root ( state_root, & mount_id) ;
127+ let mut pending_containers = VecDeque :: from ( [ mount_root. item . identifier . clone ( ) ] ) ;
128+ let mut visited_containers = BTreeSet :: new ( ) ;
129+ let mut visited_items = BTreeSet :: new ( ) ;
130+ children. push ( mount_root) ;
131+
132+ while let Some ( container_identifier) = pending_containers. pop_front ( ) {
133+ if !visited_containers. insert ( container_identifier. clone ( ) ) {
134+ continue ;
135+ }
136+ let report = virtual_fs:: virtual_fs_children_with_content_root (
137+ store,
138+ & content_root,
139+ & mount_id,
140+ & container_identifier,
141+ ) ?;
142+ for item in report. children {
143+ if !visited_items. insert ( item. identifier . clone ( ) ) {
144+ continue ;
145+ }
146+ if item. kind == FileProviderItemKind :: Folder {
147+ pending_containers. push_back ( item. identifier . clone ( ) ) ;
148+ }
149+ children. push ( FileProviderDomainChild {
150+ mount_id : mount_id. 0 . clone ( ) ,
151+ item,
152+ } ) ;
153+ }
154+ }
155+ }
156+
157+ Ok ( FileProviderDomainChildrenReport {
158+ domain_id : domain_id. to_string ( ) ,
159+ children,
160+ } )
161+ }
162+
112163pub fn file_provider_item < S > (
113164 store : & S ,
114165 mount_id : & MountId ,
@@ -4050,6 +4101,139 @@ mod tests {
40504101 assert_eq ! ( report. children[ 1 ] . item. identifier, "mount:notion-main" ) ;
40514102 }
40524103
4104+ #[ test]
4105+ fn shared_macos_file_provider_working_set_recursively_lists_cached_items ( ) {
4106+ let state_root = temp_root ( "locality-file-provider-working-set" ) ;
4107+ let mount_id = MountId :: new ( "notion-main" ) ;
4108+ let mut store = InMemoryStateStore :: new ( ) ;
4109+ store
4110+ . save_mount (
4111+ MountConfig :: new (
4112+ mount_id. clone ( ) ,
4113+ "notion" ,
4114+ state_root. join ( "visible/notion" ) ,
4115+ )
4116+ . projection ( ProjectionMode :: MacosFileProvider ) ,
4117+ )
4118+ . expect ( "save notion mount" ) ;
4119+ for ( remote_id, title, path) in [
4120+ ( "company" , "Company" , "Company/page.md" ) ,
4121+ ( "compliance" , "Compliance" , "Company/Compliance/page.md" ) ,
4122+ (
4123+ "privacy" ,
4124+ "Privacy Policy" ,
4125+ "Company/Compliance/Privacy Policy/page.md" ,
4126+ ) ,
4127+ ] {
4128+ store
4129+ . save_entity ( EntityRecord :: new (
4130+ mount_id. clone ( ) ,
4131+ RemoteId :: new ( remote_id) ,
4132+ EntityKind :: Page ,
4133+ title,
4134+ path,
4135+ ) )
4136+ . expect ( "save nested Notion page" ) ;
4137+ }
4138+
4139+ let report =
4140+ file_provider_domain_working_set ( & store, & state_root, MACOS_FILE_PROVIDER_DOMAIN_ID )
4141+ . expect ( "working set" ) ;
4142+ let items = report
4143+ . children
4144+ . iter ( )
4145+ . map ( |child| {
4146+ (
4147+ child. mount_id . as_str ( ) ,
4148+ child. item . identifier . as_str ( ) ,
4149+ child. item . parent_identifier . as_deref ( ) ,
4150+ child. item . filename . as_str ( ) ,
4151+ child. item . path . as_str ( ) ,
4152+ & child. item . kind ,
4153+ )
4154+ } )
4155+ . collect :: < Vec < _ > > ( ) ;
4156+
4157+ assert_eq ! (
4158+ items,
4159+ vec![
4160+ (
4161+ "notion-main" ,
4162+ "mount:notion-main" ,
4163+ Some ( "root" ) ,
4164+ "notion" ,
4165+ "notion" ,
4166+ & FileProviderItemKind :: Folder ,
4167+ ) ,
4168+ (
4169+ "notion-main" ,
4170+ "guidance:AGENTS.md" ,
4171+ Some ( "mount:notion-main" ) ,
4172+ "AGENTS.md" ,
4173+ "AGENTS.md" ,
4174+ & FileProviderItemKind :: File ,
4175+ ) ,
4176+ (
4177+ "notion-main" ,
4178+ "guidance:CLAUDE.md" ,
4179+ Some ( "mount:notion-main" ) ,
4180+ "CLAUDE.md" ,
4181+ "CLAUDE.md" ,
4182+ & FileProviderItemKind :: File ,
4183+ ) ,
4184+ (
4185+ "notion-main" ,
4186+ "children:company" ,
4187+ Some ( "mount:notion-main" ) ,
4188+ "Company" ,
4189+ "Company" ,
4190+ & FileProviderItemKind :: Folder ,
4191+ ) ,
4192+ (
4193+ "notion-main" ,
4194+ "children:compliance" ,
4195+ Some ( "children:company" ) ,
4196+ "Compliance" ,
4197+ "Company/Compliance" ,
4198+ & FileProviderItemKind :: Folder ,
4199+ ) ,
4200+ (
4201+ "notion-main" ,
4202+ "company" ,
4203+ Some ( "children:company" ) ,
4204+ "page.md" ,
4205+ "Company/page.md" ,
4206+ & FileProviderItemKind :: File ,
4207+ ) ,
4208+ (
4209+ "notion-main" ,
4210+ "compliance" ,
4211+ Some ( "children:compliance" ) ,
4212+ "page.md" ,
4213+ "Company/Compliance/page.md" ,
4214+ & FileProviderItemKind :: File ,
4215+ ) ,
4216+ (
4217+ "notion-main" ,
4218+ "children:privacy" ,
4219+ Some ( "children:compliance" ) ,
4220+ "Privacy Policy" ,
4221+ "Company/Compliance/Privacy Policy" ,
4222+ & FileProviderItemKind :: Folder ,
4223+ ) ,
4224+ (
4225+ "notion-main" ,
4226+ "privacy" ,
4227+ Some ( "children:privacy" ) ,
4228+ "page.md" ,
4229+ "Company/Compliance/Privacy Policy/page.md" ,
4230+ & FileProviderItemKind :: File ,
4231+ ) ,
4232+ ]
4233+ ) ;
4234+ let _ = fs:: remove_dir_all ( state_root) ;
4235+ }
4236+
40534237 #[ test]
40544238 fn shared_macos_file_provider_domain_children_distinguish_same_connector_mount_points ( ) {
40554239 let mut store = InMemoryStateStore :: new ( ) ;
0 commit comments