Add object reader & writer.
This commit is contained in:
61
Cargo.lock
generated
61
Cargo.lock
generated
@@ -61,6 +61,15 @@ version = "1.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bsv"
|
name = "bsv"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -92,6 +101,21 @@ dependencies = [
|
|||||||
"vec_map",
|
"vec_map",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpuid-bool"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "error-chain"
|
name = "error-chain"
|
||||||
version = "0.12.4"
|
version = "0.12.4"
|
||||||
@@ -102,6 +126,16 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.1.15"
|
version = "0.1.15"
|
||||||
@@ -143,8 +177,10 @@ dependencies = [
|
|||||||
name = "libbsv"
|
name = "libbsv"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"digest",
|
||||||
"error-chain",
|
"error-chain",
|
||||||
"serde",
|
"serde",
|
||||||
|
"sha2",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"toml",
|
"toml",
|
||||||
"uuid",
|
"uuid",
|
||||||
@@ -178,6 +214,12 @@ version = "0.20.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
|
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opaque-debug"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
@@ -284,6 +326,19 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"cfg-if",
|
||||||
|
"cpuid-bool",
|
||||||
|
"digest",
|
||||||
|
"opaque-debug",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -333,6 +388,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
|
|||||||
23
bsv/src_bak/core/error.rs
Normal file
23
bsv/src_bak/core/error.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use std::result;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum Error {
|
||||||
|
InvalidObjectIdSize,
|
||||||
|
InvalidObjectIdCharacter,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
fn message(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Error::InvalidObjectIdSize =>
|
||||||
|
"invalid size for object id string".to_string(),
|
||||||
|
Error::InvalidObjectIdCharacter =>
|
||||||
|
"invalid character in object id string".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
5
bsv/src_bak/core/mod.rs
Normal file
5
bsv/src_bak/core/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod error;
|
||||||
|
pub mod object_id;
|
||||||
|
pub mod object_type;
|
||||||
|
pub mod object;
|
||||||
|
pub mod object_db;
|
||||||
21
bsv/src_bak/core/object.rs
Normal file
21
bsv/src_bak/core/object.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
use super::object_id::ObjectId;
|
||||||
|
use super::object_type::ObjectType;
|
||||||
|
|
||||||
|
|
||||||
|
/// An object in an ObjectDb.
|
||||||
|
///
|
||||||
|
/// An Objects is fundamentaly an arbitrary blob of data. It has a unique identifier, a size and
|
||||||
|
/// a type.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Object {
|
||||||
|
pub id: ObjectId,
|
||||||
|
pub otype: ObjectType,
|
||||||
|
pub size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Object {
|
||||||
|
fn new(object_id: ObjectId, otype: ObjectType, size: usize) -> Object {
|
||||||
|
Object { id: object_id, otype, size }
|
||||||
|
}
|
||||||
|
}
|
||||||
17
bsv/src_bak/core/object_db.rs
Normal file
17
bsv/src_bak/core/object_db.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use std::io;
|
||||||
|
|
||||||
|
use super::error::Result;
|
||||||
|
use super::object_id::ObjectId;
|
||||||
|
use super::object_type::ObjectType;
|
||||||
|
use super::object::Object;
|
||||||
|
|
||||||
|
|
||||||
|
pub trait ObjectDb {
|
||||||
|
fn object_exists(&self, oid: &ObjectId) -> Result<bool>;
|
||||||
|
fn get_object(&self, oid: &ObjectId) -> Result<Option<Object>>;
|
||||||
|
fn open_object(&self, oid: &ObjectId) -> Result<Box<dyn io::Read>>;
|
||||||
|
fn compute_object_id(&self, otype: ObjectType, reader: &dyn io::Read) -> Result<ObjectId>;
|
||||||
|
|
||||||
|
fn add_object(&mut self, otype: ObjectType, reader: &dyn io::Read) -> Result<Object>;
|
||||||
|
fn delete_object(&mut self, oid: &ObjectId) -> Result<()>;
|
||||||
|
}
|
||||||
16
bsv/src_bak/core/object_type.rs
Normal file
16
bsv/src_bak/core/object_type.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
|
||||||
|
/// Describe the type of an Object.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct ObjectType {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ObjectType {
|
||||||
|
fn new(name: &str) -> ObjectType {
|
||||||
|
ObjectType {
|
||||||
|
name: name.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
bsv/src_bak/main.rs
Normal file
7
bsv/src_bak/main.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
mod core;
|
||||||
|
mod object_db;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
@@ -11,3 +11,6 @@ serde = "1.0.106"
|
|||||||
toml = "0.5.6"
|
toml = "0.5.6"
|
||||||
uuid = { version = "0.8.1", features = ["serde", "v4"] }
|
uuid = { version = "0.8.1", features = ["serde", "v4"] }
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
digest = "0.9.0"
|
||||||
|
sha2 = "0.9.1"
|
||||||
|
flate2 = "1.0.17"
|
||||||
|
|||||||
@@ -42,5 +42,10 @@ error_chain! {
|
|||||||
description("Object id contains invalid character")
|
description("Object id contains invalid character")
|
||||||
display("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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ pub struct ObjectId {
|
|||||||
|
|
||||||
|
|
||||||
impl ObjectId {
|
impl ObjectId {
|
||||||
pub fn new(id: Vec<u8>) -> ObjectId {
|
pub fn new(id: &[u8]) -> ObjectId {
|
||||||
ObjectId {
|
ObjectId {
|
||||||
id
|
id: id.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn object_id_display() {
|
fn object_id_display() {
|
||||||
let obj = ObjectId::new(vec![
|
let obj = ObjectId::new(&[
|
||||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn object_id_debug() {
|
fn object_id_debug() {
|
||||||
let obj = ObjectId::new(vec![
|
let obj = ObjectId::new(&[
|
||||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn str_to_object_id() {
|
fn str_to_object_id() {
|
||||||
let obj = ObjectId::from_str("0001020304050607").unwrap();
|
let obj = ObjectId::from_str("0001020304050607").unwrap();
|
||||||
let obj_ref = ObjectId::new(vec![
|
let obj_ref = ObjectId::new(&[
|
||||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -14,63 +14,8 @@
|
|||||||
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
pub mod simple_db;
|
||||||
|
pub mod object;
|
||||||
use crate::core::error::*;
|
|
||||||
use crate::core::object_id::ObjectId;
|
|
||||||
|
|
||||||
|
|
||||||
const OBJECTS_FOLDER: &str = "objects";
|
pub use simple_db::SimpleDb;
|
||||||
|
|
||||||
|
|
||||||
#[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"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
161
libbsv/src/simple_db/object.rs
Normal file
161
libbsv/src/simple_db/object.rs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
// 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 digest;
|
||||||
|
extern crate sha2;
|
||||||
|
extern crate flate2;
|
||||||
|
|
||||||
|
use std::io::{Read, Write, copy};
|
||||||
|
|
||||||
|
use digest::Digest;
|
||||||
|
|
||||||
|
use crate::core::error::*;
|
||||||
|
use crate::core::ObjectId;
|
||||||
|
|
||||||
|
|
||||||
|
type ObjectType = [u8; 4];
|
||||||
|
|
||||||
|
pub const TYPE_BLOB: &ObjectType = b"blob";
|
||||||
|
pub const TYPE_TREE: &ObjectType = b"tree";
|
||||||
|
|
||||||
|
|
||||||
|
pub struct ObjectWriter<'a, W: Write, D: Digest> {
|
||||||
|
writer: &'a mut W,
|
||||||
|
digest: D,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'a, W: Write, D: Digest> ObjectWriter<'a, W, D> {
|
||||||
|
pub fn new(writer: &'a mut W, otype: &ObjectType, size: u64)
|
||||||
|
-> Result<ObjectWriter<'a, W, D>> {
|
||||||
|
|
||||||
|
let mut digest = D::new();
|
||||||
|
|
||||||
|
digest.update(otype);
|
||||||
|
digest.update(&size.to_be_bytes());
|
||||||
|
|
||||||
|
writer.write_all(otype)?;
|
||||||
|
writer.write_all(&size.to_be_bytes())?;
|
||||||
|
|
||||||
|
Ok(ObjectWriter {
|
||||||
|
writer,
|
||||||
|
digest,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(self) -> Result<ObjectId> {
|
||||||
|
Ok(ObjectId::new(&self.digest.finalize()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'a, W: Write, D: Digest> Write for ObjectWriter<'a, W, D> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
let size = self.writer.write(buf)?;
|
||||||
|
self.digest.update(&buf[..size]);
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
self.writer.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn write_object<'a, R, W>(reader: &mut R, writer: &'a mut W,
|
||||||
|
otype: &ObjectType, size: u64)
|
||||||
|
-> Result<ObjectId> where
|
||||||
|
R: Read,
|
||||||
|
W: Write {
|
||||||
|
let mut owriter: ObjectWriter<W, sha2::Sha224>
|
||||||
|
= ObjectWriter::new(writer, otype, size)?;
|
||||||
|
|
||||||
|
copy(reader, &mut owriter)?;
|
||||||
|
|
||||||
|
owriter.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct ObjectReader<R: Read> {
|
||||||
|
reader: R,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<R: Read> ObjectReader<R> {
|
||||||
|
pub fn new(mut reader: R)
|
||||||
|
-> Result<(ObjectType, u64, ObjectReader<R>)> {
|
||||||
|
|
||||||
|
let mut buffer = [0u8; 12];
|
||||||
|
reader.read_exact(&mut buffer)?;
|
||||||
|
|
||||||
|
let otype = {
|
||||||
|
let mut otype = [0; 4];
|
||||||
|
otype.copy_from_slice(&buffer[0..4]);
|
||||||
|
otype
|
||||||
|
};
|
||||||
|
let size = {
|
||||||
|
let mut size_bytes = [0; 8];
|
||||||
|
size_bytes.copy_from_slice(&buffer[4..12]);
|
||||||
|
u64::from_be_bytes(size_bytes)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
otype,
|
||||||
|
size,
|
||||||
|
ObjectReader {
|
||||||
|
reader,
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<R: Read> Read for ObjectReader<R> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
self.reader.read(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn object_read_write() {
|
||||||
|
use std::io::{Cursor, Seek, SeekFrom};
|
||||||
|
|
||||||
|
let payload = b"Hello World!";
|
||||||
|
let mut source = Cursor::new(payload);
|
||||||
|
let mut fake_file = Cursor::new(vec![]);
|
||||||
|
let mut output = vec![];
|
||||||
|
|
||||||
|
write_object(&mut source, &mut fake_file, TYPE_BLOB, payload.len() as u64).unwrap();
|
||||||
|
fake_file.seek(SeekFrom::Start(0)).unwrap();
|
||||||
|
|
||||||
|
let (otype, size, mut reader) = ObjectReader::new(fake_file).unwrap();
|
||||||
|
let read_size = reader.read_to_end(&mut output).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(otype, *TYPE_BLOB);
|
||||||
|
assert_eq!(size, payload.len() as u64);
|
||||||
|
assert_eq!(read_size, payload.len());
|
||||||
|
assert_eq!(output, payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
76
libbsv/src/simple_db/simple_db.rs
Normal file
76
libbsv/src/simple_db/simple_db.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::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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user