This commit is contained in:
2021-05-25 23:17:38 +02:00
parent 82ea603ba4
commit 468f81b86c
13 changed files with 275 additions and 82 deletions

View File

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

View File

@@ -52,5 +52,15 @@ error_chain! {
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)
}
}
}

View File

@@ -21,8 +21,8 @@ pub mod object;
pub mod repository;
pub const NAME: &'static str = env!("CARGO_PKG_NAME");
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub const NAME: &str = env!("CARGO_PKG_NAME");
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub use error::{Error, Result, ErrorKind};

View File

@@ -14,7 +14,162 @@
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
use std::path::{Path};
use std::fs::Metadata;
use std::os::unix::fs::MetadataExt;
use crate::core::error::*;
use crate::{ObjectId};
pub type ObjectType = [u8; 4];
pub const OTYPE_BLOB: &ObjectType = b"blob";
pub const OTYPE_TREE: &ObjectType = b"tree";
pub fn object_type_from_metadata(md: &Metadata) -> Result<ObjectType> {
if md.is_file() {
Ok(*OTYPE_BLOB)
}
else if md.is_dir() {
Ok(*OTYPE_TREE)
}
else {
Err(ErrorKind::UnsupportedFileType.into())
}
}
enum PermissionsFlag {
Read = 0x04,
Write = 0x02,
Execute = 0x01,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Permissions {
flags: u8,
}
impl Default for Permissions {
fn default() -> Permissions {
Permissions { flags: 0 }
}
}
impl Permissions {
pub fn read_only() -> Permissions {
*Self::default().set_read(true)
}
pub fn read_write() -> Permissions {
*Self::read_only().set_write(true)
}
pub fn read_execute() -> Permissions {
*Self::read_only().set_execute(true)
}
pub fn read_write_execute() -> Permissions {
*Self::read_write().set_execute(true)
}
pub fn from_unix_mode(mode: u32) -> Permissions {
*Self::default()
.set_read(mode & 0o400 != 0)
.set_write(mode & 0o200 != 0)
.set_execute(mode & 0o100 != 0)
}
pub fn is_read(&self) -> bool {
self.test_flag(PermissionsFlag::Read)
}
pub fn is_write(&self) -> bool {
self.test_flag(PermissionsFlag::Write)
}
pub fn is_execute(&self) -> bool {
self.test_flag(PermissionsFlag::Execute)
}
pub fn set_read(&mut self, read: bool) -> &mut Self {
self.set_flag(PermissionsFlag::Read, read)
}
pub fn set_write(&mut self, write: bool) -> &mut Self {
self.set_flag(PermissionsFlag::Write, write)
}
pub fn set_execute(&mut self, execute: bool) -> &mut Self {
self.set_flag(PermissionsFlag::Execute, execute)
}
fn test_flag(&self, flag: PermissionsFlag) -> bool {
(self.flags & flag as u8) != 0
}
fn set_flag(&mut self, flag: PermissionsFlag, value: bool) -> &mut Self {
if value { self.flags |= flag as u8; }
else { self.flags &= !(flag as u8); }
self
}
}
pub struct TreeItem {
otype: ObjectType,
oid: ObjectId,
modification_time: i64,
permissions: Permissions,
name: String,
}
impl TreeItem {
pub fn new(otype: &ObjectType, oid: &ObjectId, name: &str) -> TreeItem {
TreeItem {
otype: *otype,
oid: oid.clone(),
modification_time: 0,
permissions: Permissions::read_write_execute(),
name: name.into(),
}
}
pub fn from_file_path_and_id(file_path: &Path, oid: &ObjectId) -> Result<TreeItem> {
let md = file_path.symlink_metadata()?;
Ok(TreeItem {
otype: object_type_from_metadata(&md)?,
oid: oid.clone(),
modification_time: md.mtime(),
permissions: Permissions::from_unix_mode(md.mode()),
name: file_path.file_name()
.and_then(|n| n.to_str())
.ok_or_else(|| ErrorKind::InvalidPath(file_path.into()))?
.into(),
})
}
pub fn object_type(&self) -> ObjectType {
self.otype
}
pub fn object_id(&self) -> &ObjectId {
&self.oid
}
pub fn modification_time(&self) -> i64 {
self.modification_time
}
pub fn permissions(&self) -> &Permissions {
&self.permissions
}
pub fn name(&self) -> &str {
&self.name
}
}
pub type TreeObject = Vec<TreeItem>;

View File

@@ -15,13 +15,14 @@
use std::fmt;
use std::str::{FromStr};
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
/// 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 {
@@ -35,8 +36,13 @@ impl ObjectId {
id: id.into(),
}
}
}
pub fn from_str(id_str: &str) -> Result<ObjectId> {
impl FromStr for ObjectId {
type Err = Error;
fn from_str(id_str: &str) -> Result<ObjectId> {
if id_str.len() % 2 != 0 {
return Err(ErrorKind::InvalidObjectIdSize.into());
}
@@ -78,6 +84,8 @@ impl fmt::Debug for ObjectId {
#[cfg(test)]
mod tests {
use std::str::{FromStr};
use super::ObjectId;
#[test]

View File

@@ -26,7 +26,7 @@ use super::config::{Config, RepositoryConfig};
use crate::simple_db::SimpleDb;
const CONFIG_FILENAME: &'static str = ".bsv";
const CONFIG_FILENAME: &str = ".bsv";
#[derive(Debug)]
@@ -42,7 +42,7 @@ impl Repository {
bail!(ErrorKind::NonEmptyDirectory(path.into()))
}
if device_name.len() == 0 {
if device_name.is_empty() {
bail!("Device name must not be empty.")
}
@@ -74,7 +74,7 @@ impl Repository {
Ok(Repository {
config: Rc::clone(&config),
db: SimpleDb::new(path.into())?,
db: SimpleDb::new(path)?,
})
}
}

View File

@@ -16,6 +16,7 @@
#[macro_use]
extern crate error_chain;
extern crate serde;
pub mod core;

View File

@@ -129,6 +129,8 @@ impl SimpleDb {
#[cfg(test)]
mod tests {
use std::str::{FromStr};
use super::ObjectId;
use super::SimpleDb;

View File

@@ -14,11 +14,11 @@
// along with cdb. If not, see <https://www.gnu.org/licenses/>.
pub mod simple_db;
pub mod db;
pub mod object;
pub use simple_db::SimpleDb;
pub use db::SimpleDb;
pub use object::{
ObjectReader,
};

View File

@@ -185,6 +185,8 @@ impl<T: Read + Seek> WriteAsObject for T {
#[cfg(test)]
mod tests {
use std::str::{FromStr};
use crate::core::OTYPE_BLOB;
use super::*;

View File

@@ -18,6 +18,7 @@ extern crate tempfile;
extern crate libbsv;
use std::str::{FromStr};
use std::path::{PathBuf};
use std::io::{Cursor, Read};
use std::fs::{create_dir_all, write};