Restructure project and some progress.
This commit is contained in:
2
libbsv/.gitignore
vendored
Normal file
2
libbsv/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
13
libbsv/Cargo.toml
Normal file
13
libbsv/Cargo.toml
Normal 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
34
libbsv/src/core/config.rs
Normal 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
46
libbsv/src/core/error.rs
Normal 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
30
libbsv/src/core/mod.rs
Normal 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;
|
||||
110
libbsv/src/core/object_id.rs
Normal file
110
libbsv/src/core/object_id.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
80
libbsv/src/core/repository.rs
Normal file
80
libbsv/src/core/repository.rs
Normal 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
32
libbsv/src/lib.rs
Normal 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;
|
||||
76
libbsv/src/simple_db/mod.rs
Normal file
76
libbsv/src/simple_db/mod.rs
Normal 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
40
libbsv/tests/simple_db.rs
Normal 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));
|
||||
}
|
||||
Reference in New Issue
Block a user