diff --git a/Cargo.lock b/Cargo.lock index 66b7d92..6b5edf1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -176,11 +176,100 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +[[package]] +name = "futures" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8e3078b7b2a8a671cb7a3d17b4760e4181ea243227776ba83fd043b4ca034e" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a4d35f7401e948629c9c3d6638fb9bf94e0b2121e96c3b428cc4e631f3eb74" +dependencies = [ + "futures-core", + "futures-sink", +] + [[package]] name = "futures-core" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" +checksum = "d674eaa0056896d5ada519900dbf97ead2e46a7b6621e8160d79e2f2e1e2784b" + +[[package]] +name = "futures-executor" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc709ca1da6f66143b8c9bec8e6260181869893714e9b5a490b169b0414144ab" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc94b64bb39543b4e432f1790b6bf18e3ee3b74653c5449f63310e9a74b123c" + +[[package]] +name = "futures-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f57ed14da4603b2554682e9f2ff3c65d7567b53188db96cb71538217fc64581b" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8764258ed64ebc5d9ed185cf86a95db5cac810269c5d20ececb32e0088abbd" + +[[package]] +name = "futures-task" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd26820a9f3637f1302da8bceba3ff33adbe53464b54ca24d4e2d4f1db30f94" +dependencies = [ + "once_cell", +] + +[[package]] +name = "futures-util" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a894a0acddba51a2d49a6f4263b1e64b8c579ece8af50fa86503d52cd1eea34" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] [[package]] name = "generic-array" @@ -232,12 +321,6 @@ dependencies = [ "winapi-build", ] -[[package]] -name = "lazy_format" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f389fd31a70d3b0f1d9d80e8f7a9366127fe5fb819f4d4dd21122a167352da8f" - [[package]] name = "lazy_static" version = "1.4.0" @@ -396,17 +479,55 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "pin-project" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro-hack" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" + +[[package]] +name = "proc-macro-nested" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" + [[package]] name = "proc-macro2" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] @@ -508,9 +629,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.38" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4" +checksum = "e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd" dependencies = [ "proc-macro2", "quote", @@ -649,7 +770,7 @@ dependencies = [ "cfg-if", "chrono", "color-eyre", - "lazy_format", + "futures", "lazy_static", "rustc_version", "sha2", diff --git a/Cargo.toml b/Cargo.toml index 0e506d9..9d7c422 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,8 +23,8 @@ cfg-if = "0.1.10" sha2 = "0.9.1" chrono = "0.4.13" color-eyre = "0.5.1" -lazy_format = "1.8.3" lazy_static = "1.4.0" +futures = "0.3.6" [build-dependencies] rustc_version = "0.2" diff --git a/src/args.rs b/src/args.rs index 8a58da6..42c2232 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,46 +1,41 @@ -//! Arg parsing +//! Parse args use super::*; -pub fn program_name() -> &'static str -{ - lazy_static::lazy_static!{ - static ref NAME: &'static str = Box::leak(std::env::args().next().unwrap().into_boxed_str()); - } - - &NAME[..] -} - -/// Print usage and exit pub fn usage() -> ! { - println!(r#"Videl (versioning delete). - -Usage: {program} [OPTIONS] -Usage: {program} --help - -OPTIONS: - -Compiled with: -{features} -"#, program = program_name(), features = ); - std::process::exit(0) + println!(r#"Usage: {program} + +VersIoning DELeter v{version}, by {authours} with <3 + (Licensed GPL 3.0 or later)"#, + program = program_name(), + version = env!("CARGO_PKG_VERSION"), + authours = env!("CARGO_PKG_AUTHORS")); + std::process::exit(1) } -#[inline] pub fn parse() -> ReportedResult +pub fn program_name() -> &'static str { - parse_as_args(std::env::args().skip(1)) + lazy_static! { + static ref PROGRAM: String = std::env::args().next().unwrap(); + } + &PROGRAM[..] } -pub fn parse_as_args(args: I) -> ReportedResult -where I: IntoIterator, -T: AsRef +pub fn process(mut callback: F) -> impl Future> +where F: FnMut(String) -> T, + T: Future + Send + 'static, + T::Output: Send { - let mut args = args.into_iter(); - - while let Some(arg) = args.next() { - let arg = arg.as_ref(); - + let args = std::env::args(); + let output: Vec<_> = args.skip(1).map(|arg| tokio::spawn(callback(arg))).collect(); + async move { + let mut j=0; + for (i, x) in (0..).zip(futures::future::join_all(output).await) { + x + .wrap_err(eyre!("Child panic or cancel")) + .with_note(|| format!("Child {}", i).header("While processing"))?; + j+=1; + } + Ok(j) } - - todo!() } diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index 7037378..0000000 --- a/src/config.rs +++ /dev/null @@ -1,104 +0,0 @@ -//! Config and params -use std::{ - path::{ - PathBuf, - }, - error, -}; - -/// The error handling mode for this run. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub enum ErrorMode -{ - Ignore, - Report, - Cancel, - Terminate, -} - -pub trait ErrModeExt -{ - fn handle_with(self, err: &ErrorMode) -> Result,E>; -} -impl ErrModeExt for Result -where E: error::Error -{ - #[inline] fn handle_with(self, err: &ErrorMode) -> Result,E> - { - err.handle(self) - } -} - -impl ErrorMode -{ - pub fn handle(&self, err: Result) -> Result, E> - where E: error::Error, - { - match err { - Ok(v) => Ok(Some(v)), - Err(err) => { - match self { - Self::Ignore => Ok(None), - Self::Report => { - eprintln!("Error: {}", err); - Ok(None) - }, - Self::Cancel => { - Err(err) - }, - Self::Terminate => { - eprintln!("Fatal error: {}", err); - std::process::exit(-1) - } - } - } - } - } -} - -impl Default for ErrorMode -{ - #[inline] - fn default() -> Self - { - Self::Report - } -} - -/// The common options flags for all modes -#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct Flags -{ - pub verbose: bool, - pub error_mode: ErrorMode, -} - -impl Default for Flags -{ - #[inline] - fn default() -> Self - { - Self { - verbose: false, - error_mode: ErrorMode::default(), - } - } -} - -/// Each option mode -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Mode -{ - Normal { - paths: Vec, - }, - Help, -} - -/// All config for this run -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Config -{ - pub data: Mode, - pub flags: Flags, -} diff --git a/src/consts.rs b/src/consts.rs deleted file mode 100644 index 818270f..0000000 --- a/src/consts.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! Consts - -/// Size for most buffers -pub const BUFFER_SIZE: usize = 4096; diff --git a/src/database.rs b/src/database.rs deleted file mode 100644 index 8412b67..0000000 --- a/src/database.rs +++ /dev/null @@ -1,113 +0,0 @@ -//! Handles building the database, and exposes data structures for the database and ways to interact with them -use super::*; -use std::{ - collections::{ - HashMap, - HashSet, - }, - path::{ - PathBuf, - Path, - }, - cmp::{ - PartialOrd, - Ordering, - }, - marker::{ - Unpin, - }, -}; -use chrono::{ - DateTime, - Utc, -}; -use tokio::{ - prelude::*, - io::{ - AsyncRead, - }, -}; - -#[derive(Debug, PartialEq, Eq)] -pub struct Database -{ - /// Key here is the actual location in /tmp/videl of the database and versioned files - database: HashMap, -} - -/// All versions of a single file -#[derive(Debug, PartialEq, Eq)] -pub struct Entry -{ - /// Versions are a hashset because we don't need to version files that are /entirely/ identical. (i.e. same timestamp and hash) - versions: HashSet, -} - -/// A single version of a single file -#[derive(Debug, PartialEq, Eq, Hash)] -pub struct Version -{ - date: DateTime, - hash: hash::Sha256Sum, - size: usize, -} - -impl PartialOrd for Version -{ - #[inline] fn partial_cmp(&self, other: &Self) -> Option - { - self.date.partial_cmp(&other.date) - } -} - -impl Version -{ - /// Gets the lowermost name of the path for this specific version - pub fn get_pathname_string(&self) -> String - { - format!("{}", self.date.timestamp_nanos()) - } - - /// Create a new `Version` from a stream source with a specific UTC timestamp. - pub async fn new_specific(timestamp: DateTime, from: &mut R) -> io::Result - where R: AsyncRead + Unpin - { - let (size, hash) = { - let mut hash = hash::Sha256Sum::empty(); - (hash.compute_into(from).await?, hash) - }; - - Ok(Self { - date: timestamp, - hash, - size, - }) - } - - /// Create a new `Version` from a stream sorce with the current UTC timestamp. - pub async fn new(from: &mut R) -> io::Result - where R: AsyncRead + Unpin - { - Self::new_specific(Utc::now(), from).await - } - - /// Length of this `Version` - #[inline] pub fn len(&self) -> usize - { - self.size - } - - /// Timestamp of this `Version` - #[inline] pub fn timestamp(&self) -> &DateTime - { - &self.date - } - - /// Hash of this `Version` - #[inline] pub fn hash(&self) -> &hash::Sha256Sum - { - &self.hash - } - - //TODO: How do we manage serialisation? Do we save all this metadata, or just recalculate it from the filename? -} diff --git a/src/error/aggregate.rs b/src/error/aggregate.rs deleted file mode 100644 index e4571bb..0000000 --- a/src/error/aggregate.rs +++ /dev/null @@ -1,119 +0,0 @@ -//! Aggregate errors -use super::*; -use std::result::Result; - -/// An aggregate of 2 operations that *might* have failed. -/// -/// In order for all aggregate operations to propagate a value, they must all succeed. -/// However, will return the errors of failed ones and continue propagating an `Err()` variant, so you get any of the error(s) from the previous aggregate calls. -/// -/// This produces a potential tree of errors, which the `Error` trait cannot represent, so the reporting for `Aggregate` is a bit shit so far, but its general rule is: -/// * Debug formatter prints all of the first and second errors including their sub-errors. So, the entire tree. But in an ugly and hard to read way -/// * The default formatter prints only the *first* error, but also only if the second error exists, because: -/// * The `source()` for the aggregate is first the *second* error, if exists, then the *first* if not. -/// -/// This means, `Aggregate`'s message itself when it does form a tree will be the first branch of the tree, and its source will be the second branch. The second branch is signigicanly better formatted by `eyre`, so the results from the first branch might be a bit hard to decipher, but it's fit for purpose in smaller chains. -/// -/// We prioratise the second branch as the source because that's the one least likely to be an `Aggregate` itself, and will have a more meaningful message trace when reported by `eyre`. -pub struct Aggregate(Option, Option); - -pub trait AggregateExt: Sized -{ - /// Aggregate this result with a function that can take the value if suceeded and return its own value, or a reference to the error and return either its own error or *both* - /// - /// # How it works - /// `Operation 2` is always ran, and gets the value from `operation 1` if it suceeded. - /// There is never a time where the closure will not be called except when there is a panic - /// - /// * If `operation 1` and `2` fail: The aggregate containing both errors returns - /// * If `operation 1` succeeds but operation 2 does not: the error from `operation 2` is returned - /// * If `operation 2` succeeds but `operation 1` does not: the error from `operation 1` is returned - /// * If both succeed: the value from `operation 2` is returned. - fn aggregate_with(self, next: F) -> Result> - where F: FnOnce(Result) -> Result; - /// Same as `aggregate_with` except it takes a result instead of taking a closure, so it is evaluated before the aggregation is called. In practive this has little difference, because all closures are called regardless, however it can help in certain situation where we don't need to know the value of pervious aggregate calls, or if we want to calls to happen even if there is a panic in one of the aggregations. - fn aggregate(self, next: Result) -> Result<(T,V), Aggregate>; -} - -impl AggregateExt for Result -{ - fn aggregate(self, next: Result) -> Result<(T,V), Aggregate> - { - match self { - Ok(v) => match next { - Ok(v1) => Ok((v,v1)), - Err(err) => Err(Aggregate(None, Some(err))), - }, - Err(er) => match next { - Ok(_) => Err(Aggregate(Some(er), None)), - Err(err) => Err(Aggregate(Some(er), Some(err))), - }, - } - } - fn aggregate_with(self, next: F) -> Result> - where F: FnOnce(Result) -> Result - { - match self { - Ok(v) => { - match next(Ok(v)) { - Err(err) => Err(Aggregate(None, Some(err))), - Ok(ok) => Ok(ok), - } - } - Err(e) => { - match next(Err(&e)) { - Err(err) => Err(Aggregate(Some(e), Some(err))), - Ok(_) => Err(Aggregate(Some(e), None)), - } - } - } - } -} - -impl fmt::Debug for Aggregate -where T: fmt::Debug, - U: fmt::Debug -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - write!(f, "Aggregate Error ({:?}, {:?})", self.0, self.1) - } -} - -impl fmt::Display for Aggregate -where T: error::Error, - U: error::Error, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - write!(f, "Aggregate Error ({})", self.0.as_ref().map(|_| 1).unwrap_or(0) + self.1.as_ref().map(|_| 1).unwrap_or(0))?; - if self.1.is_some() { - if let Some(one) = &self.0 { - writeln!(f, "\nBranch: {}", one)?; - for next in std::iter::successors(one.source(), |e| e.source()) - { - writeln!(f, " -> {}", next)?; - } - } - } - Ok(()) - } -} - -impl error::Error for Aggregate -where T: error::Error + 'static, - U: error::Error + 'static, -{ - fn source(&self) -> Option<&(dyn error::Error + 'static)> - { - if let Some(z) = &self.1 { - return Some(z); - } - - if let Some(o) = &self.0 { - return Some(o); - } - - None - } -} diff --git a/src/error/context.rs b/src/error/context.rs deleted file mode 100644 index 3289e2b..0000000 --- a/src/error/context.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! Error contexts -use super::*; - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Default)] -pub struct Context -{ - file: Option<&'static str>, - line: Option, - column: Option, -} - -impl Context -{ - /// Create a new populated context - pub const fn with_all(file: &'static str, line: u32, column: u32) -> Self - { - Self { - file: Some(file), - line: Some(line), - column: Some(column) - } - } - - pub fn is_empty(&self) -> bool - { - self.column.is_none() && - self.line.is_none() && - self.file.is_none() - } - - -} - -impl fmt::Display for Context -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - if self.is_empty() { - write!(f, "") - } else { - if let Some(file) = self.file { - write!(f, "{} ", file) - } else { - write!(f, "? ") - }?; - if let Some(line) = self.line { - write!(f, "{}:", line) - } else { - write!(f, "?:") - }?; - if let Some(column) = self.column { - write!(f, "{}", column) - } else { - write!(f, "?") - } - } - } -} - -pub struct ContextMessage(pub(super) D, pub(super) Context) -where D: fmt::Display; - -impl ContextMessage -where D: fmt::Display -{ - pub const fn new(ctx: Context, msg: D) -> Self - { - Self(msg, ctx) - } -} - diff --git a/src/error/message.rs b/src/error/message.rs deleted file mode 100644 index 2851db0..0000000 --- a/src/error/message.rs +++ /dev/null @@ -1,113 +0,0 @@ -//! Message string -use super::*; -use std::result::Result; - -pub enum Message -{ - Str(&'static str), - String(String), - Other(Box), - None, -} -impl cmp::PartialEq for Message -{ - fn eq(&self, other: &Self) -> bool - { - self.as_str() == other.as_str() - } -} - -impl Message -{ - /// Into the internal string - pub fn into_string(self) -> Option> - { - Some(match self { - Self::Str(string) => Cow::Borrowed(string), - Self::String(string) => Cow::Owned(string), - Self::Other(other) => Cow::Owned(other.to_string()), - _ => return None, - }) - } - /// Returns message as a string, either created from `format!`, or referenced from inner - pub fn as_str(&self) -> Option> - { - Some(match self { - Self::Str(string) => Cow::Borrowed(string), - Self::String(string) => Cow::Borrowed(&string), - Self::Other(other) => Cow::Owned(other.to_string()), - _ => return None, - }) - } - pub const fn from_str(string: &'static str) -> Self - { - Self::Str(string) - } - pub const fn from_string(string: String) -> Self - { - Self::String(string) - } - pub fn from_other(other: impl fmt::Display + Send + Sync + 'static) -> Self - { - Self::Other(Box::new(other)) - } - - pub const fn none() -> Self - { - Self::None - } - - pub fn fmt(&self) -> impl fmt::Display + '_ - { - struct Format<'a>(&'a Message); - impl<'a> fmt::Display for Format<'a> - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - match self.0 { - Message::Str(string) => write!(f, "{}", string), - Message::String(string) => write!(f, "{}", string), - Message::Other(other) => write!(f, "{}", other), - _ => Ok(()) - } - - } - } - - Format(&self) - } -} - -impl FromStr for Message -{ - type Err = !; - - fn from_str(string: &str) -> Result - { - Ok(Self::String(string.to_string())) - } -} - -impl From for Message -where T: fmt::Display + 'static + Send + Sync -{ - fn from(from: T) -> Self - { - Self::from_other(from) - } -} - -impl fmt::Debug for Message -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - write!(f, "error::Message (")?; - match self { - Self::Str(string) => write!(f, "{:?}", string), - Self::String(string) => write!(f, "{:?}", string), - Self::Other(_) => write!(f, ""), - _=> write!(f, ""), - }?; - write!(f,")") - } -} diff --git a/src/error/mod.rs b/src/error/mod.rs deleted file mode 100644 index 1f93d4a..0000000 --- a/src/error/mod.rs +++ /dev/null @@ -1,349 +0,0 @@ -//! Error handling stuffs (I think we'll go with this one in the end) -use std::{ - error, - io, - cmp, - fmt, - str::{ - FromStr, - }, - borrow::Cow, - marker::{ - Send, Sync - }, -}; - -/// Result for all our operations, see `ErrorKind` below -/// -/// This type can be used for when we want to propagate an error, but not pass it off to `eyre` reporter just yet, because we might still want to act on it and respond. -pub type Result = std::result::Result; - -mod context; -pub use context::*; -mod message; -use message::*; -mod wrap; -pub use wrap::*; -mod aggregate; -pub use aggregate::*; - -impl Default for ErrorKind -{ - #[inline] - fn default() -> Self - { - Self::None - } -} - -impl Error -{ - /// Into the `ErrorKind` - pub fn into_inner(self) -> ErrorKind - { - *self.internal - } - - /// The kind of the error - pub fn kind(&self) -> &ErrorKind - { - &self.internal - } - - /// The message for this error - pub fn message(&self) -> Option> - { - self.message.as_str() - } - - /// The context from this error - pub fn context(&self) -> &Context - { - &self.context - } - - /// Create a new error with a constant string - pub fn new_static(err: ErrorKind, ctx: Context, message: &'static str) -> Self - { - Self { - internal: box err, - context: ctx, - message: Message::from_str(message), - } - } - /// Create a new error with a string - pub fn new_string(err: ErrorKind, ctx: Context, message: String) -> Self - { - Self { - internal: box err, - context: ctx, - message: Message::from_string(message), - } - } - /// Create a new error with a message - pub fn new(err: ErrorKind, ctx: Context, message: impl fmt::Display + 'static + Send + Sync) -> Self - { - Self { - internal: box err, - context: ctx, - message: Message::from_other(message), - } - } - /// Create a new error with no message - pub fn new_none(err: ErrorKind, ctx: Context) -> Self - { - Self { - internal: box err, - context: ctx, - message: Message::None, - } - } -} - -impl fmt::Display for Error -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - if !self.context().is_empty() { - write!(f, "{}: ", self.context)?; - } - match &self.message { - Message::None => write!(f, "{}", self.internal), - message => write!(f, "{}", message.fmt()) - } - } -} -impl error::Error for Error -{ - fn source(&self) -> Option<&(dyn error::Error + 'static)> - { - match &self.message { - Message::None => std::iter::successors(self.internal.source(), |e| e.source()).take(1).next(), - _ => Some(&self.internal), - } - } -} - -#[macro_export] macro_rules! context { - () => ($crate::error::Context::with_all(file!(), line!(), column!())); - ($msg:expr) => ($crate::error::ContextMessage::new($crate::context!(), $msg)); - (yield $fmt:literal $($tt:tt)*) => ($crate::context!(lazy_format::lazy_format!($fmt $($tt)*))); - ($fmt:literal $($tt:tt)*) => ($crate::context!(format!($fmt $($tt)*))); -} - -/// Macro for instantiating errors and also ad-hoc error types -/// -/// # Instantiating `Error`. -/// The primary use is to return errors of the `Error` struct, this is how it can work. -/// ## With `ErrorKind` -/// The kind should be prefixed with `;`: -/// ``` -/// error!(ErrorKind::Unknown; "unknown error message"); -/// error!(ErrorKind::IO(yield io_err; "I/O error opening {:?}", file)); -/// ``` -/// -/// ## Without `ErrorKind` -/// With all except a single string literal, the message is boxed as the `ErrorKind`, and printed with a `None` message. -/// This means that the message given as an ad-hoc error type is counted as the source. -/// -/// ``` -/// error!("unknown error"); -/// error!(yield "something went wrong: {}", message); -/// ``` -/// -/// # Defining errors -/// To create ad-hoc errors, use `error!(impl ...)`. It has the same syntax as the above cases, except with no error kind -/// ``` -/// error!(impl "Error message"); -/// error!(impl yield "lazy formatted string {}", string); -/// ``` -/// -/// # `yield`ing formatting -/// When format string are provided to `error!()`, they by default will use the eager `format!()` macro, which will immediately evaluate it's arguments and produce the string to put into the error. -/// To instead box the arguments and evaluate as-needed, we can use the `lazy_format` crate by prefixing the format string literals with `yield`. This works on both `Error` instantiations and ad-hoc error definitions. -#[macro_export] macro_rules! error { - (@ $msg:expr) => { - { - pub struct DisplayError(D, Context) where D: fmt::Display; - impl fmt::Debug for DisplayError - where D: fmt::Display - { - #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - write!(f, "{}", std::any::type_name::()) - } - } - impl fmt::Display for DisplayError - where D: fmt::Display - { - #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - self.0.fmt(f) - } - } - impl error::Error for DisplayError - where D: fmt::Display{} - - impl From> for Error - { - fn from(from: DisplayError Self - { - Self::new_none(ErrorKind::Any(box from), from.context); - } - } - - - DisplayError($msg, context!()) - } - }; - - (impl $lit:literal) => ($crate::error!(@ $lit)); - (impl $expr:expr) => ($crate::error!(@ $expr)); - (impl yield $lit:literal $($tt:tt)*) => ($crate::error!(@ lazy_format::lazy_format!($lit $($tt)*))); - (impl $lit:literal $($tt:tt)*) => ($crate::error!(@ format!($lit $($tt)*))); - - ($kind:expr; $lit:literal) => ($crate::error::Error::new_static($kind, $crate::context!(), $lit)); - ($lit:literal) => ($crate::error!(Default::default(), $lit)); - - ($kind:expr; $msg:expr) => ($crate::error::Error::new($kind, $crate::context!(), $msg)); - ($msg:expr) => ($crate::error::Error::new_none($crate::error::ErrorKind::Any(box $crate::error!(@ $msg)), $crate::context!())); - - (yield $lit:literal $($tt:tt)*) => ($crate::error::Error::new_none($crate::error::ErrorKind::Any(box $crate::error!(@ lazy_format::lazy_format!($lit $($tt)*))), $crate::context!())); - ($lit:literal $($tt:tt)*) => ($crate::error::Error::new_none($crate::error::ErrorKind::Any(box $crate::error!(@ format!($lit $($tt)*))), $crate::context!())); - - (yield $kind:expr; $lit:literal $($tt:tt)*) => ($crate::error!($kind; lazy_format::lazy_format!($lit $($tt)*))); - ($kind:expr; $lit:literal $($tt:tt)*) => ($crate::error::Error::new_string($kind, $crate::context!(), format!($lit $($tt)*))); -} - -#[derive(Debug)] -pub struct Error -{ - internal: Box, - context: Context, - message: Message, -} - -// Actual stuffs - -#[non_exhaustive] -#[derive(Debug)] -pub enum ErrorKind -{ - IO(io::Error), - Wrap(Error), - Any(Box), - None, - Unknown, -} - -impl fmt::Display for ErrorKind -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - match self { - Self::IO(io) => write!(f, "i/o error: {}", io), - Self::Wrap(wrap) => write!(f, "{}", wrap), - Self::Any(any) => write!(f, "{}", any), - Self::None => Ok(()), - _ => write!(f, "unknown error"), - } - } -} - -impl error::Error for ErrorKind -{ - fn source(&self) -> Option<&(dyn error::Error + 'static)> - { - Some(match &self { - Self::IO(io) => io, - Self::Wrap(wrap) => return wrap.source(), - Self::Any(any) => any.as_ref(), - _ => return None, - }) - } -} - -impl From for ErrorKind -{ - fn from(from: io::Error) -> Self - { - Self::IO(from) - } -} - -impl From for Error -where T: Into -{ - fn from(from: T) -> Self - { - Self { - internal: box from.into(), - context: Default::default(), - message: Message::None, - } - } -} - -#[cfg(test)] -#[allow(unused_imports)] -mod test -{ - use super::*; - use color_eyre::{ - eyre::{ - Result as ReportedResult, - }, - Help, - SectionExt, - }; - #[test] - fn test() -> ReportedResult<()> - { - fn testt() -> Result<()> { - let err= error!(yield one().unwrap_err().into_inner(); "hello world {} {}", 1, 2); - Err(err) - .wrap_err_with(|| context!("with string")) - .wrap_err_with(|| context!(yield "with another string and some stuffs {}", 1))?; - Ok(()) - } - - fn works() -> Result<()> - { - Ok(()) - } - - fn one() -> Result<()> - { - Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "end of records"))?; - Ok(()) - } - - fn two() -> Result<()> - { - Err(std::io::Error::new(std::io::ErrorKind::NotFound, "nonexistent"))?; - Ok(()) - } - - fn three() -> Result<()> - { - Err(std::io::Error::new(std::io::ErrorKind::BrokenPipe, "piped")) - .wrap_err_with(|| context!("hello hello"))?; - Ok(()) - } - - - - Ok(one() - .aggregate_with(|_| testt()) - .aggregate_with(|_| two()) - .aggregate_with(|_| works()) - .aggregate_with(|_| three())?) - } - - #[test] - fn strings() -> ReportedResult<()> - { - Err(error!(yield "literal format {}", 1))?; - Ok(()) - } -} diff --git a/src/error/wrap.rs b/src/error/wrap.rs deleted file mode 100644 index 6d90f77..0000000 --- a/src/error/wrap.rs +++ /dev/null @@ -1,117 +0,0 @@ -//! Wrapping errors -use super::*; -use std::result::Result; - -/// Convenienve constant for `map_msg`, used so you don't need to keep going `Option::::None`. -pub const NO_MESSAGE: Option = None; - -/// Traits for assigning messages and context to any error that can be transformed into `ErrorKind`. -pub trait Assign: Sized -{ - /// Wrap an error with a context message deferred - fn wrap_err_with(self, with: F) -> Result - where D: fmt::Display + Send + Sync + 'static, - F: FnOnce() -> ContextMessage; - /// Wrap an error with a context message - #[inline] fn wrap_err(self, with: ContextMessage) -> Result - where D: fmt::Display + Send + Sync + 'static - { - self.wrap_err_with(|| with) - } - /// Wrap an error with a message and no context - #[inline] fn wrap_err_no_context(self, with: D) -> Result - where D: fmt::Display + Send + Sync + 'static - { - self.wrap_err(ContextMessage::new(Default::default(), with)) - } - /// Wrap an error with a message and no context deferred - #[inline] fn wrap_err_with_no_context(self, with: F) -> Result - where D: fmt::Display + Send + Sync + 'static, - F: FnOnce() -> D - { - self.wrap_err_with(|| ContextMessage::new(Default::default(), with())) - } -} - -impl Assign for Result -{ - fn wrap_err_with(self, with: F) -> Result - where D: fmt::Display + Send + Sync + 'static, - F: FnOnce() -> ContextMessage - { - self.map_err(|e| { - let val = with(); - Error::new(ErrorKind::Wrap(e), val.1, val.0) - }) - } -} -impl Assign for Result - where E: Into -{ - fn wrap_err_with(self, with: F) -> Result - where D: fmt::Display + Send + Sync + 'static, - F: FnOnce() -> ContextMessage - { - self.map_err(|e| { - let val = with(); - Error::new(e.into(), val.1, val.0) - }) - } -} - -/// Error mapping extensions -pub trait WrapErr: Sized -{ - - /// Map the error kind - fn map_kind_with ErrorKind>(self, kind: F) -> Result; - /// Map the error kind impeatively - #[inline] fn map_kind(self, kind: ErrorKind) -> Result - { - self.map_kind_with(|_| kind) - } - - /// Map the error message - fn map_msg_with>) -> Option>(self, msg: F) -> Result - where M: fmt::Display + Send + Sync + 'static; - /// Map the error message - #[inline] fn map_msg(self, msg: M) -> Result - where M: fmt::Display + Send + Sync + 'static - { - self.map_msg_with(|_| Some(msg)) - } - - #[inline] fn map_msg_remove(self) -> Result - { - self.map_msg_with(|_| NO_MESSAGE) - } -} - -impl WrapErr for Result -{ - fn map_kind_with ErrorKind>(self, kind: F) -> Result - { - self.map_err(|err| { - Error { - internal: box kind(*err.internal), - ..err - } - }) - } - - fn map_msg_with>) -> Option>(self, msg: F) -> Result - where M: fmt::Display + Send + Sync + 'static - { - self.map_err(|err| { - let msg = msg(err.message.into_string()); - - Error { - message: match msg { - Some(msg) => Message::from_other(msg), - None => Message::None, - }, - ..err - } - }) - } -} diff --git a/src/ext.rs b/src/ext.rs deleted file mode 100644 index 8af87ac..0000000 --- a/src/ext.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! Extensions -use std::{ - fmt::{ - Write as _, - }, -}; - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct HexSlice<'a>(&'a [u8]); - -impl<'a> HexSlice<'a> -{ - /// Create a new `HexSlice` wrapper - #[inline] pub fn new(from: &'a T) -> Self - where T: AsRef<[u8]> + ?Sized + 'a - { - Self(from.as_ref()) - } - - /// Create a hex string output - pub fn to_hex_string(&self) -> String - { - - let mut output = String::with_capacity(self.0.len()*2); - - write!(output, "{}", self).unwrap(); - - output - } -} - -impl std::fmt::Display for HexSlice<'_> -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - for byte in self.0.iter() - { - write!(f, "{:02x}", byte)?; - } - Ok(()) - } -} - - -pub trait HexSliceExt -{ - fn as_hex(&self) -> HexSlice<'_>; -} - -impl HexSliceExt for T -where T: AsRef<[u8]> -{ - #[inline] fn as_hex(&self) -> HexSlice<'_> - { - HexSlice(self.as_ref()) - } -} - -impl AsRef<[u8]> for HexSlice<'_> -{ - fn as_ref(&self) -> &[u8] - { - self.0 - } -} - -#[cfg(test)] -mod tests -{ - use super::*; - #[test] - fn hex_encode_bytes() - { - let input = b"hello world"; - let output = input.as_hex(); - assert_eq!("68656c6c6f20776f726c64", output.to_hex_string()); - assert_eq!(input.as_hex(), output.as_hex()); - assert_eq!(format!("{}", output), "68656c6c6f20776f726c64"); - } -} diff --git a/src/features.rs b/src/features.rs deleted file mode 100644 index a15b3ea..0000000 --- a/src/features.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! For printing features etc. -use std::{ - fmt, -}; -use cfg_if::cfg_if; - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Enabled -{ - On, - Off, -} - -trait Feature -{ - const DEFAULT: Enabled; - const CURRENT: Enabled; - - const fn is_default() -> bool - { - Self::DEFAULT == Self::CURRENT - } - - const fn is_enabled() -> bool - { - Self::CURRENT == Enabled::On - } -} - -#[macro_export] macro_rules! feature { - ($name:literal) => { - cfg_if!{ - if #[cfg(feature=$name)] { - //TODO: create anony type that impl's Feature up threre, and such - } else { - - } - } - } -} - - - -#[derive(Debug)] -pub struct Features; - -impl fmt::Display for Features -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - //TODO: print features - } -} diff --git a/src/hash.rs b/src/hash.rs deleted file mode 100644 index dfa3db0..0000000 --- a/src/hash.rs +++ /dev/null @@ -1,149 +0,0 @@ -//! Hashing abstraction -use super::*; -use sha2::{ - Sha256, Digest, -}; -use tokio::{ - prelude::*, - io::{ - AsyncRead, - }, -}; -use std::{ - io, - marker::Unpin, -}; - -pub const SHA256_SIZE: usize = 32; - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, Ord, PartialOrd)] -pub struct Sha256Sum([u8; SHA256_SIZE]); - -impl Default for Sha256Sum -{ - #[inline] - fn default() -> Self - { - Self([0; SHA256_SIZE]) - } -} - -impl Sha256Sum -{ - /// Create a new empty hash sum container - pub const fn empty() -> Self - { - Self([0; SHA256_SIZE]) - } - - pub const fn from_raw(from: [u8; SHA256_SIZE]) -> Self - { - Self(from) - } - - pub fn from_slice(from: &[u8]) -> Self - { - let mut output = [0u8; SHA256_SIZE]; - assert_eq!(util::copy_slice(&mut output, from), SHA256_SIZE); - Self(output) - } - - /// Compute SHA256 hash from a slice - pub fn compute_slice(from: T) -> Self - where T: AsRef<[u8]> - { - let mut hasher = Sha256::new(); - hasher.update(from.as_ref()); - - Self::from_slice(&hasher.finalize()[..]) - } - - /// Compute SHA256 from a stream - /// - /// # Notes - /// Will read until 0 bytes are returned from the `read()` call. - pub async fn compute_stream(from: &mut W) -> io::Result - where W: AsyncRead + Unpin - { - let mut read; - let mut buffer = [0u8; consts::BUFFER_SIZE]; - let mut hasher = Sha256::new(); - while {read = from.read(&mut buffer[..]).await?; read != 0} { - hasher.update(&buffer[..read]); - } - Ok(Self::from_slice(&hasher.finalize()[..])) - } - - /// Compute from a stream into this instance. Return the number of bytes successfully written into the instance. - pub async fn compute_into(&mut self, from: &mut W) -> io::Result - where W: AsyncRead + Unpin - { - let mut read; - let mut done=0; - let mut buffer = [0u8; consts::BUFFER_SIZE]; - let mut hasher = Sha256::new(); - while {read = from.read(&mut buffer[..]).await?; done += read; read !=0} { - hasher.update(&buffer[..read]); - } - util::copy_slice(self, hasher.finalize()); - Ok(done) - } -} - -impl AsRef<[u8]> for Sha256Sum -{ - #[inline] fn as_ref(&self) -> &[u8] - { - &self.0[..] - } -} - -impl AsMut<[u8]> for Sha256Sum -{ - #[inline] fn as_mut(&mut self) -> &mut [u8] - { - &mut self.0[..] - } -} - -impl From<[u8; SHA256_SIZE]> for Sha256Sum -{ - #[inline] fn from(from: [u8; SHA256_SIZE]) -> Self - { - Self(from) - } -} - -impl From for Sha256Sum -{ - fn from(from: Sha256) -> Self - { - Self::from_slice(&from.finalize()) - } -} - -/// Update a hasher from a stream async -pub async fn stream_async(hasher: &mut H, from: &mut W) -> io::Result -where H: Digest, - W: AsyncRead + Unpin -{ - let mut read; - let mut done=0; - let mut buffer = [0u8; consts::BUFFER_SIZE]; - while {read = from.read(&mut buffer[..]).await?; done += read; read !=0} { - hasher.update(&buffer[..read]); - } - Ok(done) -} - -impl std::fmt::Display for Sha256Sum -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - for byte in self.0.iter() - { - write!(f, "{:02x}", byte)?; - } - Ok(()) - } -} diff --git a/src/main.rs b/src/main.rs index baa67ec..290b5a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,64 +1,67 @@ -#![cfg_attr(nightly, feature(never_type))] -#![cfg_attr(nightly, feature(box_syntax))] -#![cfg_attr(nightly, feature(const_fn))] +#![cfg_attr(nightly, feature(never_type))] #![allow(dead_code)] use cfg_if::cfg_if; +use lazy_static::lazy_static; +use futures::prelude::*; -mod ext; -use ext::*; - -mod error; -#[allow(unused_imports)] -use error::{ - WrapErr as _, - Assign as _, - Result as VidelResult, -}; #[allow(unused_imports)] use color_eyre::{ eyre::{ self, eyre, - Result as ReportedResult, + WrapErr as _, }, + SectionExt as _, Help as _, }; -mod consts; mod util; -mod hash; -mod metadata; -mod resolve; -mod database; -mod config; -mod features; mod args; -type Pointer = *const !; +cfg_if!{ + if #[cfg(nightly)] { + type Never = !; + } else { + type Never = std::convert::Infallible; + } +} + +type Pointer = *const Never; -fn install() -> ReportedResult<()> +fn install() -> eyre::Result<()> { color_eyre::install()?; Ok(()) } -async fn begin() -> ReportedResult +async fn process(file: String) +{ + println!(" -> {}", file); + //TODO: Process this file +} + +async fn begin() -> eyre::Result { install()?; - + + if args::process(process).await + .wrap_err(eyre!("One or more child workers failed to complete successfully"))? == 0 { + args::usage(); + } Ok(0) } #[tokio::main] -async fn main() +async fn main() -> eyre::Result<()> { std::process::exit(match begin().await { - Ok(0) => return, + Ok(0) => return Ok(()), Err(err) => { eprintln!("\nexited with error: {}", err); - 1 + return Err(err) + .wrap_err(eyre!("Fatal error")); }, Ok(v) => v }) diff --git a/src/metadata.rs b/src/metadata.rs deleted file mode 100644 index 99a1d88..0000000 --- a/src/metadata.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! Abstraction over the metadata file -//! -//! This module contains the data structures and functions to -//!* Create metadata files -//!* Load metadata files into memory -//!* Inspect and mutate metadata -//!* Validate metadata -//!* Save metadata files to disk diff --git a/src/resolve.rs b/src/resolve.rs deleted file mode 100644 index 3e5face..0000000 --- a/src/resolve.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! Resolution -//! -//! Pathname and filename encodings and such - -use super::*; -use std::{ - path::{ - Path, - }, - io::{ - self, - }, - os::unix::ffi::OsStrExt as _, //idc about windows - marker::Unpin, -}; - -use tokio::{ - prelude::*, - io::AsyncWrite, -}; - -#[cfg(not(feature="fast_pathnames"))] -use sha2::{ - Sha256, Digest, -}; - -/// Encode a pathname to an output buffer -/// -/// # Notes -/// This only gets the final part of the encoded pathname. You should prepend the parent afterwards. -pub async fn encode_pathname_to, W: AsyncWrite+Unpin>(from: P, mut to: W) -> io::Result -{ - let string = encode_pathname(from); - - to.write_all(string.as_bytes()).await?; - - Ok(string.len()) -} - -/// Encode a pathname to a string -/// -/// # Notes -/// This only gets the final part of the encoded pathname. You should prepend the parent afterwards. -pub fn encode_pathname>(from: P) -> String -{ - cfg_if! { - if #[cfg(feature="fast_pathnames")] { - base64::encode(from.as_ref().as_os_str().as_bytes()) - } else { - let mut hasher = Sha256::new(); - hasher.update(from.as_ref().as_os_str().as_bytes()); - hasher.finalize().as_hex().to_hex_string() - } - } -} - -//TODO: `deocode_pathname`. Takes input pathname (the full encoded one), and either: -// - fast_pathnames: Decodes the final part into the pathname -// - not(fast_pathnames): Looks in `metadata` to find the correct pathname, and validates the hash diff --git a/src/util.rs b/src/util.rs index 3c697f0..698f2bd 100644 --- a/src/util.rs +++ b/src/util.rs @@ -23,6 +23,39 @@ where V: AsMut<[T]>, i } +pub mod bytes +{ + /// Copy `src`'s bytes into `dst`, returning the number of bytes copied. + /// + /// # Note + /// The memory regions must not overlap + pub fn copy_slice(dst: &mut [u8], src: &[u8]) -> usize + { + let sz = std::cmp::min(dst.len(), src.len()); + if sz != 0 { + unsafe { + std::ptr::copy_nonoverlapping(&src[0] as *const u8, &mut dst[0] as *mut u8, sz); + } + } + sz + } + + /// Copy `src`'s bytes into `dst`, returning the number of bytes copied. + /// + /// # Note + /// The memory regions can overlap + pub fn move_slice(dst: &mut [u8], src: &[u8]) -> usize + { + let sz = std::cmp::min(dst.len(), src.len()); + if sz != 0 { + unsafe { + std::ptr::copy(&src[0] as *const u8, &mut dst[0] as *mut u8, sz); + } + } + sz + } +} + #[cfg(test)] mod tests {