Browse Source

Parking bsvfs WIP experiment.

deprecated_bsvfs
Simon Boyé 2 years ago
parent
commit
5a94fdd4a9
  1. 277
      Cargo.lock
  2. 1
      Cargo.toml
  3. 5
      bsvfs/Cargo.toml
  4. 491
      bsvfs/src/fs.rs
  5. 386
      bsvfs/src/fs_impl.rs
  6. 32
      bsvfs/src/lib.rs
  7. 468
      bsvfs/src/mem_fs.rs
  8. 1
      libbsv/Cargo.toml
  9. 5
      libbsv/src/lib.rs
  10. 23
      libbsv/src/permissions.rs
  11. 1
      libbsv/src/repository.rs
  12. 14
      libbsv/src/tree_item.rs
  13. 25
      libbsv/src/tree_walker.rs

277
Cargo.lock

@ -4,18 +4,18 @@ version = 3
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.18" version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
@ -26,6 +26,15 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "bsvfs"
version = "0.1.0"
dependencies = [
"camino",
"tempfile",
"thiserror",
]
[[package]] [[package]]
name = "camino" name = "camino"
version = "1.0.7" version = "1.0.7"
@ -54,6 +63,12 @@ dependencies = [
"toml", "toml",
] ]
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -62,9 +77,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.1.5" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef" checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -78,31 +93,70 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "fastrand"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
"instant",
]
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.4" version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
dependencies = [ dependencies = [
"typenum", "typenum",
"version_check", "version_check",
] ]
[[package]] [[package]]
name = "getrandom" name = "instant"
version = "0.2.3" version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
]
[[package]]
name = "io-lifetimes"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3"
dependencies = [
"libc", "libc",
"wasi", "windows-sys 0.45.0",
] ]
[[package]] [[package]]
name = "libbsv" name = "libbsv"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bsvfs",
"camino", "camino",
"cas-core", "cas-core",
"cas-simple", "cas-simple",
@ -112,9 +166,15 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.98" version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]] [[package]]
name = "memchr" name = "memchr"
@ -128,12 +188,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.27" version = "1.0.27"
@ -152,60 +206,20 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.9" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.6.0" version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -214,30 +228,35 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.6.27" version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]] [[package]]
name = "remove_dir_all" name = "rustix"
version = "0.5.3" version = "0.36.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
dependencies = [ dependencies = [
"winapi", "bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys 0.45.0",
] ]
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.127" version = "1.0.155"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" checksum = "71f2b4817415c6d4210bfe1c7bfcf4801b2d904cb4d0e1a8fdb651013c9e86b8"
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.9.5" version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [ dependencies = [
"block-buffer", "block-buffer",
"cfg-if", "cfg-if",
@ -259,16 +278,15 @@ dependencies = [
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.2.0" version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "fastrand",
"rand",
"redox_syscall", "redox_syscall",
"remove_dir_all", "rustix",
"winapi", "windows-sys 0.42.0",
] ]
[[package]] [[package]]
@ -293,18 +311,18 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.5.8" version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.13.0" version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
@ -314,15 +332,9 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.3" version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]] [[package]]
name = "winapi" name = "winapi"
@ -345,3 +357,84 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
[[package]]
name = "windows_i686_gnu"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
[[package]]
name = "windows_i686_msvc"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"

1
Cargo.toml

@ -1,6 +1,7 @@
[workspace] [workspace]
members = [ members = [
"bsvfs",
"cas-core", "cas-core",
"cas-simple", "cas-simple",
"libbsv", "libbsv",

5
bsvfs/Cargo.toml

@ -1,12 +1,13 @@
[package] [package]
name = "cas-core" name = "bsvfs"
version = "0.1.0" version = "0.1.0"
authors = ["Simon Boyé <sim.boye@gmail.com>"] authors = ["Simon Boyé <sim.boye@gmail.com>"]
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
[dependencies] [dependencies]
thiserror = "1.0.25"
camino = { version = "1.0.7" } camino = { version = "1.0.7" }
thiserror = "1.0.25"
[dev-dependencies] [dev-dependencies]
tempfile = "3.2.0"

491
bsvfs/src/fs.rs

@ -0,0 +1,491 @@
// 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.
//
// bsv 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 bsv. If not, see <https://www.gnu.org/licenses/>.
use std::{
io::{Read, Result, Seek, Write},
time::SystemTime,
};
use camino::{Utf8Path, Utf8PathBuf};
// FileType
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FileType {
Dir,
File,
Symlink,
Other,
}
impl FileType {
pub fn is_dir(&self) -> bool {
*self == FileType::Dir
}
pub fn is_file(&self) -> bool {
*self == FileType::File
}
pub fn is_symlink(&self) -> bool {
*self == FileType::Symlink
}
}
// Permissions
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Permissions {
read: bool,
write: bool,
execute: bool,
}
impl Permissions {
pub fn new(read: bool, write: bool, execute: bool) -> Self {
Permissions { read, write, execute }
}
pub fn is_read(&self) -> bool {
self.read
}
pub fn is_write(&self) -> bool {
self.write
}
pub fn is_execute(&self) -> bool {
self.execute
}
}
impl std::fmt::Display for Permissions {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
use std::fmt::Write;
f.write_char(if self.read { 'r' } else { '-' })?;
f.write_char(if self.write { 'w' } else { '-' })?;
f.write_char(if self.execute { 'x' } else { '-' })?;
Ok(())
}
}
impl std::str::FromStr for Permissions {
type Err = ParsePermissionError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
if s.len() == 3 {
let mut chars = s.chars();
let read = parse_permission_char(&mut chars, 'r')?;
let write = parse_permission_char(&mut chars, 'w')?;
let execute = parse_permission_char(&mut chars, 'x')?;
assert!(chars.next().is_none());
Ok(Self { read, write, execute })
}
else {
Err(ParsePermissionError::InvalidLength(s.len()))
}
}
}
fn parse_permission_char(chars: &mut std::str::Chars, bit_char: char)
-> std::result::Result<bool, ParsePermissionError>
{
let c = chars.next().unwrap();
if c == bit_char || c == '-' {
Ok(c == bit_char)
}
else {
Err(ParsePermissionError::InvalidChar(bit_char, c))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ParsePermissionError {
InvalidLength(usize),
InvalidChar(char, char),
}
impl std::fmt::Display for ParsePermissionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidLength(length) => write!(f, "expected 3 characters, got {}", length),
Self::InvalidChar(expected, actual) => write!(f, "exected character {}, got {}", expected, actual),
}
}
}
impl std::error::Error for ParsePermissionError {
}
// Metadata
pub trait Metadata {
fn file_type(&self) -> FileType;
fn is_dir(&self) -> bool {
self.file_type().is_dir()
}
fn is_file(&self) -> bool {
self.file_type().is_file()
}
fn is_symlink(&self) -> bool {
self.file_type().is_symlink()
}
fn len(&self) -> u64;
fn permissions(&self) -> Permissions;
fn modified(&self) -> Result<SystemTime>;
fn created(&self) -> Result<SystemTime>;
}
// File
const OPEN_READ: u8 = 0x01;
const OPEN_WRITE: u8 = 0x02;
const OPEN_APPEND: u8 = 0x04;
const OPEN_TRUNCATE: u8 = 0x08;
const OPEN_CREATE: u8 = 0x10;
const OPEN_CREATE_NEW: u8 = 0x20;
#[derive(Debug, PartialEq, Eq)]
pub struct OpenOptions {
options: u8,
}
impl OpenOptions {
pub fn new() -> Self {
OpenOptions {
options: 0,
}
}
pub fn is_read(&self) -> bool {
(self.options & OPEN_READ) != 0
}
pub fn is_write(&self) -> bool {
(self.options & OPEN_WRITE) != 0
}
pub fn is_append(&self) -> bool {
(self.options & OPEN_APPEND) != 0
}
pub fn is_truncate(&self) -> bool {
(self.options & OPEN_TRUNCATE) != 0
}
pub fn is_create(&self) -> bool {
(self.options & OPEN_CREATE) != 0
}
pub fn is_create_new(&self) -> bool {
(self.options & OPEN_CREATE_NEW) != 0
}
pub fn read(&mut self, read: bool) -> &mut Self {
if read {
self.options |= OPEN_READ;
} else {
self.options &= !OPEN_READ;
}
self
}
pub fn write(&mut self, write: bool) -> &mut Self {
if write {
self.options |= OPEN_WRITE;
} else {
self.options &= !OPEN_WRITE;
}
self
}
pub fn append(&mut self, append: bool) -> &mut Self {
if append {
self.options |= OPEN_APPEND;
} else {
self.options &= !OPEN_APPEND;
}
self
}
pub fn truncate(&mut self, truncate: bool) -> &mut Self {
if truncate {
self.options |= OPEN_TRUNCATE;
} else {
self.options &= !OPEN_TRUNCATE;
}
self
}
pub fn create(&mut self, create: bool) -> &mut Self {
if create {
self.options |= OPEN_CREATE;
} else {
self.options &= !OPEN_CREATE;
}
self
}
pub fn create_new(&mut self, create_new: bool) -> &mut Self {
if create_new {
self.options |= OPEN_CREATE_NEW;
} else {
self.options &= !OPEN_CREATE_NEW;
}
self
}
}
pub trait File: Sized + Read + Write + Seek {
type Metadata: Metadata;
fn metadata(&self) -> Result<Self::Metadata>;
fn set_len(&self, size: u64) -> Result<()>;
fn sync_data(&self) -> Result<()>;
fn sync_all(&self) -> Result<()>;
}
// DirEntry
pub trait DirEntry {
type Metadata: Metadata;
fn path(&self) -> Result<Utf8PathBuf>;
fn metadata(&self) -> Result<Self::Metadata>;
fn file_type(&self) -> Result<FileType> {
Ok(self.metadata()?.file_type())
}
fn file_name(&self) -> Result<String> {
Ok(
self.path()?
.file_name()
.expect("DirEntry::path() returned an invalid path with no file name")
.to_string()
)
}
}
// FileSystem
pub trait FileSystem {
type File: File;
type DirEntry: DirEntry;
type ReadDir: Iterator<Item=Result<Self::DirEntry>>;
fn open_with_options<P: AsRef<Utf8Path>>(&self, path: P, options: &OpenOptions) -> Result<Self::File>;
fn open<P: AsRef<Utf8Path>>(&self, path: P) -> Result<Self::File> {
self.open_with_options(
path,
OpenOptions::new()
.read(true)
)
}
fn create_or_truncate<P: AsRef<Utf8Path>>(&self, path: P) -> Result<Self::File> {
self.open_with_options(
path,
OpenOptions::new()
.write(true)
.create(true)
)
}
fn create_new<P: AsRef<Utf8Path>>(&self, path: P) -> Result<Self::File> {
self.open_with_options(
path,
OpenOptions::new()
.write(true)
.create_new(true)
)
}
fn read_dir<P: AsRef<Utf8Path>>(&self, path: P) -> Result<Self::ReadDir>;
}
#[cfg(test)]
mod test {
use std::str::FromStr;
use super::*;
#[test]
fn test_file_type() {
let dir = FileType::Dir;
assert!(dir.is_dir());
assert!(!dir.is_file());
assert!(!dir.is_symlink());
let file = FileType::File;
assert!(!file.is_dir());
assert!(file.is_file());
assert!(!file.is_symlink());
let symlink = FileType::Symlink;
assert!(!symlink.is_dir());
assert!(!symlink.is_file());
assert!(symlink.is_symlink());
}
#[test]
fn test_open_options() {
let mut options = OpenOptions::new();
assert!(!options.is_read());
assert!(!options.is_write());
assert!(!options.is_append());
assert!(!options.is_truncate());
assert!(!options.is_create());
assert!(!options.is_create_new());
options.read(true);
assert!(options.is_read());
assert!(!options.is_write());
assert!(!options.is_append());
assert!(!options.is_truncate());
assert!(!options.is_create());
assert!(!options.is_create_new());
options.read(false);
assert!(!options.is_read());
assert!(!options.is_write());
assert!(!options.is_append());
assert!(!options.is_truncate());
assert!(!options.is_create());
assert!(!options.is_create_new());
options.write(true);
assert!(!options.is_read());
assert!(options.is_write());
assert!(!options.is_append());
assert!(!options.is_truncate());
assert!(!options.is_create());
assert!(!options.is_create_new());
options.write(false);
assert!(!options.is_read());
assert!(!options.is_write());
assert!(!options.is_append());
assert!(!options.is_truncate());
assert!(!options.is_create());
assert!(!options.is_create_new());
options.append(true);
assert!(!options.is_read());
assert!(!options.is_write());
assert!(options.is_append());
assert!(!options.is_truncate());
assert!(!options.is_create());
assert!(!options.is_create_new());
options.append(false);
assert!(!options.is_read());
assert!(!options.is_write());
assert!(!options.is_append());
assert!(!options.is_truncate());
assert!(!options.is_create());
assert!(!options.is_create_new());
options.truncate(true);
assert!(!options.is_read());
assert!(!options.is_write());
assert!(!options.is_append());
assert!(options.is_truncate());
assert!(!options.is_create());
assert!(!options.is_create_new());
options.truncate(false);
assert!(!options.is_read());
assert!(!options.is_write());
assert!(!options.is_append());
assert!(!options.is_truncate());
assert!(!options.is_create());
assert!(!options.is_create_new());
options.create(true);
assert!(!options.is_read());
assert!(!options.is_write());
assert!(!options.is_append());
assert!(!options.is_truncate());
assert!(options.is_create());
assert!(!options.is_create_new());
options.create(false);
assert!(!options.is_read());
assert!(!options.is_write());
assert!(!options.is_append());
assert!(!options.is_truncate());
assert!(!options.is_create());
assert!(!options.is_create_new());
options.create_new(true);
assert!(!options.is_read());
assert!(!options.is_write());
assert!(!options.is_append());
assert!(!options.is_truncate());
assert!(!options.is_create());
assert!(options.is_create_new());
options.create_new(false);
assert!(!options.is_read());
assert!(!options.is_write());
assert!(!options.is_append());
assert!(!options.is_truncate());
assert!(!options.is_create());
assert!(!options.is_create_new());
}
#[test]
fn test_permission_display() {
let expected = [
"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx",
];
for i in 0..8 {
let read = (i & 0x04) != 0;
let write = (i & 0x02) != 0;
let execute = (i & 0x01) != 0;
let perm = Permissions { read, write, execute };
let perm_str = perm.to_string();
assert_eq!(perm_str, expected[i]);
}
}
#[test]
fn test_permission_from_str() {
let perms = [
"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx",
];
for i in 0..8 {
let read = (i & 0x04) != 0;
let write = (i & 0x02) != 0;
let execute = (i & 0x01) != 0;
let perm = Permissions::from_str(perms[i]).unwrap();
let expected = Permissions { read, write, execute };
assert_eq!(perm, expected);
}
assert_eq!(Permissions::from_str("rw"), Err(ParsePermissionError::InvalidLength(2)));
assert_eq!(Permissions::from_str("rw--"), Err(ParsePermissionError::InvalidLength(4)));
assert_eq!(Permissions::from_str("-x-"), Err(ParsePermissionError::InvalidChar('w', 'x')));
assert_eq!(Permissions::from_str("123"), Err(ParsePermissionError::InvalidChar('r', '1')));
}
}

386
bsvfs/src/fs_impl.rs

@ -0,0 +1,386 @@
// 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.
//
// bsv 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 bsv. If not, see <https://www.gnu.org/licenses/>.
use std::{
error::Error,
ffi::OsString,
fmt::{Arguments, Display},
fs::{
self,
DirEntry as StdDirEntry,
File as StdFile,
Metadata as StdMetadata,
ReadDir as StdReadDir,
},
io::{self, IoSlice, IoSliceMut, Read, Result, Seek, SeekFrom, Write},
time::SystemTime,
};
use camino::{Utf8Path, Utf8PathBuf};
use crate::{
fs::*,
};
#[derive(Clone, Debug)]
struct NonUtf8Filename {
filename: OsString,
}
impl NonUtf8Filename {
fn new(filename: OsString) -> Self {
Self {
filename: filename,
}
}
}
impl Display for NonUtf8Filename {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Non UTF-8 filename: '{:?}'", self.filename)
}
}
impl Error for NonUtf8Filename {
}
fn as_io_error<E>(error: E) -> io::Error
where E: Into<Box<dyn Error + Send + Sync>>,
{
io::Error::new(io::ErrorKind::Other, error)
}
fn as_string(os_string: OsString) -> Result<String> {
os_string.into_string()
.map_err(|filename| {
io::Error::new(
io::ErrorKind::Other,
NonUtf8Filename::new(filename),
)
})
}
fn as_filetype(std_filetype: fs::FileType) -> FileType {
if std_filetype.is_dir() {
FileType::Dir
} else if std_filetype.is_file() {
FileType::File
} else if std_filetype.is_symlink() {
FileType::Symlink
} else {
FileType::Other
}
}
pub struct SysMetadata {
metadata: StdMetadata,
}
impl Metadata for SysMetadata {
fn file_type(&self) -> FileType {
as_filetype(self.metadata.file_type())
}
fn len(&self) -> u64 {
self.metadata.len()
}
#[cfg(not(unix))]
fn permissions(&self) -> Permissions {
Permissions(true, self.metadata.permissions().readonly(), false)
}
#[cfg(unix)]
fn permissions(&self) -> Permissions {
use std::os::unix::fs::MetadataExt;
let mode = self.metadata.mode();
Permissions::new(
mode & 0o100 != 0,
mode & 0o200 != 0,
mode & 0o400 != 0,
)
}
fn modified(&self) -> Result<SystemTime> {
Ok(self.metadata.modified()?)
}
fn created(&self) -> Result<SystemTime> {
Ok(self.metadata.created()?)
}
}
impl From<StdMetadata> for SysMetadata {
fn from(value: StdMetadata) -> Self {
SysMetadata {
metadata: value
}
}
}
pub struct SysFile {
file: StdFile,
}
impl File for SysFile {
type Metadata = SysMetadata;
fn metadata(&self) -> Result<Self::Metadata> {
Ok(self.file.metadata()?.into())
}
fn set_len(&self, size: u64) -> Result<()> {
Ok(self.file.set_len(size)?)
}
fn sync_data(&self) -> Result<()> {
Ok(self.file.sync_data()?)
}
fn sync_all(&self) -> Result<()> {
Ok(self.file.sync_all()?)
}
}
impl Read for SysFile {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
self.file.read(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
self.file.read_vectored(bufs)
}
// fn is_read_vectored(&self) -> bool {
// self.file.is_read_vectored()
// }
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
self.file.read_to_end(buf)
}
fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
self.file.read_to_string(buf)
}
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
self.file.read_exact(buf)
}
// fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
// self.file.read_buf(buf)
// }
// fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
// self.file.read_buf_exact(cursor)
// }
}
impl Seek for SysFile {
fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
self.file.seek(pos)
}
fn rewind(&mut self) -> Result<()> {
self.file.rewind()
}
// fn stream_len(&mut self) -> Result<u64> {
// self.file.stream_len()
// }
fn stream_position(&mut self) -> Result<u64> {
self.file.stream_position()
}
}
impl Write for SysFile {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.file.write(buf)
}
fn flush(&mut self) -> Result<()> {
self.file.flush()
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
self.file.write_vectored(bufs)
}
// fn is_write_vectored(&self) -> bool {
// self.file.is_write_vectored()
// }
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
self.file.write_all(buf)
}
// fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> Result<()> {
// self.file.write_all_vectored(bufs)
// }
fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result<()> {
self.file.write_fmt(fmt)
}
}
pub struct SysDirEntry {
dir_entry: StdDirEntry,
}
impl DirEntry for SysDirEntry {
type Metadata = SysMetadata;
fn path(&self) -> Result<Utf8PathBuf> {
Utf8PathBuf::try_from(
self.dir_entry.path()
).map_err(as_io_error)
}
fn metadata(&self) -> Result<Self::Metadata> {
Ok(self.dir_entry.metadata()?.into())
}
fn file_type(&self) -> Result<FileType> {
Ok(as_filetype(self.dir_entry.file_type()?))
}
fn file_name(&self) -> Result<String> {
as_string(self.dir_entry.file_name())
}
}
impl From<StdDirEntry> for SysDirEntry {
fn from(dir_entry: StdDirEntry) -> Self {
SysDirEntry {
dir_entry: dir_entry,
}
}
}
pub struct SysReadDir {
read_dir: StdReadDir,
}
impl Iterator for SysReadDir {
type Item = Result<SysDirEntry>;
fn next(&mut self) -> Option<Self::Item> {
match self.read_dir.next() {
Some(Ok(dir_entry)) => Some(Ok(dir_entry.into())),
Some(Err(error)) => Some(Err(error)),
None => None,
}
}
}
pub struct SysFileSystem;
impl SysFileSystem {
pub fn new() -> Self {
Self{}
}
}
impl FileSystem for SysFileSystem {
type File = SysFile;
type DirEntry = SysDirEntry;
type ReadDir = SysReadDir;
fn open_with_options<P: AsRef<Utf8Path>>(&self, path: P, options: &OpenOptions) -> Result<Self::File> {
Ok(SysFile{
file: StdFile::options()
.read(options.is_read())
.write(options.is_write())
.append(options.is_append())
.truncate(options.is_truncate())
.create(options.is_create())
.create_new(options.is_create_new())
.open(path.as_ref().as_std_path())?,
})
}
fn read_dir<P: AsRef<Utf8Path>>(&self, path: P) -> Result<SysReadDir> {
Ok(SysReadDir {
read_dir: fs::read_dir(path.as_ref().as_std_path())?,
})
}
}
#[cfg(test)]
mod test {
use camino::Utf8PathBuf;
use super::*;
use crate::FileSystem;
#[test]
fn test_file_read_write() {
let dir = tempfile::tempdir().expect("failed to create temp test dir");
let file_path = Utf8PathBuf::from_path_buf(dir.path().join("test.txt")).unwrap();
let message = b"Hello world!\nThis is a test\n";
let fs = SysFileSystem::new();
let mut file = fs.create_new(&file_path).unwrap();
file.write(message).unwrap();
drop(file);
let mut file = fs.open(&file_path).unwrap();
let mut buf = [0u8; 256];
let read_size = file.read(&mut buf).unwrap();
assert_eq!(read_size, message.len());
assert_eq!(&buf[..message.len()], message);
}
#[test]
fn test_read_dir() {
let dir = tempfile::tempdir().expect("failed to create temp test dir");
let dir_path = Utf8Path::from_path(dir.path()).unwrap();
let file0_path = dir_path.join("file.txt");
let file1_path = dir_path.join("zzz.txt");
let dir0_path = dir_path.join("some_dir");
let fs = SysFileSystem::new();
{
let mut file = fs.create_new(file0_path).unwrap();
file.write(b"file.txt").unwrap();
}
{
let mut file = fs.create_new(file1_path).unwrap();
file.write(b"zzz.txt").unwrap();
}
fs::create_dir(dir0_path).unwrap();
let mut entries = fs.read_dir(dir_path).unwrap()
.collect::<Result<Vec<_>>>().unwrap();
entries.sort_by_key(|entry| entry.file_name().unwrap());
let mut it = entries.iter();
let file0_entry = it.next().unwrap();
assert_eq!(file0_entry.file_name().unwrap(), "file.txt");
assert_eq!(file0_entry.file_type().unwrap(), FileType::File);
let file0_metadata = file0_entry.metadata().unwrap();
assert!(file0_metadata.is_file());
assert_eq!(file0_metadata.len(), 8);
let dir0_entry = it.next().unwrap();
assert_eq!(dir0_entry.file_name().unwrap(), "some_dir");
assert_eq!(dir0_entry.file_type().unwrap(), FileType::Dir);
let dir0_metadata = dir0_entry.metadata().unwrap();
assert!(dir0_metadata.is_dir());
let file1_entry = it.next().unwrap();
assert_eq!(file1_entry.file_name().unwrap(), "zzz.txt");
assert_eq!(file1_entry.file_type().unwrap(), FileType::File);
let file1_metadata = file1_entry.metadata().unwrap();
assert!(file1_metadata.is_file());
assert_eq!(file1_metadata.len(), 7);
}
}

32
bsvfs/src/lib.rs

@ -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.
//
// bsv 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 bsv. If not, see <https://www.gnu.org/licenses/>.
extern crate camino;
extern crate thiserror;
#[cfg(test)]
extern crate tempfile;
// mod error;
mod fs;
mod fs_impl;
mod mem_fs;
pub use crate::{
fs::*,
fs_impl::*,
};

468
bsvfs/src/mem_fs.rs

@ -0,0 +1,468 @@
// 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.
//
// bsv 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 bsv. If not, see <https://www.gnu.org/licenses/>.
use std::{
fmt::{Arguments},
io::{Cursor, Error, IoSlice, IoSliceMut, Read, Result, Seek, SeekFrom, Write, ErrorKind},
time::SystemTime, rc::Rc, cell::RefCell,
};
use camino::{Utf8Path, Utf8PathBuf};
use crate::{
fs::*,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MemMetadata {
file_type: FileType,
len: u64,
permissions: Permissions,
modified: SystemTime,
created: SystemTime,
}
impl Metadata for MemMetadata {
fn file_type(&self) -> FileType {
self.file_type
}
fn len(&self) -> u64 {
self.len
}
fn permissions(&self) -> Permissions {
self.permissions.clone()
}
fn modified(&self) -> Result<SystemTime> {
Ok(self.modified)
}
fn created(&self) -> Result<SystemTime> {
Ok(self.created)
}
}
#[derive(Clone, Debug)]
pub struct MemFile {
metadata: MemMetadata,
data: RefCell<Cursor<Vec<u8>>>,
}
impl File for MemFile {
type Metadata = MemMetadata;
// fn open_with_options<P: AsRef<Utf8Path>>(path: P, options: &OpenOptions) -> Result<Self> {
// Ok(SysFile{
// file: StdFile::options()
// .read(options.is_read())
// .write(options.is_write())
// .append(options.is_append())
// .truncate(options.is_truncate())
// .create(options.is_create())
// .create_new(options.is_create_new())
// .open(path.as_ref().as_std_path())?,
// })
// }
fn metadata(&self) -> Result<Self::Metadata> {
Ok(self.metadata.clone())
}
fn set_len(&self, size: u64) -> Result<()> {
let mut data = self.data.borrow_mut();
let pos = data.position();
data.get_mut().resize(size as usize, 0);
if size < pos {
data.seek(SeekFrom::Start(pos));
}
Ok(())
}
fn sync_data(&self) -> Result<()> {
Ok(())
}
fn sync_all(&self) -> Result<()> {
Ok(())
}
}
impl Read for MemFile {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let data = self.data.get_mut();
data.read(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
let data = self.data.get_mut();
data.read_vectored(bufs)
}
// fn is_read_vectored(&self) -> bool {
// self.data.is_read_vectored()
// }
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
let data = self.data.get_mut();
data.read_to_end(buf)
}
fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
let data = self.data.get_mut();
data.read_to_string(buf)
}
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
let data = self.data.get_mut();
data.read_exact(buf)
}
// fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
// self.data.read_buf(buf)
// }
// fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
// self.data.read_buf_exact(cursor)
// }
}
impl Seek for MemFile {
fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
let data = self.data.get_mut();
data.seek(pos)
}
fn rewind(&mut self) -> Result<()> {
let data = self.data.get_mut();
data.rewind()
}
// fn stream_len(&mut self) -> Result<u64> {
// self.data.stream_len()
// }
fn stream_position(&mut self) -> Result<u64> {
let data = self.data.get_mut();
data.stream_position()
}
}
impl Write for MemFile {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let data = self.data.get_mut();
data.write(buf)
}
fn flush(&mut self) -> Result<()> {
let data = self.data.get_mut();
data.flush()
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
let data = self.data.get_mut();
data.write_vectored(bufs)
}
// fn is_write_vectored(&self) -> bool {
// self.data.is_write_vectored()
// }
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
let data = self.data.get_mut();
data.write_all(buf)
}
// fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> Result<()> {
// self.data.write_all_vectored(bufs)
// }
fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result<()> {
let data = self.data.get_mut();
data.write_fmt(fmt)
}
}
#[derive(Clone, Debug)]
struct MemDir {
metadata: MemMetadata,
items: Vec<Rc<RefCell<DirItem>>>,
}
impl MemDir {
fn new() -> Self {
let time = SystemTime::now();
Self::new_with_time(&time)
}
fn new_with_time(time: &SystemTime) -> Self {
MemDir {
metadata: MemMetadata {
file_type: FileType::Dir,
len: 0,
permissions: Permissions::new(true, true, false),
modified: time.clone(),
created: time.clone(),
},
items: vec![],
}
}
fn get(&self, name: &str) -> Option<&DirItem> {
None
}
fn get_mut(&self, name: &str) -> Option<&mut DirItem> {
None
}
fn search(&self, name: &str) -> std::result::Result<usize, usize> {
self.items.binary_search_by(
|item| item.borrow().name().cmp(name),
)
}
}
#[derive(Clone, Debug)]
enum DirItem {
Dir(String, MemDir),
File(String, MemFile),
Err(String, ErrorKind, String),
}
impl DirItem {
fn name(&self) -> &str {
match self {
DirItem::Dir(ref name, _) => name,
DirItem::File(ref name, _) => name,
DirItem::Err(ref name, _, _) => name,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MemDirEntry {
path: Utf8PathBuf,
metadata: MemMetadata,
}
impl DirEntry for MemDirEntry {
type Metadata = MemMetadata;
fn path(&self) -> Result<Utf8PathBuf> {
Ok(self.path.clone())
}
fn metadata(&self) -> Result<Self::Metadata> {
Ok(self.metadata.clone())
}
fn file_type(&self) -> Result<FileType> {
Ok(self.metadata.file_type())
}
fn file_name(&self) -> Result<String> {
Ok(self.path.file_name().unwrap().into())
}
}
pub struct MemReadDir {
base_path: Utf8PathBuf,
items: Vec<DirItem>,
index: usize,
}
impl Iterator for MemReadDir {
type Item = Result<MemDirEntry>;
fn next(&mut self) -> Option<Self::Item> {
match self.items.get(self.index) {
Some(DirItem::Dir(ref name, ref dir)) => {
let mut path = self.base_path.clone();
path.push(name);
self.index += 1;
Some(Ok(MemDirEntry{path, metadata: dir.metadata.clone()}))
},
Some(DirItem::File(ref name, ref file)) => {
let mut path = self.base_path.clone();
path.push(name);
self.index += 1;
Some(Ok(MemDirEntry{path, metadata: file.metadata.clone()}))
},
Some(DirItem::Err(_, kind, ref msg)) => {
self.index += 1;
Some(Err(Error::new(*kind, msg.clone())))
},
None => None,
}
}
}
pub struct MemFileSystem {
root: MemDir,
}
impl MemFileSystem {
pub fn new() -> Self {
Self {
root: MemDir::new_with_time(&SystemTime::UNIX_EPOCH),
}
}
pub fn mkdir<P: AsRef<Utf8Path>>(&mut self, path: P, parents: bool, exist_ok: bool) -> Result<()> {
use camino::Utf8Component::*;
let mut parents = Vec::new();
let mut iter = path.as_ref().components();
match iter.next() {
Some(Prefix(_)) => {
return make_err("Windows-like path are not supported by MemFileSystem");
},
Some(RootDir) => {
parents.push(&mut self.root);
},
Some(_) => {
return make_err("path must be absolute");
},
None => {
return make_err("path is empty")
},
}
for comp in iter {
match comp {
Prefix(_) |
RootDir => {
panic!();
},
CurDir => {},
ParentDir => {
if parents.len() <= 1 {
return make_err("invalid path")
}
parents.pop();
},
Normal(name) => {
let maybe_item = parents.last().unwrap().get_mut(name);
match maybe_item {
Some(DirItem::Dir(_, ref mut dir)) => {
parents.push(dir);
},
Some(DirItem::File(_, _)) => {
return make_err("item is a file")
}
Some(DirItem::Err(_, kind, msg)) => {
return Err(std::io::Error::new(*kind, &**msg));
}
None => {},
}
},
}
}
make_err("TODO")
}
}
impl FileSystem for MemFileSystem {
type File = MemFile;
type DirEntry = MemDirEntry;
type ReadDir = MemReadDir;
fn open_with_options<P: AsRef<Utf8Path>>(&self, path: P, options: &OpenOptions) -> Result<Self::File> {
make_err("TODO")
}
fn read_dir<P: AsRef<Utf8Path>>(&self, path: P) -> Result<MemReadDir> {
Ok(MemReadDir {
base_path: path.as_ref().into(),
items: vec![], // TODO
index: 0,
})
}
}
fn make_error(message: &str) -> std::io::Error {
std::io::Error::new(
std::io::ErrorKind::Other,
message,
)
}
fn make_err<T>(message: &str) -> Result<T> {
Err(make_error(message))
}
#[cfg(test)]
mod test {
// use camino::Utf8PathBuf;
// use super::*;
// #[test]
// fn test_file_read_write() {
// let dir = tempfile::tempdir().expect("failed to create temp test dir");
// let file_path = Utf8PathBuf::from_path_buf(dir.path().join("test.txt")).unwrap();
// let message = b"Hello world!\nThis is a test\n";
// let mut file = SysFile::create_new(&file_path).unwrap();
// file.write(message).unwrap();
// drop(file);
// let mut file = SysFile::open(&file_path).unwrap();
// let mut buf = [0u8; 256];
// let read_size = file.read(&mut buf).unwrap();
// assert_eq!(read_size, message.len());
// assert_eq!(&buf[..message.len()], message);
// }
// #[test]
// fn test_read_dir() {
// let dir = tempfile::tempdir().expect("failed to create temp test dir");
// let dir_path = Utf8Path::from_path(dir.path()).unwrap();
// let file0_path = dir_path.join("file.txt");
// let file1_path = dir_path.join("zzz.txt");
// let dir0_path = dir_path.join("some_dir");
// {
// let mut file = SysFile::create_new(file0_path).unwrap();
// file.write(b"file.txt").unwrap();
// }
// {
// let mut file = SysFile::create_new(file1_path).unwrap();
// file.write(b"zzz.txt").unwrap();
// }
// fs::create_dir(dir0_path).unwrap();
// let mut entries = SysFileSystem::read_dir(dir_path).unwrap()
// .collect::<Result<Vec<_>>>().unwrap();
// entries.sort_by_key(|entry| entry.file_name().unwrap());
// let mut it = entries.iter();
// let file0_entry = it.next().unwrap();
// assert_eq!(file0_entry.file_name().unwrap(), "file.txt");
// assert_eq!(file0_entry.file_type().unwrap(), FileType::File);
// let file0_metadata = file0_entry.metadata().unwrap();
// assert!(file0_metadata.is_file());
// assert_eq!(file0_metadata.len(), 8);
// let dir0_entry = it.next().unwrap();
// assert_eq!(dir0_entry.file_name().unwrap(), "some_dir");
// assert_eq!(dir0_entry.file_type().unwrap(), FileType::Dir);
// let dir0_metadata = dir0_entry.metadata().unwrap();
// assert!(dir0_metadata.is_dir());
// let file1_entry = it.next().unwrap();
// assert_eq!(file1_entry.file_name().unwrap(), "zzz.txt");
// assert_eq!(file1_entry.file_type().unwrap(), FileType::File);
// let file1_metadata = file1_entry.metadata().unwrap();
// assert!(file1_metadata.is_file());
// assert_eq!(file1_metadata.len(), 7);
// }
}

1
libbsv/Cargo.toml

@ -9,5 +9,6 @@ license = "AGPL-3.0-or-later"
toml = "0.5.8" toml = "0.5.8"
camino = "1.0.7" camino = "1.0.7"
regex = "1.6.0" regex = "1.6.0"
bsvfs = { path = "../bsvfs" }
cas-core = { path = "../cas-core" } cas-core = { path = "../cas-core" }
cas-simple = { path = "../cas-simple" } cas-simple = { path = "../cas-simple" }

5
libbsv/src/lib.rs

@ -18,10 +18,11 @@ extern crate toml;
extern crate camino; extern crate camino;
extern crate regex; extern crate regex;
extern crate bsvfs;
extern crate cas_core; extern crate cas_core;
mod permissions; // mod permissions;
mod tree_item; mod tree_item;
mod tree_walker; mod tree_walker;
// mod config; // mod config;
@ -30,7 +31,7 @@ mod ignore;
mod repository; mod repository;
pub use crate::permissions::Permissions; // pub use crate::permissions::Permissions;
pub use crate::tree_item::{Serialize, TreeItem}; pub use crate::tree_item::{Serialize, TreeItem};
pub use crate::path_map::{PathMap, PathPair}; pub use crate::path_map::{PathMap, PathPair};
pub use crate::repository::{Repository}; pub use crate::repository::{Repository};

23
libbsv/src/permissions.rs

@ -14,6 +14,7 @@
// along with bsv. If not, see <https://www.gnu.org/licenses/>. // along with bsv. If not, see <https://www.gnu.org/licenses/>.
use bsvfs::{Metadata};
use cas_core::{err, Error, Result}; use cas_core::{err, Error, Result};
@ -25,24 +26,12 @@ pub struct Permissions {
} }
impl Permissions { impl Permissions {
#[cfg(not(unix))] pub fn from_metadata<M: Metadata>(metadata: &M) -> Result<Self> {
pub fn from_metadata(metadata: &std::fs::Metadata) -> Result<Self> { let permissions = metadata.permissions();
if metadata.permissions().readonly() {
Ok(Permissions::READ_ONLY)
}
else {
Ok(Permission::READ_WRITE)
}
}
#[cfg(unix)]
pub fn from_metadata(metadata: &std::fs::Metadata) -> Result<Self> {
use std::os::unix::fs::MetadataExt;
let mode = metadata.mode();
Ok(Self { Ok(Self {
read: mode & 0o100 != 0, read: permissions.is_read(),
write: mode & 0o200 != 0, write: permissions.is_write(),
execute: mode & 0o400 != 0, execute: permissions.is_execute(),
}) })
} }

1
libbsv/src/repository.rs

@ -20,7 +20,6 @@ use toml::Value;
use cas_core::{Cas, err, Error, ObjectId, Result}; use cas_core::{Cas, err, Error, ObjectId, Result};
use cas_simple::{SimpleCas}; use cas_simple::{SimpleCas};
pub use crate::permissions::Permissions;
pub use crate::tree_item::{Serialize, TreeItem}; pub use crate::tree_item::{Serialize, TreeItem};

14
libbsv/src/tree_item.rs

@ -17,10 +17,9 @@
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::io::{BufRead, Write}; use std::io::{BufRead, Write};
use bsvfs::{Metadata, Permissions};
use cas_core::{err, Error, ObjectId, ObjectType, Result}; use cas_core::{err, Error, ObjectId, ObjectType, Result};
use crate::Permissions;
pub trait Serialize { pub trait Serialize {
fn serialize<W: Write>(&self, out: &mut W) -> Result<()>; fn serialize<W: Write>(&self, out: &mut W) -> Result<()>;
@ -51,9 +50,10 @@ pub struct TreeItem {
} }
impl TreeItem { impl TreeItem {
pub fn from_metadata(name: String, metadata: &std::fs::Metadata, oid: ObjectId) -> Result<Self> { // pub fn from_metadata(name: String, metadata: &std::fs::Metadata, oid: ObjectId) -> Result<Self> {
pub fn from_metadata<M: Metadata>(name: String, metadata: &M, oid: ObjectId) -> Result<Self> {
let otype = otype_from_metadata(metadata)?; let otype = otype_from_metadata(metadata)?;
let permissions = Permissions::from_metadata(metadata)?; let permissions = metadata.permissions();
Ok(Self { Ok(Self {
name: name, name: name,
@ -134,7 +134,7 @@ impl Serialize for [TreeItem] {
} }
pub fn otype_from_metadata(metadata: &std::fs::Metadata) -> Result<ObjectType> { pub fn otype_from_metadata<M: Metadata>(metadata: &M) -> Result<ObjectType> {
let file_type = metadata.file_type(); let file_type = metadata.file_type();
if file_type.is_file() { if file_type.is_file() {
@ -193,7 +193,7 @@ mod tests {
size: 42, size: 42,
created: UNIX_EPOCH + Duration::from_secs(1234), created: UNIX_EPOCH + Duration::from_secs(1234),
modified: UNIX_EPOCH + Duration::from_secs(5678), modified: UNIX_EPOCH + Duration::from_secs(5678),
permissions: Permissions { read: true, write: false, execute: true }, permissions: Permissions::new(true, false, true),
oid: ObjectId::from_str("0123456789abcdef").unwrap(), oid: ObjectId::from_str("0123456789abcdef").unwrap(),
}; };
let expected = "0123456789abcdef\ttest\t42\tr-x\t1234000\t5678000\tTest $¢ह€한/\n".as_bytes(); let expected = "0123456789abcdef\ttest\t42\tr-x\t1234000\t5678000\tTest $¢ह€한/\n".as_bytes();
@ -216,7 +216,7 @@ mod tests {
size: 42, size: 42,
created: UNIX_EPOCH + Duration::from_secs(1234), created: UNIX_EPOCH + Duration::from_secs(1234),
modified: UNIX_EPOCH + Duration::from_secs(5678), modified: UNIX_EPOCH + Duration::from_secs(5678),
permissions: Permissions { read: true, write: false, execute: true }, permissions: Permissions::new(true, false, true),
oid: ObjectId::from_str("0123456789abcdef").unwrap(), oid: ObjectId::from_str("0123456789abcdef").unwrap(),
}; };

25
libbsv/src/tree_walker.rs

@ -15,14 +15,15 @@
use std::iter::Peekable; use std::iter::Peekable;
use std::fs::{DirEntry, Metadata, ReadDir, read_dir}; // use std::fs::{DirEntry, Metadata, ReadDir, read_dir};
use std::vec::IntoIter; use std::vec::IntoIter;
use camino::{Utf8Path, Utf8PathBuf}; use camino::{Utf8Path};
use cas_core::{err, Error, ObjectId, Result}; use bsvfs::{DirEntry, FileSystem};
use cas_core::{ObjectId, Result};
use crate::{PathPair, Repository, TreeItem}; use crate::{TreeItem};
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -31,7 +32,7 @@ pub enum Action {
Update, Update,
Remove, Remove,
Skip, Skip,
Ignore, // Ignore,
} }
pub struct TreeWalker { pub struct TreeWalker {
@ -41,18 +42,14 @@ pub struct TreeWalker {
impl TreeWalker { impl TreeWalker {
pub fn new<P: AsRef<Utf8Path>>(path: P, prev_tree: Vec<TreeItem>) -> Result<Self> { pub fn new<Fs: FileSystem, P: AsRef<Utf8Path>>(fs: Fs, path: P, prev_tree: Vec<TreeItem>) -> Result<Self> {
let dir_entries = read_dir(path.as_ref().to_path_buf())? let dir_entries = fs.read_dir(path.as_ref().to_path_buf())?
.map(|res| res.map_err(|err| err.into())) .map(|res| res.map_err(|err| err.into()))
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
let mut dir_items: Vec<_> = dir_entries.into_iter() let mut dir_items: Vec<_> = dir_entries.into_iter()
.map(|dir_entry| { .map(|dir_entry| {
let file_name = dir_entry let file_name = dir_entry.file_name()?;
.file_name()
.into_string()
// .or_else(|os_string| err!("non-unicode file name '{}'", os_string.to_string_lossy()))?;
.or_else(|os_string| Err(Error::NonUnicodeFileName(os_string.to_string_lossy().into())))?;
let metadata = dir_entry.metadata()?; let metadata = dir_entry.metadata()?;
Ok(TreeItem::from_metadata( Ok(TreeItem::from_metadata(
file_name, file_name,
@ -117,11 +114,13 @@ impl Iterator for TreeWalker {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bsvfs::SysFileSystem;
use super::*; use super::*;
#[test] #[test]
fn test_tree_walker() { fn test_tree_walker() {
for item in TreeWalker::new("/home/draklaw/tmp", vec![]).unwrap() { for item in TreeWalker::new(SysFileSystem::new(), "/home/draklaw/tmp", vec![]).unwrap() {
match item { match item {
Ok((action, tree_item)) => println!("{:?} {:?}", action, tree_item.name), Ok((action, tree_item)) => println!("{:?} {:?}", action, tree_item.name),
Err(err) => println!("error while iterating directory: {}", err), Err(err) => println!("error while iterating directory: {}", err),

Loading…
Cancel
Save