diff --git a/Cargo.lock b/Cargo.lock index 9423e02..c2f6328 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,15 +62,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" -[[package]] -name = "c-main" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "797bbff8bd2bcddb7f0ee638b55398686adac15174689a86da5ffc0f51219f75" -dependencies = [ - "libc", -] - [[package]] name = "cc" version = "1.0.79" @@ -300,7 +291,6 @@ name = "hopper" version = "0.1.0" dependencies = [ "arg", - "c-main", "console", "curl", "dialoguer", @@ -380,6 +370,15 @@ version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +[[package]] +name = "libc-print" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06cea5d58bd9ba4717bbf5c6c5bb11bb6e9e76685b7fff34039b80f50ce86c11" +dependencies = [ + "libc", +] + [[package]] name = "libloading" version = "0.7.4" @@ -551,9 +550,9 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534" dependencies = [ "unicode-ident", ] @@ -1010,16 +1009,16 @@ dependencies = [ [[package]] name = "yacexits" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe740dd05c1bbc919431e842e6c1bea30195e0518ae99cae35b7f0730ddc18" +version = "0.2.0" +source = "git+https://git.tebibyte.media/yac/yacexits.git?branch=c-entry#d2af983f4ad18dec2af0604d7f34b7863daa5c25" dependencies = [ "bindgen", "libc", + "libc-print", ] [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/Cargo.toml b/Cargo.toml index 225a24b..baca1d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ authors = [ [dependencies] arg = "0.4.1" -c-main = "1.0.1" console = "0.15.0" curl = "0.4.44" dialoguer = "0.9.0" @@ -23,4 +22,4 @@ serde_json = "1" tokio = { version = "1", features = ["full"] } toml = "0.7.3" xdg = "2.4.1" -yacexits = "0.1.3" +yacexits = { git = "https://git.tebibyte.media/yac/yacexits.git", branch = "c-entry", version = "0.2.0" } diff --git a/src/config.rs b/src/config.rs index 2c77a21..265143b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -19,9 +19,11 @@ * Hopper. If not, see . */ +use crate::HopError; + use std::{ fs::File, - io::{Read, self}, + io::{ Read, self }, path::PathBuf, }; @@ -32,8 +34,6 @@ use yacexits::{ EX_UNAVAILABLE, }; -use crate::error::CError; - #[derive(Deserialize)] pub struct Config { pub hopfiles: Vec, @@ -46,52 +46,103 @@ pub struct Sources { } pub enum ConfigError { - CreateFailed(io::Error), + CreateError(io::Error), OpenError(io::Error), ReadError(io::Error), FormatError(std::string::FromUtf8Error), ParseError(toml::de::Error), } -impl CError for ConfigError { - fn message(&self) -> String { - match self { - Self::CreateFailed(err) => { - format!("Unable to create configuration file: {}", err) +impl From for HopError { + fn from(error: ConfigError) -> Self { + let (message, code) = match error { + ConfigError::CreateError(err) => { + ( + format!("{}: Unable to create configuration file.", err), + EX_UNAVAILABLE, + ) }, - Self::OpenError(err) => { - format!("Unable to open configuration file: {}", err) + ConfigError::OpenError(err) => { + ( + format!("{}: Unable to open configuration file.", err), + EX_UNAVAILABLE, + ) }, - Self::ReadError(err) => { - format!("Error while reading configuration file: {}", err) + ConfigError::ReadError(err) => { + ( + format!("{}: Error while reading configuration file", err), + EX_DATAERR, + ) }, - Self::FormatError(err) => { - format!("Configuration file is not valid utf-8: {}", err) + ConfigError::FormatError(err) => { + ( + format!("{}: Configuration file is not valid UTF-8.", err), + EX_DATAERR + ) }, - Self::ParseError(err) => { - format!("Unable to parse configuration file: {}", err) + ConfigError::ParseError(err) => { + ( + format!("{}: Unable to parse configuration file.", err), + EX_DATAERR, + ) }, - } - } + }; - fn code(&self) -> u32 { - match self { - Self::CreateFailed(_) => EX_UNAVAILABLE, - Self::OpenError(_) => EX_UNAVAILABLE, - Self::ReadError(_) => EX_DATAERR, - Self::FormatError(_) => EX_DATAERR, - Self::ParseError(_) => EX_DATAERR, - } - + Self { message, code } } } -pub fn get_config(dirs: BaseDirectories) -> Result { - dirs.place_config_file("config.toml").map_err(ConfigError::CreateFailed) +impl From for (String, u32) { + fn from(error: ConfigError) -> Self { + let (message, code) = match error { + ConfigError::CreateError(err) => { + ( + format!("{}: Unable to create configuration file.", err), + EX_UNAVAILABLE, + ) + }, + ConfigError::OpenError(err) => { + ( + format!("{}: Unable to open configuration file.", err), + EX_UNAVAILABLE, + ) + }, + ConfigError::ReadError(err) => { + ( + format!("{}: Error while reading configuration file", err), + EX_DATAERR, + ) + }, + ConfigError::FormatError(err) => { + ( + format!("{}: Configuration file is not valid UTF-8.", err), + EX_DATAERR + ) + }, + ConfigError::ParseError(err) => { + ( + format!("{}: Unable to parse configuration file", err), + EX_DATAERR, + ) + }, + }; + + (message, code) + } + +} + +impl From for ConfigError { + fn from(err: xdg::BaseDirectoriesError) -> Self { + ConfigError::CreateError(io::Error::from(err)) + } } impl Config { - pub fn read_config(config_path: PathBuf) -> Result { + pub fn read_config() -> Result { + let config_path = BaseDirectories::with_prefix("hopper")? + .place_config_file("config.toml") + .map_err(ConfigError::CreateError)?; let mut buf: Vec = Vec::new(); let mut config_file = File::open(&config_path) diff --git a/src/error.rs b/src/error.rs index 751a146..9e59fef 100644 --- a/src/error.rs +++ b/src/error.rs @@ -18,48 +18,38 @@ use yacexits::*; -pub trait CError { - fn code(&self) -> u32; +pub struct HopError { + pub code: u32, - fn message(&self) -> String; - - fn exit(&self) -> ! { - eprintln!("{}: {}", program_invokation(), self.message()); - exit(self.code()); - } + pub message: String, } -fn program_invokation() -> String { - // TODO: ideally this would be argv[0] from main. - // This could be done with a const OnceCell, but I'm not sure I like that solution. - // Using std, we can do this though: - std::env::args().next() - // with a fallback to the program name - .unwrap_or_else(|| env!("CARGO_PKG_NAME").to_owned()) -} - -impl<'l> CError for arg::ParseKind<'l> { - fn message(&self) -> String { - format!( - "Usage: {}{}", - program_invokation(), // argv[0], - " [-v] add | get | init | list | remove | update\n\n".to_owned() + +impl From> for HopError { + fn from(_: arg::ParseKind) -> Self { + let message = format!( + "Usage: {}", + "[-v] add | get | init | list | remove | update\n\n".to_owned() + "add [-m version] [-f hopfiles...] packages...\n" + "get [-n] [-d directory] [-m versions...] [-t types...] packages\n" + "init [-f hopfiles...] version type\n" + "list [[-f hopfiles...] | [-m versions...] [-t types...]]\n" + "remove [[-f hopfiles...] | type version]] packages...\n" + "update [[-f hopfiles... | [-m versions...] [-t types...]]", - ) + ); + Self { message, code: EX_USAGE } } - - fn code(&self) -> u32 { EX_USAGE } } -impl CError for xdg::BaseDirectoriesError { - fn message(&self) -> String { - format!("Unable to open configuration file: {}", self) - } +impl From for HopError { + fn from(err: xdg::BaseDirectoriesError) -> Self { + let message = format!("{}: Unable to open configuration file", err); - fn code(&self) -> u32 { EX_UNAVAILABLE } -} \ No newline at end of file + Self { message, code: EX_UNAVAILABLE } + } +} + +impl From for (String, u32) { + fn from(err: HopError) -> Self { + (err.message, err.code) + } +} diff --git a/src/main.rs b/src/main.rs index 7c45e19..79d3a1e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,7 +36,8 @@ use hopfile::*; use error::*; use yacexits::{ - exit, + exit, + EX_OSERR, EX_SOFTWARE, }; @@ -47,34 +48,30 @@ struct AppContext { #[tokio::main] #[no_mangle] -async fn rust_main(arguments: c_main::Args) { +async fn rust_main(arguments: yacexits::Args) -> Result { let argv: Vec<&str> = arguments.into_iter().collect(); - let args = Arguments::from_args( - argv - .clone() - .into_iter() - ).unwrap_or_else(|e| e.exit()); + let args = match Arguments::from_args(argv.clone().into_iter()) { + Ok(args) => args, + Err(_) => { + return Err((format!("Unable to ascertain arguments."), EX_OSERR)); + } + }; - let xdg_basedirs = xdg::BaseDirectories::with_prefix("hopper") - .unwrap_or_else(|e| e.exit()); - - let config = get_config(xdg_basedirs) - .and_then(Config::read_config) - .unwrap_or_else(|e| e.exit()); - + let config = Config::read_config()?; let ctx = AppContext { args, config }; match ctx.args.sub { // Command::Get(search_args) => cmd_get(&ctx, search_args).await, // Command::Init(hopfile_args) => cmd_init(hopfile_args).await, _ => { - eprintln!( - "{}: {}: Unimplemented subcommand.", - argv[0], - ctx.args.sub - ); - exit(EX_SOFTWARE); + return Err(( + format!( + "{}: Unimplemented subcommand.", + ctx.args.sub + ), + EX_SOFTWARE, + )); }, }; }