Skip to content

Commit 7f60b99

Browse files
committed
fix: avoid rewriting workspaces on read
1 parent b02e9da commit 7f60b99

1 file changed

Lines changed: 55 additions & 19 deletions

File tree

src-tauri/src/storage.rs

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,58 @@ use std::collections::HashMap;
22
use std::path::PathBuf;
33

44
use crate::types::{AppSettings, WorkspaceEntry, WorkspaceSettings};
5-
use crate::utils::normalize_windows_namespace_path;
65
use 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+
857
fn 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-
83122
fn 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

Comments
 (0)