diff --git a/app/src/uri/mod.rs b/app/src/uri/mod.rs index 023dadf1..700cb8db 100644 --- a/app/src/uri/mod.rs +++ b/app/src/uri/mod.rs @@ -657,6 +657,13 @@ fn find_matching_config_name<'a>( .find(|&config| config.name.to_lowercase() == target_name_lower) } +/// Extract the `path` query parameter, expanding a leading `~` to the +/// user's home directory. +fn parse_tab_path(url: &Url) -> Option { + let raw = url.query_pairs().find(|(k, _)| k == "path")?.1; + Some(PathBuf::from(shellexpand::tilde(&raw).into_owned())) +} + #[derive(Debug)] enum Action { NewTab, @@ -706,11 +713,7 @@ impl Action { } else { None }; - let Some(Ok(path)) = url - .query_pairs() - .find(|(k, _v)| k == "path") - .map(|(_, path)| PathBuf::from_str(&path)) - else { + let Some(path) = parse_tab_path(url) else { log::warn!("Could not parse path to open a new tab/window"); return; }; diff --git a/app/src/uri/uri_test.rs b/app/src/uri/uri_test.rs index e1586464..a4d0df78 100644 --- a/app/src/uri/uri_test.rs +++ b/app/src/uri/uri_test.rs @@ -533,3 +533,43 @@ fn validate_custom_uri_errors_do_not_leak_query_string() { assert!(!msg.contains("refresh_token"), "{msg}"); assert!(!msg.contains("LEAKED"), "{msg}"); } + +#[test] +fn test_parse_tab_path_expands_tilde() { + let url = Url::parse("warp://action/new_tab?path=~/Projects").unwrap(); + let home = dirs::home_dir().expect("HOME must be set for this test"); + assert_eq!(parse_tab_path(&url), Some(home.join("Projects"))); +} + +#[test] +fn test_parse_tab_path_expands_url_encoded_tilde() { + // `%7E` and `%2F` are URL-encoded `~` and `/`. + let url = Url::parse("warp://action/new_tab?path=%7E%2FProjects").unwrap(); + let home = dirs::home_dir().expect("HOME must be set for this test"); + assert_eq!(parse_tab_path(&url), Some(home.join("Projects"))); +} + +#[test] +fn test_parse_tab_path_absolute_path_unchanged() { + let url = Url::parse("warp://action/new_tab?path=/tmp/foo").unwrap(); + assert_eq!(parse_tab_path(&url), Some(PathBuf::from("/tmp/foo"))); +} + +#[test] +fn test_parse_tab_path_relative_path_unchanged() { + let url = Url::parse("warp://action/new_tab?path=relative/dir").unwrap(); + assert_eq!(parse_tab_path(&url), Some(PathBuf::from("relative/dir"))); +} + +#[test] +fn test_parse_tab_path_missing_returns_none() { + let url = Url::parse("warp://action/new_tab").unwrap(); + assert_eq!(parse_tab_path(&url), None); +} + +#[test] +fn test_parse_tab_path_bare_tilde() { + let url = Url::parse("warp://action/new_tab?path=~").unwrap(); + let home = dirs::home_dir().expect("HOME must be set for this test"); + assert_eq!(parse_tab_path(&url), Some(home)); +}