Skip to content

Commit

Permalink
StorePath improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
edolstra committed Dec 10, 2019
1 parent cce218f commit 6317f0f
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 61 deletions.
33 changes: 24 additions & 9 deletions nix-rust/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use std::fmt;

#[derive(Debug)]
pub enum Error {
InvalidPath(crate::store::StorePath),
BadStorePath(std::path::PathBuf),
BadNarInfo,
BadBase32,
StorePathNameTooLong,
BadStorePathName,
IOError(std::io::Error),
HttpError(reqwest::Error),
Misc(String),
Expand All @@ -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 {
fn from(err: Error) -> Self {
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::Misc(s) => unsafe { make_error(&s) },
_ => unsafe { make_error(&err.to_string()) },
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion nix-rust/src/store/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod binary_cache_store;
mod path;
mod path_info;
mod store;

pub use binary_cache_store::BinaryCacheStore;
pub use path::{StorePath, StorePathHash, StorePathName};
pub use path_info::PathInfo;
pub use store::{Store, StorePath};
pub use store::Store;
100 changes: 100 additions & 0 deletions nix-rust/src/store/path.rs
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions nix-rust/src/store/path_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ impl PathInfo {
} else if name == "References" {
if !value.is_empty() {
for r in value.split(' ') {
references.insert(StorePath::new_short(r)?);
references.insert(StorePath::new_from_base_name(r)?);
}
}
} else if name == "Deriver" {
deriver = Some(StorePath::new_short(value)?);
deriver = Some(StorePath::new_from_base_name(value)?);
} else if name == "URL" {
url = Some(value.into());
} else if name == "Compression" {
Expand Down
50 changes: 1 addition & 49 deletions nix-rust/src/store/store.rs
Original file line number Diff line number Diff line change
@@ -1,56 +1,8 @@
use super::PathInfo;
use super::{PathInfo, StorePath};
use crate::Error;
use std::collections::{BTreeMap, BTreeSet};
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 {
fn store_dir(&self) -> &str {
"/nix/store"
Expand Down

0 comments on commit 6317f0f

Please sign in to comment.