Better error management, Utf8Paths & other stuff.

This commit is contained in:
2022-08-08 11:40:45 +02:00
parent b1ceaaf636
commit 5d08b1ea57
33 changed files with 1483 additions and 1216 deletions

View File

@@ -15,12 +15,12 @@
use std::str::FromStr;
use std::path::{Path, PathBuf};
use digest::DynDigest;
use toml::Value;
use camino::{Utf8Path, Utf8PathBuf};
use cas_core::{
Cas, DefaultPipeline, Error, ObjectId, ObjectMetadata, ObjectType,
Cas, DefaultPipeline, err, Error, ObjectId, ObjectMetadata, ObjectType,
Pipeline, Reader, ReadWrapper, RefStore, Result, Writer,
};
@@ -34,27 +34,48 @@ use crate::wfile::WFile;
pub struct SimpleCas {
db_path: PathBuf,
db_path: Utf8PathBuf,
digest: Box<dyn DynDigest>,
pipeline: DefaultPipeline,
config: Value,
// config: Value,
}
impl SimpleCas {
pub fn create(db_path: PathBuf, config: Value) -> Result<Self> {
pub fn create(db_path: Utf8PathBuf, mut config: Value) -> Result<Self> {
if !config.is_table() {
return Error::err("invalid config object: must be table");
}
let maybe_engine = config.as_table_mut().unwrap()
.entry("cas")
.or_insert_with(|| toml::value::Table::new().into())
.as_table_mut().unwrap()
.entry("engine")
.or_insert("simple".into())
.as_str();
match maybe_engine {
Some(engine) if engine != "simple" => {
return err!("invalid cas.engine in config: got {}, expected simple", engine);
},
None => {
return Error::err("invalid casengine in config: expected String");
},
_ => {}
}
let digest_id = config["cas"]["digest"].as_str()
.ok_or_else(|| Error::error(
.ok_or_else(|| Error::unknown(
"mandatory cas.digest value is invalid or missing from config"
))?;
let digest = new_digest(digest_id)?;
let pipeline = DefaultPipeline::new(digest.box_clone());
if db_path.exists() {
return Err(Error::error(format!(
return err!(
"failed to create SimpleCas: target directory already exists ({})",
db_path.to_string_lossy(),
)));
db_path,
);
}
for path in [
@@ -64,10 +85,10 @@ impl SimpleCas {
&tmp_dir(&db_path),
] {
std::fs::create_dir(path).or_else(|e|
Err(Error::error(format!(
err!(
"failed to create directory ({}): {}",
path.to_string_lossy(), e,
)))
path, e,
)
)?;
}
@@ -77,15 +98,15 @@ impl SimpleCas {
db_path,
digest,
pipeline,
config,
// config,
})
}
pub fn open(db_path: PathBuf) -> Result<Self> {
pub fn open(db_path: Utf8PathBuf) -> Result<Self> {
let config = read_config(&db_path)?;
let digest_id = config["cas"]["digest"].as_str()
.ok_or_else(|| Error::error(
.ok_or_else(|| Error::unknown(
"mandatory cas.digest value is invalid or missing from config"
))?;
let digest = new_digest(digest_id)?;
@@ -95,13 +116,13 @@ impl SimpleCas {
db_path,
digest,
pipeline,
config,
// config,
})
}
pub fn save_config(&self) -> Result<()> {
write_config(&self.config, &self.db_path)
}
// pub fn save_config(&self) -> Result<()> {
// write_config(&self.config, &self.db_path)
// }
}
@@ -111,10 +132,10 @@ impl Cas for SimpleCas {
ObjectId::from_str(hex)
}
else {
Err(Error::error(format!(
err!(
"invalid object id size: got {}, expected {}",
hex.len(), self.digest.output_size() * 2,
)))
)
}
}
@@ -131,11 +152,11 @@ impl Cas for SimpleCas {
fn open_object(&self, oid: &ObjectId) -> Result<(ObjectMetadata, Box<dyn Reader>)> {
let opath = obj_path(&self.db_path, oid);
if !opath.is_file() {
return Err(Error::error(format!("object not found: {}", oid)));
return err!("object not found: {}", oid);
}
let file = std::fs::File::open(opath).or_else(|err|
Err(Error::error(format!("failed to open object {}: {}", oid, err))))?;
err!("failed to open object {}: {}", oid, err))?;
let wrapper = Box::new(ReadWrapper::new(file));
let mut reader = self.pipeline.new_reader(wrapper);
let metadata = read_metadata(&mut reader)?;
@@ -154,60 +175,60 @@ impl Cas for SimpleCas {
fn remove_object(&mut self, oid: &ObjectId) -> Result<()> {
let opath = obj_path(&self.db_path, oid);
if !opath.is_file() {
return Err(Error::error(format!("object not found: {}", oid)));
return err!("object not found: {}", oid);
}
std::fs::remove_file(opath).or_else(|err|
Err(Error::error(format!("failed to remove object {}: {}", oid, err))))?;
err!("failed to remove object {}: {}", oid, err))?;
Ok(())
}
}
impl RefStore for SimpleCas {
fn get_ref<P: AsRef<Path>>(&self, key: P) -> Result<ObjectId> {
fn get_ref<P: AsRef<Utf8Path>>(&self, key: P) -> Result<ObjectId> {
let path = ref_dir(&self.db_path).join(key.as_ref());
if !path.exists() {
Error::err(format!("reference {} does not exists", key.as_ref().to_string_lossy()))
err!("reference {} does not exists", key.as_ref())
}
else if !path.is_file() {
Error::err(format!("reference {} is not a file", key.as_ref().to_string_lossy()))
err!("reference {} is not a file", key.as_ref())
}
else {
let file = std::fs::read(path).or_else(|err|
Error::err(format!("failed to read reference file for {}: {}", key.as_ref().to_string_lossy(), err))
err!("failed to read reference file for {}: {}", key.as_ref(), err)
)?;
Ok(
ObjectId::from_str(
std::str::from_utf8(&file).or_else(|err|
Error::err(format!("invalid reference file at {}: {}", key.as_ref().to_string_lossy(), err))
err!("invalid reference file at {}: {}", key.as_ref(), err)
)?
)?
)
}
}
fn set_ref<P: AsRef<Path>>(&mut self, key: P, value: &ObjectId) -> Result<()> {
fn set_ref<P: AsRef<Utf8Path>>(&mut self, key: P, value: &ObjectId) -> Result<()> {
let path = ref_dir(&self.db_path).join(key.as_ref());
std::fs::create_dir_all(path.parent().ok_or_else(||
Error::error(format!("reference file {} has no parent dir?", key.as_ref().to_string_lossy()))
Error::unknown(format!("reference file {} has no parent dir?", key.as_ref()))
)?).or_else(|err|
Error::err(format!("failed to create reference dir for {}: {}", key.as_ref().to_string_lossy(), err))
err!("failed to create reference dir for {}: {}", key.as_ref(), err)
)?;
std::fs::write(path, value.to_string()).or_else(|err|
Error::err(format!("failed to write reference {}: {}", key.as_ref().to_string_lossy(), err))
err!("failed to write reference {}: {}", key.as_ref(), err)
)
}
fn remove_ref<P: AsRef<Path>>(&mut self, key: P) -> Result<()> {
fn remove_ref<P: AsRef<Utf8Path>>(&mut self, key: P) -> Result<()> {
let path = ref_dir(&self.db_path).join(key.as_ref());
if !path.exists() {
Error::err(format!("reference {} does not exists", key.as_ref().to_string_lossy()))
err!("reference {} does not exists", key.as_ref())
}
else if !path.is_file() {
Error::err(format!("reference {} is not a file", key.as_ref().to_string_lossy()))
err!("reference {} is not a file", key.as_ref())
}
else {
std::fs::remove_file(path).or_else(|err|
Error::err(format!("failed to remove reference file {}: {}", key.as_ref().to_string_lossy(), err))
err!("failed to remove reference file {}: {}", key.as_ref(), err)
)
}
}
@@ -215,9 +236,9 @@ impl RefStore for SimpleCas {
pub struct ObjectIdIterator {
root_dirs: Vec<PathBuf>,
inner_dirs: Vec<PathBuf>,
objects: Vec<PathBuf>,
root_dirs: Vec<Utf8PathBuf>,
inner_dirs: Vec<Utf8PathBuf>,
objects: Vec<Utf8PathBuf>,
root_index: usize,
inner_index: usize,
object_index: usize,
@@ -247,7 +268,7 @@ impl ObjectIdIterator {
let path = &self.objects[self.object_index];
if !path.is_file() {
return Error::err(format!("item in object database is not a file: {:?}", path));
return err!("item in object database is not a file: {:?}", path);
}
let id = path.ancestors()
@@ -255,9 +276,9 @@ impl ObjectIdIterator {
.collect::<Vec<_>>()
.iter()
.rev()
.map(|p| p.file_name()?.to_str())
.map(|p| p.file_name())
.collect::<Option<String>>()
.ok_or_else(|| Error::error(format!("invalid object in object database: {:?}", path)))?;
.ok_or_else(|| Error::unknown(format!("invalid object in object database: {:?}", path)))?;
ObjectId::from_str(&id)
}
@@ -342,14 +363,19 @@ impl Iterator for ObjectIdIterator {
}
}
fn read_dir(path: &Path) -> Result<Vec<PathBuf>> {
fn read_dir(path: &Utf8Path) -> Result<Vec<Utf8PathBuf>> {
let mut paths = std::fs::read_dir(path)
.or_else(|err| Err(Error::error(format!(
"failed to read directory {:?}: {}", path, err))))?
.map(|dir| dir.map(|dir| dir.path()))
.collect::<std::io::Result<Vec<_>>>()
.or_else(|err| Err(Error::error(format!(
"error while reading directory {:?}: {}", path, err))))?;
.or_else(|err| err!("failed to read directory {:?}: {}", path, err))?
.map(|res| match res {
Ok(dir) => Ok(
Utf8PathBuf::from_path_buf(dir.path())
.or_else(|e| err!("non-unicode character in path: {}: {:?}", path, e))?
),
Err(err) => err!("error while reading directory {:?}: {}", path, err),
})
.collect::<Result<Vec<_>>>()
.or_else(|err| err!(
"error while reading directory {:?}: {}", path, err))?;
paths.sort();
Ok(paths)
}
@@ -357,7 +383,7 @@ fn read_dir(path: &Path) -> Result<Vec<PathBuf>> {
#[cfg(test)]
mod tests {
use std::path::Path;
use camino::Utf8Path;
use super::*;
@@ -368,7 +394,7 @@ mod tests {
)
}
fn get_cas_path(dir: &Path) -> PathBuf {
fn get_cas_path(dir: &Utf8Path) -> Utf8PathBuf {
let mut cas_path = dir.to_path_buf();
cas_path.push(".bsv");
cas_path
@@ -377,7 +403,7 @@ mod tests {
#[test]
fn test_create_simple_cas() {
let dir = tempfile::tempdir().expect("failed to create temp test dir");
let cas_path = get_cas_path(dir.path());
let cas_path = get_cas_path(Utf8Path::from_path(dir.path()).unwrap());
let config = get_config();
let cas = SimpleCas::create(cas_path, config)
@@ -391,7 +417,7 @@ mod tests {
#[test]
fn test_write_object() {
let dir = tempfile::tempdir().expect("failed to create temp test dir");
let cas_path = get_cas_path(dir.path());
let cas_path = get_cas_path(dir.path().try_into().unwrap());
let config = get_config();
let otype = ObjectType::new(b"blob").expect("failed to create object type");
@@ -421,7 +447,7 @@ mod tests {
use std::io::Write;
let dir = tempfile::tempdir().expect("failed to create temp test dir");
let cas_path = get_cas_path(dir.path());
let cas_path = get_cas_path(dir.path().try_into().unwrap());
let config = get_config();
let otype = ObjectType::new(b"blob").expect("failed to create object type");
@@ -460,7 +486,7 @@ mod tests {
#[test]
fn test_read_write_object() {
let dir = tempfile::tempdir().expect("failed to create temp test dir");
let cas_path = get_cas_path(dir.path());
let cas_path = get_cas_path(dir.path().try_into().unwrap());
let config = get_config();
let otype = ObjectType::new(b"blob").expect("failed to create object type");
@@ -480,7 +506,7 @@ mod tests {
#[test]
fn test_remove_object() {
let dir = tempfile::tempdir().expect("failed to create temp test dir");
let cas_path = get_cas_path(dir.path());
let cas_path = get_cas_path(dir.path().try_into().unwrap());
let config = get_config();
let otype = ObjectType::new(b"blob").expect("failed to create object type");
@@ -501,7 +527,7 @@ mod tests {
#[test]
fn test_object_id_iterator() {
let dir = tempfile::tempdir().expect("failed to create temp test dir");
let cas_path = get_cas_path(dir.path());
let cas_path = get_cas_path(dir.path().try_into().unwrap());
let config = get_config();
let mut cas = SimpleCas::create(cas_path.clone(), config)
@@ -615,7 +641,7 @@ mod tests {
#[test]
fn test_reference() {
let dir = tempfile::tempdir().expect("failed to create temp test dir");
let cas_path = get_cas_path(dir.path());
let cas_path = get_cas_path(dir.path().try_into().unwrap());
let config = get_config();
let mut cas = SimpleCas::create(cas_path.clone(), config)