Loading crates/tor-config/src/path.rs +69 −5 Original line number Original line Diff line number Diff line Loading @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; use directories::{BaseDirs, ProjectDirs}; use directories::{BaseDirs, ProjectDirs}; use once_cell::sync::Lazy; use once_cell::sync::Lazy; use serde::Deserialize; use serde::{Deserialize, Serialize}; use tor_error::{ErrorKind, HasKind}; use tor_error::{ErrorKind, HasKind}; Loading @@ -29,14 +29,14 @@ use tor_error::{ErrorKind, HasKind}; /// For more information, see that crate's documentation. /// For more information, see that crate's documentation. /// /// /// Alternatively, a `CfgPath` can contain literal `PathBuf`, which will not be expaneded. /// Alternatively, a `CfgPath` can contain literal `PathBuf`, which will not be expaneded. #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] #[serde(transparent)] #[serde(transparent)] pub struct CfgPath(PathInner); pub struct CfgPath(PathInner); /// Inner implementation of CfgPath /// Inner implementation of CfgPath /// /// /// `PathInner` exists to avoid making the variants part of the public Rust API /// `PathInner` exists to avoid making the variants part of the public Rust API #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] #[serde(untagged)] #[serde(untagged)] enum PathInner { enum PathInner { /// A path that should be used literally, with no expansion. /// A path that should be used literally, with no expansion. Loading @@ -45,7 +45,7 @@ enum PathInner { Shell(String), Shell(String), } } #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] /// Inner implementation of PathInner:Literal /// Inner implementation of PathInner:Literal /// /// /// `LiteralPath` exists to arrange that `PathInner::Literal`'s (de)serialization /// `LiteralPath` exists to arrange that `PathInner::Literal`'s (de)serialization Loading Loading @@ -309,8 +309,10 @@ mod test { #[cfg(test)] #[cfg(test)] mod test_serde { mod test_serde { use super::*; use super::*; use std::ffi::OsString; use std::fmt::Debug; #[derive(Deserialize, Eq, PartialEq, Debug)] #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)] struct TestConfigFile { struct TestConfigFile { p: CfgPath, p: CfgPath, } } Loading Loading @@ -356,4 +358,66 @@ mod test_serde { assert_eq!(cp.as_literal_path(), Some(&*PathBuf::from("lit"))); assert_eq!(cp.as_literal_path(), Some(&*PathBuf::from("lit"))); } } } } fn non_string_path() -> PathBuf { #[cfg(target_family = "unix")] { use std::os::unix::ffi::OsStringExt; return PathBuf::from(OsString::from_vec(vec![0x80_u8])); } #[cfg(target_family = "windows")] { use std::os::windows::ffi::OsStringExt; return PathBuf::from(OsString::from_wide(&[0xD800_u16])); } #[allow(unreachable_code)] // Cannot test non-Stringy Paths on this platform PathBuf::default() } fn test_roundtrip_cases<SER, S, DESER, E, F>(ser: SER, deser: DESER) where SER: Fn(&TestConfigFile) -> Result<S, E>, DESER: Fn(&S) -> Result<TestConfigFile, F>, S: Debug, E: Debug, F: Debug, { let case = |easy, p| { let input = TestConfigFile { p }; let s = match ser(&input) { Ok(s) => s, Err(e) if easy => panic!("ser failed {:?} e={:?}", &input, &e), Err(_) => return, }; dbg!(&input, &s); let output = deser(&s).expect("deser failed"); assert_eq!(&input, &output, "s={:?}", &s); }; case(true, CfgPath::new("string".into())); case(true, CfgPath::new_literal(PathBuf::from("nice path"))); case(true, CfgPath::new_literal(PathBuf::from("path with ✓"))); // Non-UTF-8 paths are really hard to serialize. We allow the serializsaton // to fail, and if it does, we skip the rest of the round trip test. // But, if they did serialise, we want to make sure that we can deserialize. // Hence this test case. case(false, CfgPath::new_literal(non_string_path())); } #[test] fn roundtrip_json() { test_roundtrip_cases( |input| serde_json::to_string(&input), |json| serde_json::from_str(json), ); } #[test] fn roundtrip_toml() { test_roundtrip_cases(|input| toml::to_string(&input), |toml| toml::from_str(toml)); } } } Loading
crates/tor-config/src/path.rs +69 −5 Original line number Original line Diff line number Diff line Loading @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; use directories::{BaseDirs, ProjectDirs}; use directories::{BaseDirs, ProjectDirs}; use once_cell::sync::Lazy; use once_cell::sync::Lazy; use serde::Deserialize; use serde::{Deserialize, Serialize}; use tor_error::{ErrorKind, HasKind}; use tor_error::{ErrorKind, HasKind}; Loading @@ -29,14 +29,14 @@ use tor_error::{ErrorKind, HasKind}; /// For more information, see that crate's documentation. /// For more information, see that crate's documentation. /// /// /// Alternatively, a `CfgPath` can contain literal `PathBuf`, which will not be expaneded. /// Alternatively, a `CfgPath` can contain literal `PathBuf`, which will not be expaneded. #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] #[serde(transparent)] #[serde(transparent)] pub struct CfgPath(PathInner); pub struct CfgPath(PathInner); /// Inner implementation of CfgPath /// Inner implementation of CfgPath /// /// /// `PathInner` exists to avoid making the variants part of the public Rust API /// `PathInner` exists to avoid making the variants part of the public Rust API #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] #[serde(untagged)] #[serde(untagged)] enum PathInner { enum PathInner { /// A path that should be used literally, with no expansion. /// A path that should be used literally, with no expansion. Loading @@ -45,7 +45,7 @@ enum PathInner { Shell(String), Shell(String), } } #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] /// Inner implementation of PathInner:Literal /// Inner implementation of PathInner:Literal /// /// /// `LiteralPath` exists to arrange that `PathInner::Literal`'s (de)serialization /// `LiteralPath` exists to arrange that `PathInner::Literal`'s (de)serialization Loading Loading @@ -309,8 +309,10 @@ mod test { #[cfg(test)] #[cfg(test)] mod test_serde { mod test_serde { use super::*; use super::*; use std::ffi::OsString; use std::fmt::Debug; #[derive(Deserialize, Eq, PartialEq, Debug)] #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)] struct TestConfigFile { struct TestConfigFile { p: CfgPath, p: CfgPath, } } Loading Loading @@ -356,4 +358,66 @@ mod test_serde { assert_eq!(cp.as_literal_path(), Some(&*PathBuf::from("lit"))); assert_eq!(cp.as_literal_path(), Some(&*PathBuf::from("lit"))); } } } } fn non_string_path() -> PathBuf { #[cfg(target_family = "unix")] { use std::os::unix::ffi::OsStringExt; return PathBuf::from(OsString::from_vec(vec![0x80_u8])); } #[cfg(target_family = "windows")] { use std::os::windows::ffi::OsStringExt; return PathBuf::from(OsString::from_wide(&[0xD800_u16])); } #[allow(unreachable_code)] // Cannot test non-Stringy Paths on this platform PathBuf::default() } fn test_roundtrip_cases<SER, S, DESER, E, F>(ser: SER, deser: DESER) where SER: Fn(&TestConfigFile) -> Result<S, E>, DESER: Fn(&S) -> Result<TestConfigFile, F>, S: Debug, E: Debug, F: Debug, { let case = |easy, p| { let input = TestConfigFile { p }; let s = match ser(&input) { Ok(s) => s, Err(e) if easy => panic!("ser failed {:?} e={:?}", &input, &e), Err(_) => return, }; dbg!(&input, &s); let output = deser(&s).expect("deser failed"); assert_eq!(&input, &output, "s={:?}", &s); }; case(true, CfgPath::new("string".into())); case(true, CfgPath::new_literal(PathBuf::from("nice path"))); case(true, CfgPath::new_literal(PathBuf::from("path with ✓"))); // Non-UTF-8 paths are really hard to serialize. We allow the serializsaton // to fail, and if it does, we skip the rest of the round trip test. // But, if they did serialise, we want to make sure that we can deserialize. // Hence this test case. case(false, CfgPath::new_literal(non_string_path())); } #[test] fn roundtrip_json() { test_roundtrip_cases( |input| serde_json::to_string(&input), |json| serde_json::from_str(json), ); } #[test] fn roundtrip_toml() { test_roundtrip_cases(|input| toml::to_string(&input), |toml| toml::from_str(toml)); } } }