@@ -15,6 +15,7 @@ use uuid::Uuid;
1515use crate :: types:: OpenworkServerInfo ;
1616use crate :: utils:: now_ms;
1717use crate :: utils:: truncate_output;
18+ use crate :: workspace:: state:: normalize_local_workspace_path;
1819
1920pub mod manager;
2021pub mod spawn;
@@ -69,6 +70,14 @@ fn openwork_server_state_path(app: &AppHandle) -> Result<PathBuf, String> {
6970 Ok ( data_dir. join ( "openwork-server-state.json" ) )
7071}
7172
73+ fn normalize_workspace_key ( workspace_key : & str ) -> String {
74+ let trimmed = workspace_key. trim ( ) ;
75+ if trimmed. is_empty ( ) {
76+ return String :: new ( ) ;
77+ }
78+ normalize_local_workspace_path ( trimmed)
79+ }
80+
7281fn load_openwork_server_token_store (
7382 path : & Path ,
7483) -> Result < PersistedOpenworkServerTokenStore , String > {
@@ -140,12 +149,14 @@ fn save_openwork_server_state(
140149 Ok ( ( ) )
141150}
142151
143- fn read_preferred_openwork_port ( app : & AppHandle , workspace_key : & str ) -> Result < Option < u16 > , String > {
144- let path = openwork_server_state_path ( app) ?;
145- let state = load_openwork_server_state ( & path) ?;
146- let trimmed = workspace_key. trim ( ) ;
147- if !trimmed. is_empty ( ) {
148- if let Some ( port) = state. workspace_ports . get ( trimmed) {
152+ fn read_preferred_openwork_port_at_path (
153+ path : & Path ,
154+ workspace_key : & str ,
155+ ) -> Result < Option < u16 > , String > {
156+ let state = load_openwork_server_state ( path) ?;
157+ let normalized = normalize_workspace_key ( workspace_key) ;
158+ if !normalized. is_empty ( ) {
159+ if let Some ( port) = state. workspace_ports . get ( & normalized) {
149160 return Ok ( Some ( * port) ) ;
150161 }
151162 }
@@ -155,13 +166,20 @@ fn read_preferred_openwork_port(app: &AppHandle, workspace_key: &str) -> Result<
155166 Ok ( state. preferred_port )
156167}
157168
158- fn reserved_openwork_ports ( app : & AppHandle , exclude_workspace_key : & str ) -> Result < HashSet < u16 > , String > {
169+ fn read_preferred_openwork_port ( app : & AppHandle , workspace_key : & str ) -> Result < Option < u16 > , String > {
159170 let path = openwork_server_state_path ( app) ?;
160- let state = load_openwork_server_state ( & path) ?;
161- let excluded = exclude_workspace_key. trim ( ) ;
171+ read_preferred_openwork_port_at_path ( & path, workspace_key)
172+ }
173+
174+ fn reserved_openwork_ports_at_path (
175+ path : & Path ,
176+ exclude_workspace_key : & str ,
177+ ) -> Result < HashSet < u16 > , String > {
178+ let state = load_openwork_server_state ( path) ?;
179+ let excluded = normalize_workspace_key ( exclude_workspace_key) ;
162180 let mut reserved = HashSet :: new ( ) ;
163181 for ( workspace_key, port) in state. workspace_ports {
164- if workspace_key. trim ( ) == excluded {
182+ if workspace_key == excluded {
165183 continue ;
166184 }
167185 reserved. insert ( port) ;
@@ -174,22 +192,35 @@ fn reserved_openwork_ports(app: &AppHandle, exclude_workspace_key: &str) -> Resu
174192 Ok ( reserved)
175193}
176194
177- fn persist_preferred_openwork_port (
178- app : & AppHandle ,
195+ fn reserved_openwork_ports ( app : & AppHandle , exclude_workspace_key : & str ) -> Result < HashSet < u16 > , String > {
196+ let path = openwork_server_state_path ( app) ?;
197+ reserved_openwork_ports_at_path ( & path, exclude_workspace_key)
198+ }
199+
200+ fn persist_preferred_openwork_port_at_path (
201+ path : & Path ,
179202 workspace_key : & str ,
180203 port : u16 ,
181204) -> Result < ( ) , String > {
182- let path = openwork_server_state_path ( app) ?;
183- let mut state = load_openwork_server_state ( & path) ?;
205+ let mut state = load_openwork_server_state ( path) ?;
184206 state. version = OPENWORK_SERVER_STATE_VERSION ;
185- let trimmed = workspace_key . trim ( ) ;
186- if trimmed . is_empty ( ) {
207+ let normalized = normalize_workspace_key ( workspace_key ) ;
208+ if normalized . is_empty ( ) {
187209 state. preferred_port = Some ( port) ;
188210 } else {
189- state. workspace_ports . insert ( trimmed . to_string ( ) , port) ;
211+ state. workspace_ports . insert ( normalized , port) ;
190212 state. preferred_port = None ;
191213 }
192- save_openwork_server_state ( & path, & state)
214+ save_openwork_server_state ( path, & state)
215+ }
216+
217+ fn persist_preferred_openwork_port (
218+ app : & AppHandle ,
219+ workspace_key : & str ,
220+ port : u16 ,
221+ ) -> Result < ( ) , String > {
222+ let path = openwork_server_state_path ( app) ?;
223+ persist_preferred_openwork_port_at_path ( & path, workspace_key, port)
193224}
194225
195226fn load_or_create_workspace_tokens (
@@ -205,7 +236,8 @@ fn load_or_create_workspace_tokens_at_path(
205236 workspace_key : & str ,
206237) -> Result < PersistedOpenworkServerTokens , String > {
207238 let mut store = load_openwork_server_token_store ( path) ?;
208- if let Some ( tokens) = store. workspaces . get ( workspace_key) {
239+ let normalized = normalize_workspace_key ( workspace_key) ;
240+ if let Some ( tokens) = store. workspaces . get ( & normalized) {
209241 return Ok ( tokens. clone ( ) ) ;
210242 }
211243
@@ -217,7 +249,7 @@ fn load_or_create_workspace_tokens_at_path(
217249 } ;
218250 store
219251 . workspaces
220- . insert ( workspace_key . to_string ( ) , tokens. clone ( ) ) ;
252+ . insert ( normalized , tokens. clone ( ) ) ;
221253 save_openwork_server_token_store ( path, & store) ?;
222254 Ok ( tokens)
223255}
@@ -237,7 +269,8 @@ fn persist_workspace_owner_token_at_path(
237269 owner_token : & str ,
238270) -> Result < ( ) , String > {
239271 let mut store = load_openwork_server_token_store ( path) ?;
240- let Some ( tokens) = store. workspaces . get_mut ( workspace_key) else {
272+ let normalized = normalize_workspace_key ( workspace_key) ;
273+ let Some ( tokens) = store. workspaces . get_mut ( & normalized) else {
241274 return Ok ( ( ) ) ;
242275 } ;
243276 tokens. owner_token = Some ( owner_token. to_string ( ) ) ;
@@ -474,4 +507,53 @@ mod tests {
474507
475508 let _ = fs:: remove_file ( path) ;
476509 }
510+
511+ #[ test]
512+ fn reuses_workspace_tokens_across_canonical_path_aliases ( ) {
513+ let path = unique_temp_path ( "token-path-alias" ) ;
514+ let workspace = PathBuf :: from ( format ! (
515+ "/tmp/openwork-workspace-token-alias-{}-{}" ,
516+ std:: process:: id( ) ,
517+ now_ms( )
518+ ) ) ;
519+ fs:: create_dir_all ( & workspace) . expect ( "create temp workspace" ) ;
520+ let canonical = fs:: canonicalize ( & workspace) . expect ( "canonical workspace path" ) ;
521+
522+ let first = load_or_create_workspace_tokens_at_path ( & path, & workspace. to_string_lossy ( ) )
523+ . expect ( "store tokens using raw path" ) ;
524+ let second = load_or_create_workspace_tokens_at_path ( & path, & canonical. to_string_lossy ( ) )
525+ . expect ( "load tokens using canonical path" ) ;
526+
527+ assert_eq ! ( first. client_token, second. client_token) ;
528+ assert_eq ! ( first. host_token, second. host_token) ;
529+
530+ let _ = fs:: remove_file ( path) ;
531+ let _ = fs:: remove_dir_all ( workspace) ;
532+ }
533+
534+ #[ test]
535+ fn reuses_workspace_port_across_canonical_path_aliases ( ) {
536+ let path = unique_temp_path ( "port-path-alias" ) ;
537+ let workspace = PathBuf :: from ( format ! (
538+ "/tmp/openwork-workspace-port-alias-{}-{}" ,
539+ std:: process:: id( ) ,
540+ now_ms( )
541+ ) ) ;
542+ fs:: create_dir_all ( & workspace) . expect ( "create temp workspace" ) ;
543+ let canonical = fs:: canonicalize ( & workspace) . expect ( "canonical workspace path" ) ;
544+
545+ persist_preferred_openwork_port_at_path ( & path, & workspace. to_string_lossy ( ) , 49_123 )
546+ . expect ( "persist preferred port using raw path" ) ;
547+
548+ let preferred = read_preferred_openwork_port_at_path ( & path, & canonical. to_string_lossy ( ) )
549+ . expect ( "read preferred port using canonical path" ) ;
550+ let reserved = reserved_openwork_ports_at_path ( & path, & canonical. to_string_lossy ( ) )
551+ . expect ( "read reserved ports using canonical path" ) ;
552+
553+ assert_eq ! ( preferred, Some ( 49_123 ) ) ;
554+ assert ! ( !reserved. contains( & 49_123 ) ) ;
555+
556+ let _ = fs:: remove_file ( path) ;
557+ let _ = fs:: remove_dir_all ( workspace) ;
558+ }
477559}
0 commit comments