diff --git a/nix-rust/src/error.rs b/nix-rust/src/error.rs index 1066a266e..28b5c43af 100644 --- a/nix-rust/src/error.rs +++ b/nix-rust/src/error.rs @@ -6,6 +6,7 @@ pub enum Error { BadStorePath(std::path::PathBuf), BadNarInfo, BadBase32, + StorePathNameEmpty, StorePathNameTooLong, BadStorePathName, NarSizeFieldTooBig, @@ -42,6 +43,7 @@ impl fmt::Display for Error { Error::BadNarInfo => write!(f, ".narinfo file is corrupt"), Error::BadStorePath(path) => write!(f, "path '{}' is not a store path", path.display()), Error::BadBase32 => write!(f, "invalid base32 string"), + Error::StorePathNameEmpty => write!(f, "store path name is empty"), Error::StorePathNameTooLong => { write!(f, "store path name is longer than 211 characters") } diff --git a/nix-rust/src/store/path.rs b/nix-rust/src/store/path.rs index 4b866f9b4..03307ca7c 100644 --- a/nix-rust/src/store/path.rs +++ b/nix-rust/src/store/path.rs @@ -24,7 +24,7 @@ impl StorePath { } pub fn new_from_base_name(base_name: &str) -> Result { - if base_name.len() < STORE_PATH_HASH_CHARS + 2 + if base_name.len() < STORE_PATH_HASH_CHARS + 1 || base_name.as_bytes()[STORE_PATH_HASH_CHARS] != '-' as u8 { return Err(Error::BadStorePath(base_name.into())); @@ -68,6 +68,10 @@ pub struct StorePathName(String); impl StorePathName { pub fn new(s: &str) -> Result { + if s.len() == 0 { + return Err(Error::StorePathNameEmpty); + } + if s.len() > 211 { return Err(Error::StorePathNameTooLong); } @@ -97,4 +101,90 @@ impl fmt::Display for StorePathName { } } -// FIXME: add tests +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse() { + let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3"; + let p = StorePath::new_from_base_name(&s).unwrap(); + assert_eq!(p.name.0, "konsole-18.12.3"); + assert_eq!( + p.hash.0, + [ + 0x9f, 0x76, 0x49, 0x20, 0xf6, 0x5d, 0xe9, 0x71, 0xc4, 0xca, 0x46, 0x21, 0xab, 0xff, + 0x9b, 0x44, 0xef, 0x87, 0x0f, 0x3c + ] + ); + } + + #[test] + fn test_no_name() { + let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-"; + assert_matches!( + StorePath::new_from_base_name(&s), + Err(Error::StorePathNameEmpty) + ); + } + + #[test] + fn test_no_dash() { + let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz"; + assert_matches!( + StorePath::new_from_base_name(&s), + Err(Error::BadStorePath(_)) + ); + } + + #[test] + fn test_short_hash() { + let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxl-konsole-18.12.3"; + assert_matches!( + StorePath::new_from_base_name(&s), + Err(Error::BadStorePath(_)) + ); + } + + #[test] + fn test_invalid_hash() { + let s = "7h7qgvs4kgzsn8e6rb273saxyqh4jxlz-konsole-18.12.3"; + assert_matches!(StorePath::new_from_base_name(&s), Err(Error::BadBase32)); + } + + #[test] + fn test_long_name() { + let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + assert_matches!(StorePath::new_from_base_name(&s), Ok(_)); + } + + #[test] + fn test_too_long_name() { + let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + assert_matches!( + StorePath::new_from_base_name(&s), + Err(Error::StorePathNameTooLong) + ); + } + + #[test] + fn test_bad_name() { + let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-foo bar"; + assert_matches!( + StorePath::new_from_base_name(&s), + Err(Error::BadStorePathName) + ); + + let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-kónsole"; + assert_matches!( + StorePath::new_from_base_name(&s), + Err(Error::BadStorePathName) + ); + } + + #[test] + fn test_roundtrip() { + let s = "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3"; + assert_eq!(StorePath::new_from_base_name(&s).unwrap().to_string(), s); + } +}