StorePath improvements

This commit is contained in:
Eelco Dolstra 2019-09-17 22:26:49 +02:00
parent cce218f950
commit 6317f0f7a0
5 changed files with 130 additions and 61 deletions

View file

@ -1,9 +1,13 @@
use std::fmt;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
InvalidPath(crate::store::StorePath), InvalidPath(crate::store::StorePath),
BadStorePath(std::path::PathBuf), BadStorePath(std::path::PathBuf),
BadNarInfo, BadNarInfo,
BadBase32, BadBase32,
StorePathNameTooLong,
BadStorePathName,
IOError(std::io::Error), IOError(std::io::Error),
HttpError(reqwest::Error), HttpError(reqwest::Error),
Misc(String), Misc(String),
@ -22,19 +26,30 @@ impl From<reqwest::Error> for Error {
} }
} }
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::InvalidPath(_) => write!(f, "invalid path"),
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::StorePathNameTooLong => {
write!(f, "store path name is longer than 211 characters")
}
Error::BadStorePathName => write!(f, "store path name contains forbidden character"),
Error::IOError(err) => write!(f, "I/O error: {}", err),
Error::HttpError(err) => write!(f, "HTTP error: {}", err),
Error::Foreign(_) => write!(f, "<C++ exception>"), // FIXME
Error::Misc(s) => write!(f, "{}", s),
}
}
}
impl From<Error> for CppException { impl From<Error> for CppException {
fn from(err: Error) -> Self { fn from(err: Error) -> Self {
match err { match err {
Error::InvalidPath(_) => unsafe { make_error("invalid path") }, // FIXME
Error::BadNarInfo => unsafe { make_error(".narinfo file is corrupt") }, // FIXME
Error::BadStorePath(path) => unsafe {
make_error(&format!("path '{}' is not a store path", path.display()))
}, // FIXME
Error::BadBase32 => unsafe { make_error("invalid base32 string") }, // FIXME
Error::IOError(err) => unsafe { make_error(&err.to_string()) },
Error::HttpError(err) => unsafe { make_error(&err.to_string()) },
Error::Foreign(ex) => ex, Error::Foreign(ex) => ex,
Error::Misc(s) => unsafe { make_error(&s) }, _ => unsafe { make_error(&err.to_string()) },
} }
} }
} }

View file

@ -1,7 +1,9 @@
mod binary_cache_store; mod binary_cache_store;
mod path;
mod path_info; mod path_info;
mod store; mod store;
pub use binary_cache_store::BinaryCacheStore; pub use binary_cache_store::BinaryCacheStore;
pub use path::{StorePath, StorePathHash, StorePathName};
pub use path_info::PathInfo; pub use path_info::PathInfo;
pub use store::{Store, StorePath}; pub use store::Store;

100
nix-rust/src/store/path.rs Normal file
View file

@ -0,0 +1,100 @@
use crate::error::Error;
use crate::util::base32;
use std::fmt;
use std::path::Path;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct StorePath {
pub hash: StorePathHash,
pub name: StorePathName,
}
pub const STORE_PATH_HASH_BYTES: usize = 20;
pub const STORE_PATH_HASH_CHARS: usize = 32;
impl StorePath {
pub fn new(path: &Path, _store_dir: &str) -> Result<Self, Error> {
// FIXME: check store_dir
Self::new_from_base_name(
path.file_name()
.ok_or(Error::BadStorePath(path.into()))?
.to_str()
.ok_or(Error::BadStorePath(path.into()))?,
)
}
pub fn new_from_base_name(base_name: &str) -> Result<Self, Error> {
if base_name.len() < STORE_PATH_HASH_CHARS + 2
|| base_name.as_bytes()[STORE_PATH_HASH_CHARS] != '-' as u8
{
return Err(Error::BadStorePath(base_name.into()));
}
Ok(StorePath {
hash: StorePathHash::new(&base_name[0..STORE_PATH_HASH_CHARS])?,
name: StorePathName::new(&base_name[STORE_PATH_HASH_CHARS + 1..])?,
})
}
}
impl fmt::Display for StorePath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}-{}", self.hash, self.name)
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct StorePathHash([u8; STORE_PATH_HASH_BYTES]);
impl StorePathHash {
pub fn new(s: &str) -> Result<Self, Error> {
assert_eq!(s.len(), STORE_PATH_HASH_CHARS);
let v = base32::decode(s)?;
assert_eq!(v.len(), STORE_PATH_HASH_BYTES);
let mut bytes: [u8; 20] = Default::default();
bytes.copy_from_slice(&v[0..STORE_PATH_HASH_BYTES]);
Ok(Self(bytes))
}
}
impl fmt::Display for StorePathHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&base32::encode(&self.0))
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct StorePathName(String);
impl StorePathName {
pub fn new(s: &str) -> Result<Self, Error> {
if s.len() > 211 {
return Err(Error::StorePathNameTooLong);
}
if s.starts_with('.')
|| !s.chars().all(|c| {
c.is_ascii_alphabetic()
|| c.is_ascii_digit()
|| c == '+'
|| c == '-'
|| c == '.'
|| c == '_'
|| c == '?'
|| c == '='
})
{
return Err(Error::BadStorePathName);
}
Ok(Self(s.to_string()))
}
}
impl fmt::Display for StorePathName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
// FIXME: add tests

View file

@ -43,11 +43,11 @@ impl PathInfo {
} else if name == "References" { } else if name == "References" {
if !value.is_empty() { if !value.is_empty() {
for r in value.split(' ') { for r in value.split(' ') {
references.insert(StorePath::new_short(r)?); references.insert(StorePath::new_from_base_name(r)?);
} }
} }
} else if name == "Deriver" { } else if name == "Deriver" {
deriver = Some(StorePath::new_short(value)?); deriver = Some(StorePath::new_from_base_name(value)?);
} else if name == "URL" { } else if name == "URL" {
url = Some(value.into()); url = Some(value.into());
} else if name == "Compression" { } else if name == "Compression" {

View file

@ -1,56 +1,8 @@
use super::PathInfo; use super::{PathInfo, StorePath};
use crate::Error; use crate::Error;
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
use std::path::Path; use std::path::Path;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct StorePath {
pub hash: String,
pub name: String,
}
pub const STORE_PATH_HASH_CHARS: usize = 32;
impl StorePath {
pub fn new(path: &Path, _store_dir: &str) -> Result<Self, Error> {
// FIXME: check store_dir
Self::new_short(
path.file_name()
.ok_or(Error::BadStorePath(path.into()))?
.to_str()
.ok_or(Error::BadStorePath(path.into()))?,
)
}
pub fn new_short(base_name: &str) -> Result<Self, Error> {
if base_name.len() < STORE_PATH_HASH_CHARS + 2
|| base_name.as_bytes()[STORE_PATH_HASH_CHARS] != '-' as u8
{
return Err(Error::BadStorePath(base_name.into()));
}
// FIXME: validate name
Ok(StorePath {
hash: base_name[0..STORE_PATH_HASH_CHARS].to_string(),
name: base_name[STORE_PATH_HASH_CHARS + 1..].to_string(),
})
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct StorePathHash {
bytes: [u8; 20],
}
/*
impl StorePathHash {
pub fn to_base32(&self) -> String {
"7h7qgvs4kgzsn8a6rb273saxyqh4jxlz".to_string()
}
}
*/
pub trait Store: Send + Sync { pub trait Store: Send + Sync {
fn store_dir(&self) -> &str { fn store_dir(&self) -> &str {
"/nix/store" "/nix/store"