Restructure project and some progress.

This commit is contained in:
2020-09-24 21:45:02 +02:00
parent 688b6eb15a
commit 9bf0cf7b60
18 changed files with 565 additions and 159 deletions

2
libbsv/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/target
Cargo.lock

13
libbsv/Cargo.toml Normal file
View File

@@ -0,0 +1,13 @@
[package]
name = "libbsv"
version = "0.1.0"
authors = ["Simon Boyé <sim.boye@gmail.com>"]
edition = "2018"
license = "AGPL-3.0-or-later"
[dependencies]
error-chain = "0.12.2"
serde = "1.0.106"
toml = "0.5.6"
uuid = { version = "0.8.1", features = ["serde", "v4"] }
tempfile = "3.1.0"

34
libbsv/src/core/config.rs Normal file
View File

@@ -0,0 +1,34 @@
// 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;
use uuid::Uuid;
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct RepositoryConfig {
pub path: PathBuf,
pub device_name: String,
pub uuid: Uuid,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
pub repository: RepositoryConfig,
}

46
libbsv/src/core/error.rs Normal file
View File

@@ -0,0 +1,46 @@
// 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;
error_chain! {
foreign_links {
Io(::std::io::Error);
}
errors {
NoRepositorySpecifiedError {
description("No repository specifed")
display("No repository specifed. Use -r or set BSV_REPOSITORY environment variable.")
}
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")
}
}
}

30
libbsv/src/core/mod.rs Normal file
View File

@@ -0,0 +1,30 @@
// 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/>.
pub mod error;
pub mod config;
pub mod object_id;
pub mod repository;
pub const NAME: &'static str = env!("CARGO_PKG_NAME");
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub use error::Error;
pub use config::{Config, RepositoryConfig};
pub use object_id::ObjectId;
pub use repository::Repository;

View File

@@ -0,0 +1,110 @@
// 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 super::error::*;
/// A unique identifier for an object.
///
/// This is the handle used to referenc 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: Vec<u8>,
}
impl ObjectId {
pub fn new(id: Vec<u8>) -> ObjectId {
ObjectId {
id
}
}
pub fn from_str(id_str: &str) -> Result<ObjectId> {
if id_str.len() % 2 != 0 {
return Err(ErrorKind::InvalidObjectIdSize.into());
}
let byte_count = id_str.len() / 2;
let mut id = Vec::with_capacity(byte_count);
for byte_index in 0..byte_count {
let str_index = byte_index * 2;
let byte_str = id_str.get(str_index..(str_index + 2))
.ok_or(ErrorKind::InvalidObjectIdCharacter)?;
id.push(u8::from_str_radix(byte_str, 16)
.or(Err(ErrorKind::InvalidObjectIdCharacter))?);
}
Ok(ObjectId {
id
})
}
}
impl fmt::Display for ObjectId {
fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
for byte in self.id.iter() {
write!(f, "{:02x}", byte)?;
}
Ok(())
}
}
impl fmt::Debug for ObjectId {
fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
write!(f, "ObjectId::new(\"{}\")", self)
}
}
#[cfg(test)]
mod tests {
use super::ObjectId;
#[test]
fn object_id_display() {
let obj = ObjectId::new(vec![
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
]);
assert_eq!(format!("{}", obj), "0001020304050607");
}
#[test]
fn object_id_debug() {
let obj = ObjectId::new(vec![
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").unwrap();
let obj_ref = ObjectId::new(vec![
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
]);
assert_eq!(obj, obj_ref);
}
}

View File

@@ -0,0 +1,80 @@
// 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::{Path, PathBuf};
use std::fs::{self, create_dir};
use std::rc::Rc;
use uuid::Uuid;
use super::error::*;
use super::config::{Config, RepositoryConfig};
use crate::simple_db::SimpleDb;
const CONFIG_FILENAME: &'static str = ".bsv";
#[derive(Debug)]
pub struct Repository {
config: Rc<Config>,
db: SimpleDb,
}
impl Repository {
pub fn create(path: &Path, device_name: &str) -> Result<Repository> {
if path.exists() {
bail!(ErrorKind::NonEmptyDirectory(path.into()))
}
if device_name.len() == 0 {
bail!("Device name must not be empty.")
}
create_dir(&path).chain_err(|| "Failed to create repository.")?;
let config = Rc::new(
Config {
repository: RepositoryConfig {
path: path.into(),
device_name: device_name.into(),
uuid: Uuid::new_v4(),
},
}
);
let config_path = {
let mut config_path = PathBuf::from(path);
config_path.push(CONFIG_FILENAME);
config_path
};
fs::write(
config_path,
toml::to_string(&*config)
.chain_err(|| format!("Failed to serialize {}.", CONFIG_FILENAME))?,
).chain_err(||
format!("Failed to create repository: failed to write {}.", CONFIG_FILENAME)
)?;
Ok(Repository {
config: Rc::clone(&config),
db: SimpleDb::new(path.into())?,
})
}
}

32
libbsv/src/lib.rs Normal file
View File

@@ -0,0 +1,32 @@
// 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/>.
#[macro_use]
extern crate error_chain;
pub mod core;
pub mod simple_db;
pub use crate::core::{
Error,
Config, RepositoryConfig,
ObjectId,
Repository,
};
pub use crate::simple_db::SimpleDb;

View File

@@ -0,0 +1,76 @@
// 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::{Path, PathBuf};
use crate::core::error::*;
use crate::core::object_id::ObjectId;
const OBJECTS_FOLDER: &str = "objects";
#[derive(Debug)]
pub struct SimpleDb {
path: PathBuf,
}
impl SimpleDb {
pub fn new(path: &Path) -> Result<SimpleDb> {
Ok(SimpleDb {
path: path.into(),
})
}
pub fn has_object(&self, oid: &ObjectId) -> bool {
let obj_path = self.path_from_id(oid);
if let Ok(metadata) = std::fs::metadata(obj_path) {
metadata.is_file()
}
else {
false
}
}
fn path_from_id(&self, oid: &ObjectId) -> PathBuf {
let oid_str = oid.to_string();
let mut path = self.path.clone();
path.push(OBJECTS_FOLDER);
path.push(oid_str.get(..4).unwrap()); // unwrap is ok here because we know oid_str is a
path.push(oid_str.get(4..).unwrap()); // hexadecimal number (i.e. ascii only).
path
}
}
#[cfg(test)]
mod tests {
use super::ObjectId;
use super::SimpleDb;
#[test]
fn simple_db_path_from_id() {
let db = SimpleDb::new(std::path::Path::new(".bsv")).unwrap();
let oid = ObjectId::from_str("0001020304050607").unwrap();
let path = db.path_from_id(&oid);
assert_eq!(path, std::path::PathBuf::from(".bsv/objects/0001/020304050607"));
}
}

40
libbsv/tests/simple_db.rs Normal file
View File

@@ -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/>.
extern crate tempfile;
extern crate libbsv;
#[test]
fn simple_db_has_object() {
let temp_dir = tempfile::TempDir::new().unwrap();
let db = libbsv::SimpleDb::new(temp_dir.path()).unwrap();
let oid = libbsv::ObjectId::from_str("0001020304050607").unwrap();
assert!(!db.has_object(&oid));
let mut object_path: std::path::PathBuf = temp_dir.path().into();
object_path.push("objects");
object_path.push("0001");
std::fs::create_dir_all(&object_path).unwrap();
object_path.push("020304050607");
std::fs::write(object_path, "test").unwrap();
assert!(db.has_object(&oid));
}