Browse Source

Start implementing a cas in separate crates.

deprecated_bsvfs
Draklaw 4 years ago
parent
commit
5664beca57
  1. 297
      Cargo.lock
  2. 6
      Cargo.toml
  3. 13
      cas-core/Cargo.toml
  4. 40
      cas-core/src/cas.rs
  5. 122
      cas-core/src/error.rs
  6. 44
      cas-core/src/lib.rs
  7. 212
      cas-core/src/object_id.rs
  8. 61
      cas-core/src/object_metadata.rs
  9. 63
      cas-core/src/object_type.rs
  10. 252
      cas-core/src/pipeline.rs
  11. 15
      cas-simple/Cargo.toml
  12. 245
      cas-simple/src/lib.rs
  13. 2
      libbsv/Cargo.toml
  14. 137
      libbsv/src/core/error.rs
  15. 18
      libbsv/src/core/mod.rs
  16. 11
      libbsv/src/core/object.rs
  17. 6
      libbsv/src/core/object_id.rs
  18. 6
      libbsv/src/core/repository.rs
  19. 20
      libbsv/src/lib.rs

297
Cargo.lock

@ -2,62 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "addr2line"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03345e98af8f3d786b6d9f656ccfa6ac316d954e92bc4841f0bba20789d5fb5a"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backtrace"
version = "0.3.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.2.1"
@ -74,20 +18,24 @@ dependencies = [
] ]
[[package]] [[package]]
name = "bsv" name = "cas-core"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clap", "digest",
"error-chain", "sha2",
"hostname", "thiserror",
"libbsv",
] ]
[[package]] [[package]]
name = "cc" name = "cas-simple"
version = "1.0.67" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" "cas-core",
"digest",
"sha2",
"tempfile",
"toml",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -95,39 +43,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.1.1" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dec1028182c380cc45a2e2c5ec841134f2dfd0f8f5f0a5bcd68004f81b5efdf4" checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef"
dependencies = [ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crc32fast"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.9.0" version = "0.9.0"
@ -137,28 +61,6 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "error-chain"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
dependencies = [
"backtrace",
"version_check",
]
[[package]]
name = "flate2"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.4" version = "0.14.4"
@ -171,82 +73,20 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.2" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi", "wasi",
] ]
[[package]]
name = "gimli"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189"
[[package]]
name = "hermit-abi"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
"libc",
]
[[package]]
name = "hostname"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
dependencies = [
"libc",
"match_cfg",
"winapi",
]
[[package]]
name = "libbsv"
version = "0.1.0"
dependencies = [
"digest",
"error-chain",
"flate2",
"serde",
"sha2",
"tempfile",
"toml",
"uuid",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.94" version = "0.2.98"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
[[package]]
name = "match_cfg"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg",
]
[[package]]
name = "object"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170"
[[package]] [[package]]
name = "opaque-debug" name = "opaque-debug"
@ -262,9 +102,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.26" version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
dependencies = [ dependencies = [
"unicode-xid", "unicode-xid",
] ]
@ -280,9 +120,9 @@ dependencies = [
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.8.3" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [ dependencies = [
"libc", "libc",
"rand_chacha", "rand_chacha",
@ -292,9 +132,9 @@ dependencies = [
[[package]] [[package]]
name = "rand_chacha" name = "rand_chacha"
version = "0.3.0" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [ dependencies = [
"ppv-lite86", "ppv-lite86",
"rand_core", "rand_core",
@ -302,27 +142,27 @@ dependencies = [
[[package]] [[package]]
name = "rand_core" name = "rand_core"
version = "0.6.2" version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [ dependencies = [
"getrandom", "getrandom",
] ]
[[package]] [[package]]
name = "rand_hc" name = "rand_hc"
version = "0.3.0" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [ dependencies = [
"rand_core", "rand_core",
] ]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.8" version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
@ -336,31 +176,11 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "410f7acf3cb3a44527c5d9546bad4bf4e6c460915d5f9f2fc524498bfe8f70ce"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.126" version = "1.0.127"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "sha2" name = "sha2"
@ -375,17 +195,11 @@ dependencies = [
"opaque-debug", "opaque-debug",
] ]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.72" version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -407,12 +221,23 @@ dependencies = [
] ]
[[package]] [[package]]
name = "textwrap" name = "thiserror"
version = "0.11.0" version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
dependencies = [ dependencies = [
"unicode-width", "thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
dependencies = [
"proc-macro2",
"quote",
"syn",
] ]
[[package]] [[package]]
@ -430,34 +255,12 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "uuid"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [
"getrandom",
"serde",
]
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.3" version = "0.9.3"

6
Cargo.toml

@ -1,6 +1,8 @@
[workspace] [workspace]
members = [ members = [
"libbsv", "cas-core",
"bsv", "cas-simple",
# "libbsv",
# "bsv",
] ]

13
cas-core/Cargo.toml

@ -0,0 +1,13 @@
[package]
name = "cas-core"
version = "0.1.0"
authors = ["Simon Boyé <sim.boye@gmail.com>"]
edition = "2018"
license = "AGPL-3.0-or-later"
[dependencies]
thiserror = "1.0.25"
digest = { version = "0.9.0", features = ["alloc"] }
[dev-dependencies]
sha2 = "0.9.5"

40
cas-core/src/cas.rs

@ -0,0 +1,40 @@
// This file is part of bsv.
//
// bsv is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
// more details.
//
// You should have received a copy of the Affero GNU General Public License
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
use super::error::Result;
use super::object_id::ObjectId;
use super::object_type::ObjectType;
use super::object_metadata::ObjectMetadata;
// pub struct ObjectIdIterator;
pub trait Cas {
fn object_id_from_string(&self, hex: &str) -> Result<ObjectId>;
fn object_id_from_partial(&self, hex: &str) -> Result<ObjectId>;
fn has_object_id(&self, oid: &ObjectId) -> Result<bool>;
// fn iter_object_id(&self) -> Result<ObjectIdIterator>;
fn read_object(&self, oid: &ObjectId) -> Result<(ObjectMetadata, &dyn std::io::Read)>;
fn write_object(&mut self, otype: ObjectType, data: &mut dyn std::io::Read) -> Result<ObjectId>;
fn remove_object(&mut self, oid: &ObjectId) -> Result<()>;
fn read_ref(&self, key: &str) -> Result<ObjectId>;
fn write_ref(&mut self, key: &str, value: &ObjectId) -> Result<()>;
fn remove_ref(&mut self, key: &str) -> Result<()>;
}

122
cas-core/src/error.rs

@ -0,0 +1,122 @@
// This file is part of bsv.
//
// bsv is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
// more details.
//
// You should have received a copy of the Affero GNU General Public License
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
// use std::path::PathBuf;
/// Result type used through cas-core.
pub type Result<T> = std::result::Result<T, Box<Error>>;
/// Error type used through cas-core.
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
pub enum Error {
// #[error("failed to create repository: {message}")]
// RepositoryCreationFailed {
// message: String,
// source: Option<Box<dyn std::error::Error>>,
// },
// #[error("invalid object id: {message}")]
// InvalidObjectId {
// message: String,
// source: Option<Box<dyn std::error::Error>>,
// },
// #[error("invalid size (got: {size}, expected: {expected})")]
// InvalidSize {
// size: usize,
// expected: usize,
// },
// #[error("non-empty directory ({dir})")]
// NonEmptyDirectory {
// dir: PathBuf
// },
// #[error("invalid character(s) ({characters})")]
// InvalidCharacters {
// characters: String,
// },
// #[error("invalid object type ({otype:?})")]
// InvalidObjectType {
// otype: [u8; 4],
// },
// #[error("invalid object size (expected {expected}, got {size})")]
// InvalidObjectSize {
// size: u64,
// expected: u64,
// },
// #[error("unsupported file type")]
// UnsupportedFileType,
// #[error("invalid path ({path})")]
// InvalidPath { path: PathBuf },
// #[error("io error{}", format_optional_path(path))]
// IoError {
// source: std::io::Error,
// path: Option<PathBuf>,
// },
#[error("{0}")]
Error(String),
}
impl Error {
// pub fn repository_creation_failed<M: Into<String>>(message: M) -> Box<Error> {
// Box::new(Error::RepositoryCreationFailed {
// message: message.into(),
// source: None,
// })
// }
// pub fn repository_creation_failed_from<M: Into<String>>(source: Box<dyn std::error::Error>, message: M) -> Box<Error> {
// Box::new(Error::RepositoryCreationFailed {
// message: message.into(),
// source: Some(source),
// })
// }
// pub fn invalid_object_id<M: Into<String>>(message: M) -> Box<Error> {
// Box::new(Error::InvalidObjectId {
// message: message.into(),
// source: None,
// })
// }
// pub fn invalid_object_id_from<M: Into<String>>(source: Box<dyn std::error::Error>, message: M) -> Box<Error> {
// Box::new(Error::InvalidObjectId {
// message: message.into(),
// source: Some(source),
// })
// }
pub fn error<M: Into<String>>(message: M) -> Box<Error> {
Box::new(Error::Error(message.into()))
}
}
// fn format_optional_path(maybe_path: &Option<PathBuf>) -> String {
// match maybe_path {
// Some(path) => { format!(" ({:?})", path) },
// None => { String::new() }
// }
// }

44
cas-core/src/lib.rs

@ -0,0 +1,44 @@
// This file is part of bsv.
//
// bsv is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
// more details.
//
// You should have received a copy of the Affero GNU General Public License
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
//! # cas-core
//!
//! `cas-core` provides traits and types to interface with content-addressable
//! storage.
#![feature(inherent_ascii_escape)]
extern crate thiserror;
extern crate digest;
mod error;
mod object_id;
mod object_type;
mod object_metadata;
mod pipeline;
mod cas;
pub use crate::{
error::{Error, Result},
object_id::{ObjectId, hex, write_hex},
object_type::{ObjectType},
object_metadata::{ObjectMetadata},
pipeline::{Pipeline, DefaultPipeline},
cas::{Cas},
};

212
cas-core/src/object_id.rs

@ -0,0 +1,212 @@
// This file is part of bsv.
//
// bsv is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
// more details.
//
// You should have received a copy of the Affero GNU General Public License
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
use std::fmt;
use std::str::{FromStr};
use std::sync::Arc;
use super::error::*;
/// A unique identifier for an object.
///
/// This is the handle used to reference an Object. This is an opaque type that uniquely identify an
/// object. It can be compared to another ObjectId, be hashed and that's about it.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ObjectId {
id: Arc<Vec<u8>>,
}
impl ObjectId {
pub fn new(id: &[u8]) -> ObjectId {
ObjectId {
id: Arc::new(id.into()),
}
}
pub fn id(&self) -> &[u8] {
self.id.as_slice()
}
}
impl FromStr for ObjectId {
type Err = Box<Error>;
fn from_str(id_str: &str) -> Result<ObjectId> {
Ok(ObjectId {
id: Arc::new(
CharPairs::new(id_str, false)
.map(parse_byte)
.collect::<Result<Vec<_>>>()?
),
})
}
}
impl fmt::Display for ObjectId {
fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
write_hex(f, self.id.as_slice())
}
}
impl fmt::Debug for ObjectId {
fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
write!(f, "ObjectId::new(\"{}\")", self)
}
}
pub fn hex(bytes: &[u8]) -> String {
let mut hex = String::with_capacity(bytes.len() * 2);
write_hex(&mut hex, bytes).expect("can't format bytes ?");
hex
}
pub fn write_hex<W: std::fmt::Write>(write: &mut W, bytes: &[u8])
-> std::result::Result<(), std::fmt::Error>
{
for byte in bytes.iter() {
write!(write, "{:02x}", byte)?;
}
Ok(())
}
struct CharPairs<'a> {
string: &'a str,
chars: std::str::CharIndices<'a>,
char_count: usize,
index: usize,
allow_partial: bool,
}
impl<'a> CharPairs<'a> {
fn new(string: &'a str, allow_partial: bool) -> Self {
let mut char_pairs = Self {
string,
chars: string.char_indices(),
char_count: 0,
index: 0,
allow_partial,
};
char_pairs.next_char();
char_pairs
}
fn next_char(&mut self) -> bool {
if self.index == self.string.len() {
false
}
else {
match self.chars.next() {
Some((i, _)) => {
self.char_count += 1;
self.index = i;
},
None => {
self.index = self.string.len();
}
}
true
}
}
}
impl<'a> Iterator for CharPairs<'a> {
type Item = Result<&'a str>;
fn next(&mut self) -> Option<Self::Item> {
let start_index = self.index;
if !self.next_char() {
return None;
}
if self.next_char() || self.allow_partial {
Some(Ok(&self.string[start_index..self.index]))
}
else {
Some(Err(Error::error(&format!("invalid string: got {} characters, expected even number", self.char_count))))
}
}
}
fn parse_byte(maybe_str_byte: Result<&str>) -> Result<u8> {
match maybe_str_byte {
Ok(str_byte) =>
u8::from_str_radix(str_byte, 16)
.map_err(|_| Error::error("invalid character")),
Err(err) => Err(err),
}
}
#[cfg(test)]
mod tests {
use std::str::{FromStr};
use crate::error::*;
use super::ObjectId;
#[test]
fn object_id_display() {
let obj = ObjectId::new(&[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
]);
assert_eq!(format!("{}", obj), "0001020304050607");
}
#[test]
fn object_id_debug() {
let obj = ObjectId::new(&[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
]);
assert_eq!(format!("{:?}", obj), "ObjectId::new(\"0001020304050607\")");
}
#[test]
fn str_to_object_id() {
let obj = ObjectId::from_str("0001020304050607")
.expect("object id parsing failed");
let obj_ref = ObjectId::new(&[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
]);
assert_eq!(obj, obj_ref);
}
#[test]
fn str_to_object_id_invalid_size() {
let result = ObjectId::from_str("000102030405060");
assert_eq!(
result.expect_err("object id parsing should have failed"),
Error::error("invalid string: got 15 characters, expected even number"),
);
}
#[test]
fn str_to_object_id_invalid_character() {
let result = ObjectId::from_str("00010203 4050607");
assert_eq!(
result.expect_err("object id parsing should have failed"),
Error::error("invalid character"),
);
}
}

61
cas-core/src/object_metadata.rs

@ -0,0 +1,61 @@
// This file is part of bsv.
//
// bsv is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
// more details.
//
// You should have received a copy of the Affero GNU General Public License
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
use super::object_type::{ObjectType};
#[derive(Clone, Eq, PartialEq)]
pub struct ObjectMetadata {
otype: ObjectType,
size: usize,
}
impl ObjectMetadata {
pub fn new(otype: ObjectType, size: usize) -> Self {
Self {
otype,
size,
}
}
pub fn otype(&self) -> &ObjectType {
&self.otype
}
pub fn size(&self) -> usize {
self.size
}
}
impl std::fmt::Debug for ObjectMetadata {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
f.write_fmt(format_args!("<meta {} {}>", self.otype().id().escape_ascii().to_string(), self.size))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn object_metadata_debug() {
let meta = ObjectMetadata::new(ObjectType::new(b"tree").unwrap(), 1234);
assert_eq!(meta, meta);
assert_eq!(format!("{:?}", meta), "<meta tree 1234>");
}
}

63
cas-core/src/object_type.rs

@ -0,0 +1,63 @@
// This file is part of bsv.
//
// bsv is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
// more details.
//
// You should have received a copy of the Affero GNU General Public License
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
use super::error::*;
#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ObjectType {
id: [u8; 4],
}
impl ObjectType {
pub fn new(id: &[u8]) -> Result<Self> {
if id.len() != 4 {
Err(Error::error("Invalid object type size."))
}
else {
let mut buf = [0; 4];
for (i, b) in id.iter().enumerate() {
buf[i] = *b;
}
Ok(Self {
id: buf,
})
}
}
pub fn id(&self) -> &[u8; 4] {
&self.id
}
}
impl std::fmt::Debug for ObjectType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
f.write_fmt(format_args!("<otype {}>", self.id.escape_ascii().to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn object_type_debug() {
let otype = ObjectType::new(b"tree").unwrap();
assert_eq!(otype, otype);
assert_eq!(format!("{:?}", otype), "<otype tree>");
}
}

252
cas-core/src/pipeline.rs

@ -0,0 +1,252 @@
// This file is part of bsv.
//
// bsv is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
// more details.
//
// You should have received a copy of the Affero GNU General Public License
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
use digest::DynDigest;
use super::error::*;
use super::object_id::ObjectId;
pub trait Reader {
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
fn finalize(self: Box<Self>) -> Result<ObjectId>;
fn read_all(&mut self) -> Result<Vec<u8>> {
let mut buffer = [0u8; 1 << 13];
let mut data = Vec::new();
let mut count = usize::MAX;
while count != 0 {
count = self.read(&mut buffer)?;
if count != 0 {
data.extend_from_slice(&buffer[..count]);
}
}
Ok(data)
}
}
pub trait Writer {
fn write(&mut self, buf: &[u8]) -> Result<()>;
fn finalize(self: Box<Self>) -> Result<ObjectId>;
}
pub trait Pipeline {
fn new_reader(&self, reader: Box<dyn Reader>) -> Box<dyn Reader>;
fn new_writer(&self, writer: Box<dyn Writer>) -> Box<dyn Writer>;
}
impl<R: std::io::Read> Reader for R {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
<Self as std::io::Read>::read(self, buf).or_else(|err|
Err(Error::error(format!("Read error: {}", err)))
)
}
fn finalize(self: Box<Self>) -> Result<ObjectId> {
Err(Error::error("reader pipline has no digest step"))
}
}
impl<W: std::io::Write> Writer for W {
fn write(&mut self, buf: &[u8]) -> Result<()> {
self.write_all(buf).or_else(|err|
Err(Error::error(format!("Write error: {}", err)))
)
}
fn finalize(self: Box<Self>) -> Result<ObjectId> {
Err(Error::error("reader pipline has no digest step"))
}
}
pub struct DigestReader<'a> {
reader: Box<dyn Reader + 'a>,
digest: Option<Box<dyn DynDigest + 'a>>,
oid: Option<ObjectId>,
}
impl<'a> DigestReader<'a> {
pub fn new(reader: Box<dyn Reader + 'a>, digest: Box<dyn DynDigest + 'a>) -> Self {
return DigestReader {
reader,
digest: Some(digest),
oid: None,
}
}
}
impl<'a> Reader for DigestReader<'a> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let count = if let Some(dig) = &mut self.digest {
let count = self.reader.read(buf)?;
dig.update(&buf[..count]);
count
}
else {
0
};
if count == 0 {
if let Some(digest) = self.digest.take() {
self.oid = Some(ObjectId::new(&digest.finalize()));
}
}
Ok(count)
}
fn finalize(self: Box<Self>) -> Result<ObjectId> {
self.oid.ok_or_else(|| Error::error("Reader not finalized"))
}
}
pub struct DigestWriter<'a> {
writer: Box<dyn Writer + 'a>,
digest: Box<dyn DynDigest + 'a>,
}
impl<'a> DigestWriter<'a> {
pub fn new(writer: Box<dyn Writer + 'a>, digest: Box<dyn DynDigest + 'a>) -> Self {
return DigestWriter {
writer,
digest,
}
}
}
impl<'a> Writer for DigestWriter<'a> {
fn write(&mut self, buf: &[u8]) -> Result<()> {
self.digest.update(buf);
self.writer.write(buf)?;
Ok(())
}
fn finalize(self: Box<Self>) -> Result<ObjectId> {
Ok(ObjectId::new(&self.digest.finalize()))
}
}
pub struct DefaultPipeline {
digest: Box<dyn DynDigest>,
}
impl DefaultPipeline {
pub fn new(digest: Box<dyn DynDigest>) -> Self {
Self {
digest,
}
}
}
impl Pipeline for DefaultPipeline {
fn new_reader(&self, reader: Box<dyn Reader>) -> Box<dyn Reader> {
Box::new(DigestReader::new(reader, self.digest.box_clone()))
}
fn new_writer(&self, writer: Box<dyn Writer>) -> Box<dyn Writer> {
Box::new(DigestWriter::new(writer, self.digest.box_clone()))
}
}
#[cfg(test)]
mod tests {
extern crate sha2;
use std::str::FromStr;
use sha2::Sha256;
use super::*;
#[test]
fn std_read_as_reader() {
let data = b"Hello World!".to_vec();
let mut reader: Box<dyn Reader> = Box::new(data.as_slice());
let data2 = reader.read_all().expect("read failed");
let maybe_oid = reader.finalize();
assert_eq!(data2, data);
assert!(maybe_oid.is_err());
}
#[test]
fn std_write_as_writer() {
let data = b"Hello World!".to_vec();
let mut buffer = [0u8; 32];
let maybe_oid = {
let mut writer: Box<dyn Writer> = Box::new(&mut buffer[..]);
writer.write(&data).expect("write failed");
writer.finalize()
};
assert_eq!(buffer[..data.len()], data);
// Check that the rest of the buffer is unchanged
assert!(buffer[data.len()..].iter().all(|&v| v == 0));
assert!(maybe_oid.is_err());
}
#[test]
fn digest_reader() {
let data = b"Hello World!".to_vec();
let oid = ObjectId::from_str(
"7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069"
).expect("invalid object id");
let mut reader = Box::new(DigestReader::new(
Box::new(data.as_slice()),
Box::new(Sha256::default()),
));
let data2 = reader.read_all().expect("read failed");
let oid2 = reader.finalize().expect("finalize failed");
assert_eq!(data2, data);
assert_eq!(oid2, oid);
}
#[test]
fn digest_writer() {
let data = b"Hello World!".to_vec();
let oid = ObjectId::from_str(
"7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069"
).expect("invalid object id");
let mut buffer = [0u8; 32];
let oid2 = {
let mut writer = Box::new(DigestWriter::new(
Box::new(&mut buffer[..]),
Box::new(Sha256::default()),
));
writer.write(&data).expect("write failed");
writer.finalize().expect("finalize failed")
};
assert_eq!(buffer[..data.len()], data);
// Check that the rest of the buffer is unchanged
assert!(buffer[data.len()..].iter().all(|&v| v == 0));
assert_eq!(oid2, oid);
}
}

15
cas-simple/Cargo.toml

@ -0,0 +1,15 @@
[package]
name = "cas-simple"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
digest = { version = "0.9.0", features = ["alloc"] }
sha2 = "0.9.5"
toml = "0.5.8"
cas-core = { path = "../cas-core" }
tempfile = "3.2.0"
[dev-dependencies]

245
cas-simple/src/lib.rs

@ -0,0 +1,245 @@
// This file is part of bsv.
//
// bsv is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// cdb is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
// more details.
//
// You should have received a copy of the Affero GNU General Public License
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
//! # cas-simple
//!
//! `cas-simple` implements a simple content addressable storage using
//! cas-core.
extern crate digest;
extern crate sha2;
extern crate toml;
extern crate cas_core;
use std::str::FromStr;
use std::path::{Path, PathBuf};
use digest::DynDigest;
use toml::Value;
use cas_core::{Cas, Error, hex, ObjectId, ObjectMetadata, ObjectType, Result};
fn new_digest(id: &str) -> Result<Box<dyn DynDigest>> {
match id {
"sha256" => { Ok(Box::new(sha2::Sha256::default())) },
_ => { Err(Error::error(format!("unknown digest '{}'", id))) }
}
}
pub struct SimpleCas {
db_path: PathBuf,
digest: Box<dyn DynDigest>,
config: Value,
}
impl SimpleCas {
pub fn create(db_path: PathBuf, config: Value) -> Result<Self> {
let digest_id = config["cas"]["digest"].as_str()
.ok_or_else(|| Error::error(
"mandatory cas.digest value is invalid or missing from config"
))?;
let digest = new_digest(digest_id)?;
if db_path.exists() {
return Err(Error::error(format!(
"failed to create SimpleCas: target directory already exists ({})",
db_path.to_string_lossy(),
)));
}
for path in [
&db_path,
&obj_dir(&db_path),
&ref_dir(&db_path),
&tmp_dir(&db_path),
] {
std::fs::create_dir(path).or_else(|e|
Err(Error::error(format!(
"failed to create directory ({}): {}",
path.to_string_lossy(), e,
)))
)?;
}
write_config(&config, &db_path)?;
Ok(SimpleCas {
db_path,
digest,
config,
})
}
pub fn open(db_path: PathBuf) -> Result<Self> {
let config = read_config(&db_path)?;
let digest_id = config["cas"]["digest"].as_str()
.ok_or_else(|| Error::error(
"mandatory cas.digest value is invalid or missing from config"
))?;
let digest = new_digest(digest_id)?;
Ok(SimpleCas {
db_path,
digest,
config,
})
}
pub fn save_config(&self) -> Result<()> {
write_config(&self.config, &self.db_path)
}
}
impl Cas for SimpleCas {
fn object_id_from_string(&self, hex: &str) -> Result<ObjectId> {
if hex.len() == self.digest.output_size() * 2 {
ObjectId::from_str(hex)
}
else {
Err(Error::error(format!(
"invalid object id size: got {}, expected {}",
hex.len(), self.digest.output_size() * 2,
)))
}
}
fn object_id_from_partial(&self, _hex: &str) -> Result<ObjectId> {
Err(Error::error("Not implemented"))
}
fn has_object_id(&self, oid: &ObjectId) -> Result<bool> {
let opath = obj_path(&self.db_path, oid);
Ok(opath.is_file())
}
fn read_object(&self, _oid: &ObjectId) -> Result<(ObjectMetadata, &dyn std::io::Read)> {
Err(Error::error("Not implemented"))
}
fn write_object(&mut self, _otype: ObjectType, _data: &mut dyn std::io::Read) -> Result<ObjectId> {
Err(Error::error("Not implemented"))
}
fn remove_object(&mut self, _oid: &ObjectId) -> Result<()> {
Err(Error::error("Not implemented"))
}
fn read_ref(&self, _key: &str) -> Result<ObjectId> {
Err(Error::error("Not implemented"))
}
fn write_ref(&mut self, _key: &str, _value: &ObjectId) -> Result<()> {
Err(Error::error("Not implemented"))
}
fn remove_ref(&mut self, _key: &str) -> Result<()> {
Err(Error::error("Not implemented"))
}
}
fn obj_dir(cas_path: &Path) -> PathBuf {
cas_path.join("obj")
}
fn obj_path(cas_path: &Path, oid: &ObjectId) -> PathBuf {
let mut path = cas_path.to_path_buf();
path.push(hex(&oid.id()[0..1]));
path.push(hex(&oid.id()[1..2]));
path.push(hex(&oid.id()[2..]));
path
}
fn ref_dir(cas_path: &Path) -> PathBuf {
cas_path.join("ref")
}
fn tmp_dir(cas_path: &Path) -> PathBuf {
cas_path.join("tmp")
}
fn config_path(cas_path: &Path) -> PathBuf {
cas_path.join("config")
}
fn read_config(db_path: &Path) -> Result<Value> {
use std::io::Read;
let mut file = std::fs::File::open(config_path(db_path)).or_else(|err|
Err(Error::error(format!("invalid repository: no config file: {}", err)))
)?;
let mut config_str = String::new();
file.read_to_string(&mut config_str).or_else(|err|
Err(Error::error(format!("cannot read config file: {}", err)))
)?;
let config = toml::from_str(&config_str).or_else(|err|
Err(Error::error(format!("error while reading config file: {}", err)))
)?;
Ok(config)
}
fn write_config(config: &Value, db_path: &Path) -> Result<()> {
use std::io::Write;
let config_str = toml::to_string_pretty(config).or_else(|err|
Err(Error::error(format!("failed to serialize config: {}", err)))
)?;
let mut file = tempfile::NamedTempFile::new_in(tmp_dir(db_path)).or_else(|err|
Err(Error::error(format!("cannot create temp config file: {}", err)))
)?;
file.write_all(config_str.as_bytes()).or_else(|err|
Err(Error::error(format!("failed to write to temp config: {}", err)))
)?;
file.persist(config_path(db_path)).or_else(|err|
Err(Error::error(format!("failed to (over)write config: {}", err)))
)?;
Ok(())
}
#[cfg(test)]
mod tests {
extern crate tempfile;
use super::*;
#[test]
fn create_simple_cas() {
let dir = tempfile::tempdir()
.expect("failed to create temp test dir");
let cas_path = {
let mut cas_path = dir.path().to_path_buf();
cas_path.push(".bsv");
cas_path
};
let config = toml::toml!(
[cas]
digest = "sha256"
);
let cas = SimpleCas::create(cas_path, config)
.expect("failed to create SimpleCas object");
let oid = cas.object_id_from_string("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
.expect("failed to create object id");
assert!(!cas.has_object_id(&oid).expect("has_object_id failed"));
}
}

2
libbsv/Cargo.toml

@ -6,7 +6,7 @@ edition = "2018"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
[dependencies] [dependencies]
error-chain = "0.12.2" thiserror = "1.0.25"
serde = { version = "1.0.106", features = ["derive"] } serde = { version = "1.0.106", features = ["derive"] }
toml = "0.5.6" toml = "0.5.6"
uuid = { version = "0.8.1", features = ["serde", "v4"] } uuid = { version = "0.8.1", features = ["serde", "v4"] }

137
libbsv/src/core/error.rs

@ -17,50 +17,99 @@
use std::path::PathBuf; use std::path::PathBuf;
error_chain! { pub type Result<T> = std::result::Result<T, Box<Error>>;
foreign_links {
Io(::std::io::Error); #[non_exhaustive]
} #[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("failed to create repository: {message}")]
RepositoryCreationFailed {
message: String,
source: Option<Box<dyn std::error::Error>>,
},
#[error("invalid object id: {message}")]
InvalidObjectId {
message: String,
source: Option<Box<dyn std::error::Error>>,
},
#[error("invalid size (got: {size}, expected: {expected})")]
InvalidSize {
size: usize,
expected: usize,
},
#[error("non-empty directory ({dir})")]
NonEmptyDirectory {
dir: PathBuf
},
#[error("invalid character(s) ({characters})")]
InvalidObjectIdCharacter {
characters: String,
},
#[error("invalid object type ({otype:?})")]
InvalidObjectType {
otype: [u8; 4],
},
#[error("invalid object size (expected {expected}, got {size})")]
InvalidObjectSize {
size: u64,
expected: u64,
},
#[error("unsupported file type")]
UnsupportedFileType,
#[error("invalid path ({path})")]
InvalidPath { path: PathBuf },
#[error("io error{}", format_optional_path(path))]
IoError {
source: std::io::Error,
path: Option<PathBuf>,
},
#[error("{0}")]
Other(String),
}
pub fn repository_creation_failed<M: Into<String>>(message: M) -> Box<Error> {
Box::new(Error::RepositoryCreationFailed {
message: message.into(),
source: None,
})
}
pub fn repository_creation_failed_from<M: Into<String>>(source: Box<dyn std::error::Error>, message: M) -> Box<Error> {
Box::new(Error::RepositoryCreationFailed {
message: message.into(),
source: Some(source),
})
}
pub fn invalid_object_id<M: Into<String>>(message: M) -> Box<Error> {
Box::new(Error::InvalidObjectId {
message: message.into(),
source: None,
})
}
pub fn invalid_object_id_from<M: Into<String>>(source: Box<dyn std::error::Error>, message: M) -> Box<Error> {
Box::new(Error::InvalidObjectId {
message: message.into(),
source: Some(source),
})
}
errors { fn format_optional_path(maybe_path: &Option<PathBuf>) -> String {
NoRepositorySpecifiedError { match maybe_path {
description("No repository specifed") Some(path) => { format!(" ({:?})", path) },
display("No repository specifed. Use -r or set BSV_REPOSITORY environment variable.") None => { String::new() }
}
NonEmptyDirectory(dir: PathBuf) {
description("Non-empty directory")
display("Target directory {:?} must be empty.", dir)
}
InvalidObjectIdSize {
description("Object id has an invalid size")
display("Object id has an invalid size.")
}
InvalidObjectIdCharacter {
description("Object id contains invalid character")
display("Object id contains invalid character.")
}
InvalidObjectType(otype: [u8; 4]) {
description("Object has an invalid object type")
display("Object has an invalid object type {:?}.", otype)
}
MismatchingObjectSize(actual: u64, expected: u64) {
description("Mismatching object size")
display("Mismatching object size: expected {}, got {}.", actual, expected)
}
UnsupportedFileType {
description("Unsupported file type")
display("Unsupported file type.")
}
InvalidPath(path: PathBuf) {
description("Invalid path")
display("Invalid path: {:?}.", path)
}
} }
} }

18
libbsv/src/core/mod.rs

@ -15,18 +15,18 @@
pub mod error; pub mod error;
pub mod config; // pub mod config;
pub mod object_id; // pub mod object_id;
pub mod object; // pub mod object;
pub mod repository; // pub mod repository;
pub const NAME: &str = env!("CARGO_PKG_NAME"); pub const NAME: &str = env!("CARGO_PKG_NAME");
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub use error::{Error, Result, ErrorKind}; pub use error::{Error, Result};
pub use config::{Config, RepositoryConfig}; // pub use config::{Config, RepositoryConfig};
pub use object_id::ObjectId; // pub use object_id::ObjectId;
pub use object::{ObjectType, OTYPE_BLOB, OTYPE_TREE}; // pub use object::{ObjectType, OTYPE_BLOB, OTYPE_TREE};
pub use repository::Repository; // pub use repository::Repository;

11
libbsv/src/core/object.rs

@ -35,7 +35,7 @@ pub fn object_type_from_metadata(md: &Metadata) -> Result<ObjectType> {
Ok(*OTYPE_TREE) Ok(*OTYPE_TREE)
} }
else { else {
Err(ErrorKind::UnsupportedFileType.into()) Err(Error::UnsupportedFileType)
} }
} }
@ -137,7 +137,12 @@ impl TreeItem {
} }
pub fn from_file_path_and_id(file_path: &Path, oid: &ObjectId) -> Result<TreeItem> { pub fn from_file_path_and_id(file_path: &Path, oid: &ObjectId) -> Result<TreeItem> {
let md = file_path.symlink_metadata()?; let md = file_path.symlink_metadata()
.map_err(|err| Error::IoError {
source: err,
path: Some(file_path.to_path_buf())
})?;
Ok(TreeItem { Ok(TreeItem {
otype: object_type_from_metadata(&md)?, otype: object_type_from_metadata(&md)?,
oid: oid.clone(), oid: oid.clone(),
@ -145,7 +150,7 @@ impl TreeItem {
permissions: Permissions::from_unix_mode(md.mode()), permissions: Permissions::from_unix_mode(md.mode()),
name: file_path.file_name() name: file_path.file_name()
.and_then(|n| n.to_str()) .and_then(|n| n.to_str())
.ok_or_else(|| ErrorKind::InvalidPath(file_path.into()))? .ok_or_else(|| Error::InvalidPath { path: file_path.into() })?
.into(), .into(),
}) })
} }

6
libbsv/src/core/object_id.rs

@ -44,7 +44,7 @@ impl FromStr for ObjectId {
fn from_str(id_str: &str) -> Result<ObjectId> { fn from_str(id_str: &str) -> Result<ObjectId> {
if id_str.len() % 2 != 0 { if id_str.len() % 2 != 0 {
return Err(ErrorKind::InvalidObjectIdSize.into()); return Err(Error::InvalidObjectIdSize);
} }
let byte_count = id_str.len() / 2; let byte_count = id_str.len() / 2;
@ -53,9 +53,9 @@ impl FromStr for ObjectId {
for byte_index in 0..byte_count { for byte_index in 0..byte_count {
let str_index = byte_index * 2; let str_index = byte_index * 2;
let byte_str = id_str.get(str_index..(str_index + 2)) let byte_str = id_str.get(str_index..(str_index + 2))
.ok_or(ErrorKind::InvalidObjectIdCharacter)?; .ok_or(Error::InvalidObjectIdCharacter)?;
id.push(u8::from_str_radix(byte_str, 16) id.push(u8::from_str_radix(byte_str, 16)
.or(Err(ErrorKind::InvalidObjectIdCharacter))?); .or(Err(Error::InvalidObjectIdCharacter))?);
} }
Ok(ObjectId { Ok(ObjectId {

6
libbsv/src/core/repository.rs

@ -39,14 +39,14 @@ pub struct Repository {
impl Repository { impl Repository {
pub fn create(path: &Path, device_name: &str) -> Result<Repository> { pub fn create(path: &Path, device_name: &str) -> Result<Repository> {
if path.exists() { if path.exists() {
bail!(ErrorKind::NonEmptyDirectory(path.into())) return Err(Error::NonEmptyDirectory { dir: path.to_path_buf() });
} }
if device_name.is_empty() { if device_name.is_empty() {
bail!("Device name must not be empty.") return Err(Error::RepositoryCreationFailed("Device name must not be empty.".to_string()));
} }
create_dir(&path).chain_err(|| "Failed to create repository.")?; create_dir(&path).map_err(|err| Error::IoError("Failed to create repository.")?;
let config = Rc::new( let config = Rc::new(
Config { Config {

20
libbsv/src/lib.rs

@ -14,20 +14,20 @@
// along with cdb. If not, see <https://www.gnu.org/licenses/>. // along with cdb. If not, see <https://www.gnu.org/licenses/>.
#[macro_use] // #[macro_use]
extern crate error_chain; extern crate thiserror;
extern crate serde; extern crate serde;
pub mod core; pub mod core;
pub mod simple_db; // pub mod simple_db;
pub use crate::core::{ // pub use crate::core::{
Error, Result, ErrorKind, // Error, Result,
Config, RepositoryConfig, // Config, RepositoryConfig,
ObjectId, ObjectType, // ObjectId, ObjectType,
Repository, // Repository,
OTYPE_BLOB, OTYPE_TREE, // OTYPE_BLOB, OTYPE_TREE,
}; // };

Loading…
Cancel
Save