diff --git a/Cargo.toml b/Cargo.toml index 54b58ce..69759a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +mapcat = { path = "." } memmap = "0.7.0" num_cpus = "1.13.0" smallvec = "1.5.1" diff --git a/src/arg.rs b/src/arg.rs new file mode 100644 index 0000000..2de83fe --- /dev/null +++ b/src/arg.rs @@ -0,0 +1,65 @@ +use std::{fmt,error}; + +/// Print help message then exit with code `2`. +pub fn usage() -> ! +{ + eprintln!("uasge: {} ", std::env::args().next().unwrap()); + + eprintln!(); + eprintln!("mapcat v{}: mmap() based file concatenation", env!("CARGO_PKG_VERSION")); + eprintln!(" by {} with <3 (licensed GPL3+)", env!("CARGO_PKG_AUTHORS")); + eprintln!(); + + eprintln!("NOTES:"); + eprintln!("The output file is required, if one is not provided, we will just print this message and exit with error code `2`. If there are no input files, it will just create an empty output and return with error code `0` (unless there were other errors)"); + eprintln!("Output files are always clobbered regardless if there are any inputs, or if the concatenation operation succeeds / fails"); + eprintln!(); + eprintln!(" (compiled with write parallelisation enabled)"); //TODO: we will eventually feature gate the threaded scheduler based solution. but for now, it is the only one implemented so we'll just print this always + + std::process::exit(2) +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Operation +{ + pub output: String, + pub inputs: Vec, +} + +/// Parse the arguments passed to the process +#[inline] pub fn parse_args() -> Result +{ + parse(std::env::args().skip(1)) +} + +fn parse, I: IntoIterator>(args: I) -> Result +{ + let mut args = args.into_iter().map(Into::into); + + let output = args.next().ok_or(ArgParseError::NoOutput)?; + + Ok( + Operation { + output, + inputs: args.collect(), + } + ) +} + +#[derive(Debug)] +pub enum ArgParseError +{ + NoOutput, +} + +impl error::Error for ArgParseError{} +impl fmt::Display for ArgParseError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self { + Self::NoOutput => write!(f, "output file was not provided"), + } + } +} + diff --git a/src/main.rs b/src/main.rs index 0333241..c7b5f29 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,33 +1,38 @@ #![allow(dead_code)] + +#[macro_export] macro_rules! unwrap { + (return $($code:expr)?; $err:expr) => { + { + match $err { + Ok(v) => v, + Err(e) => { + eprintln!("Error: {}", e); + + #[allow(unused_variables)] + let rc = -1; + $(let rc = $code;)? + + std::process::exit(rc) + }, + } + } + }; +} + mod state; mod pool; mod map; mod job; mod work; +mod arg; mod consts; -fn usage() -> ! -{ - eprintln!("uasge: {} ", std::env::args().next().unwrap()); - - eprintln!(); - eprintln!("mapcat v{}: mmap() based file concatenation", env!("CARGO_PKG_VERSION")); - eprintln!(" by {} with <3 (licensed GPL3+)", env!("CARGO_PKG_AUTHORS")); - eprintln!(); - - eprintln!("NOTES:"); - eprintln!("The output file is required, if one is not provided, we will just exit with error code `2`. If there are no input files, it will just create an empty output and return with error code `0` (unless there were other errors)"); - eprintln!("Output files are always clobbered regardless if there are any inputs, or if the concatenation operation succeeds / fails"); - eprintln!(); - eprintln!(" (compiled with write parallelisation enabled)"); //TODO: we will eventually feature gate the threaded scheduler based solution. but for now, it is the only one implemented so we'll just print this always - - std::process::exit(0) -} -fn main() { - usage() + +fn work(arg::Operation{output, inputs}: arg::Operation) -> Result<(), Box> +{ // todo: // - create and open output file // - open and stat all input files in order (`job::create_from_file`). @@ -37,8 +42,18 @@ fn main() { // - spawn the task thread pool // - move the output mapped file to the thread-safe refcounted `state::State`. // - dispatch jobs to the pool with their fds, stats, and calculated output offsets; along with a reference to the output mapped file and a sender for the completion stream (`job::Prelude::start`) - + // - Read the completion stream receiver until all file jobs have been signaled as completed // - wait on all worker threads to complete. // - ensure all data was written. + + Ok(()) +} + +fn main() { + match arg::parse_args() { + Ok(op) => unwrap!(return 1; work(op)), + Err(arg::ArgParseError::NoOutput) => arg::usage(), + } + }