@@ -2,9 +2,58 @@ use std::collections::HashMap;
22use std:: path:: PathBuf ;
33
44use crate :: types:: { AppSettings , WorkspaceEntry , WorkspaceSettings } ;
5- use crate :: utils:: normalize_windows_namespace_path;
65use serde_json:: Value ;
76
7+ fn normalize_windows_namespace_path ( path : & str ) -> String {
8+ if path. is_empty ( ) {
9+ return String :: new ( ) ;
10+ }
11+
12+ fn strip_prefix_ascii_case < ' a > ( value : & ' a str , prefix : & str ) -> Option < & ' a str > {
13+ value
14+ . get ( ..prefix. len ( ) )
15+ . filter ( |candidate| candidate. eq_ignore_ascii_case ( prefix) )
16+ . map ( |_| & value[ prefix. len ( ) ..] )
17+ }
18+
19+ fn starts_with_drive_path ( value : & str ) -> bool {
20+ let bytes = value. as_bytes ( ) ;
21+ bytes. len ( ) >= 3
22+ && bytes[ 0 ] . is_ascii_alphabetic ( )
23+ && bytes[ 1 ] == b':'
24+ && ( bytes[ 2 ] == b'\\' || bytes[ 2 ] == b'/' )
25+ }
26+
27+ if let Some ( rest) = strip_prefix_ascii_case ( path, r"\\?\UNC\" ) {
28+ return format ! ( r"\\{rest}" ) ;
29+ }
30+ if let Some ( rest) = strip_prefix_ascii_case ( path, "//?/UNC/" ) {
31+ return format ! ( "//{rest}" ) ;
32+ }
33+ if let Some ( rest) =
34+ strip_prefix_ascii_case ( path, r"\\?\" ) . filter ( |rest| starts_with_drive_path ( rest) )
35+ {
36+ return rest. to_string ( ) ;
37+ }
38+ if let Some ( rest) =
39+ strip_prefix_ascii_case ( path, "//?/" ) . filter ( |rest| starts_with_drive_path ( rest) )
40+ {
41+ return rest. to_string ( ) ;
42+ }
43+ if let Some ( rest) =
44+ strip_prefix_ascii_case ( path, r"\\.\" ) . filter ( |rest| starts_with_drive_path ( rest) )
45+ {
46+ return rest. to_string ( ) ;
47+ }
48+ if let Some ( rest) =
49+ strip_prefix_ascii_case ( path, "//./" ) . filter ( |rest| starts_with_drive_path ( rest) )
50+ {
51+ return rest. to_string ( ) ;
52+ }
53+
54+ path. to_string ( )
55+ }
56+
857fn normalize_optional_windows_namespace_path ( path : Option < String > ) -> ( Option < String > , bool ) {
958 match path {
1059 Some ( path) => {
@@ -70,16 +119,6 @@ fn normalize_app_settings(settings: AppSettings) -> (AppSettings, bool) {
70119 )
71120}
72121
73- fn try_rewrite_workspaces_with_normalized_paths ( path : & PathBuf , list : & [ WorkspaceEntry ] ) {
74- if let Err ( error) = write_workspaces ( path, list) {
75- eprintln ! (
76- "read_workspaces: failed to persist normalized workspace paths to {}: {}" ,
77- path. display( ) ,
78- error
79- ) ;
80- }
81- }
82-
83122fn try_rewrite_settings_with_normalized_paths ( path : & PathBuf , settings : & AppSettings ) {
84123 if let Err ( error) = write_settings ( path, settings) {
85124 eprintln ! (
@@ -96,10 +135,7 @@ pub(crate) fn read_workspaces(path: &PathBuf) -> Result<HashMap<String, Workspac
96135 }
97136 let data = std:: fs:: read_to_string ( path) . map_err ( |e| e. to_string ( ) ) ?;
98137 let list: Vec < WorkspaceEntry > = serde_json:: from_str ( & data) . map_err ( |e| e. to_string ( ) ) ?;
99- let ( list, changed) = normalize_workspace_entries ( list) ;
100- if changed {
101- try_rewrite_workspaces_with_normalized_paths ( path, & list) ;
102- }
138+ let ( list, _) = normalize_workspace_entries ( list) ;
103139 Ok ( list
104140 . into_iter ( )
105141 . map ( |entry| ( entry. id . clone ( ) , entry) )
@@ -255,7 +291,7 @@ mod tests {
255291 }
256292
257293 #[ test]
258- fn read_workspaces_rewrites_namespace_paths_with_sanitized_values ( ) {
294+ fn read_workspaces_sanitizes_namespace_paths_without_rewriting_file ( ) {
259295 let temp_dir = std:: env:: temp_dir ( ) . join ( format ! ( "codex-monitor-test-{}" , Uuid :: new_v4( ) ) ) ;
260296 std:: fs:: create_dir_all ( & temp_dir) . expect ( "create temp dir" ) ;
261297 let path = temp_dir. join ( "workspaces.json" ) ;
@@ -280,9 +316,9 @@ mod tests {
280316 let stored = read. get ( "w1" ) . expect ( "stored workspace" ) ;
281317 assert_eq ! ( stored. path, r"I:\gpt-projects\json-composer" ) ;
282318
283- let rewritten = std:: fs:: read_to_string ( & path) . expect ( "read rewritten workspaces" ) ;
284- assert ! ( rewritten . contains( r#"I:\gpt-projects\json-composer"# ) ) ;
285- assert ! ( !rewritten . contains( r#"\\?\ I:\gpt-projects\json-composer"# ) ) ;
319+ let persisted = std:: fs:: read_to_string ( & path) . expect ( "read persisted workspaces" ) ;
320+ assert ! ( persisted . contains( r#"\\?\ I:\gpt-projects\json-composer"# ) ) ;
321+ assert ! ( !persisted . contains( r#""path": " I:\gpt-projects\json-composer" "# ) ) ;
286322 }
287323
288324 #[ test]
0 commit comments