start working on a cli app #7

Merged
schrottkatze merged 12 commits from :app into main 2024-01-20 19:09:52 +00:00
4 changed files with 73 additions and 54 deletions
Showing only changes of commit 7c9dca0ae2 - Show all commits

View file

@ -1,6 +1,10 @@
use clap::Parser;
use self::{cli::Args, config_file::Configs};
use self::{
cli::Args,
config_file::{find_config_file, Configs},
error::ConfigError,
};
mod cli;
mod config_file;
@ -11,14 +15,49 @@ pub struct Config {
}
impl Config {
pub fn read() -> Self {
pub fn read() -> Result<Self, ConfigError> {
let args = Args::parse();
let cfg = Configs::read(args.config_file);
let config_path = if let Some(config_path) = args.config_path {
config_path
} else {
find_config_file()?
};
let file_config = Configs::read(config_path)?;
Self {
Ok(Self {
// this is negated because to an outward api, the negative is more intuitive,
// while in the source the other way around is more intuitive
startup_msg: !(args.no_startup_message || cfg.no_startup_message),
startup_msg: !(args.no_startup_message || file_config.no_startup_message),
})
}
}
pub mod error {
#[derive(Debug)]
pub enum ConfigError {
NoConfigDir,
NoConfigFileFound,
IoError(std::io::Error),
UnknownExtension(Option<String>),
SerdeJsonError(serde_json::Error),
SerdeRonError(ron::error::SpannedError),
}
multisamplednight marked this conversation as resolved Outdated

For later, not now: Should use proper error handling.

For later, not now: Should use proper error handling.

put that on the non-existing todo list for when we have a proper error handling and reporting system

put that on the non-existing todo list for when we have a proper error handling and reporting system
impl From<std::io::Error> for ConfigError {
fn from(value: std::io::Error) -> Self {
Self::IoError(value)
}
}
impl From<serde_json::Error> for ConfigError {
fn from(value: serde_json::Error) -> Self {
Self::SerdeJsonError(value)
}
}
impl From<ron::error::SpannedError> for ConfigError {
fn from(value: ron::error::SpannedError) -> Self {
Self::SerdeRonError(value)
}
}
}

View file

@ -6,7 +6,7 @@ use clap::Parser;
pub(crate) struct Args {
/// Read this config file.
#[arg(short, long)]
pub config_file: Option<PathBuf>,
pub config_path: Option<PathBuf>,
#[arg(long, env = "NO_STARTUP_MESSAGE", default_value = "false")]
schrottkatze marked this conversation as resolved Outdated
Might want a [`clap::builder::BoolishValueParser`](https://docs.rs/clap/latest/clap/builder/struct.BoolishValueParser.html) here.

How do I use that with claps derive api?

How do I use that with claps derive api?

Through

-    #[arg(long, env = "NO_STARTUP_MESSAGE", default_value = "false")] 
+    #[arg(long, env = "NO_STARTUP_MESSAGE", action = ArgAction::SetTrue, value_parser = BoolishValueParser::new())]
Through ```diff - #[arg(long, env = "NO_STARTUP_MESSAGE", default_value = "false")] + #[arg(long, env = "NO_STARTUP_MESSAGE", action = ArgAction::SetTrue, value_parser = BoolishValueParser::new())] ```
pub no_startup_message: bool,
}

View file

@ -1,12 +1,11 @@
use std::{
fs,
path::{Path, PathBuf},
process,
};
use serde::{Deserialize, Serialize};
use crate::error_reporting::{report_serde_json_err, report_serde_ron_err};
use super::error::ConfigError;
#[derive(Debug, Serialize, Deserialize)]
pub struct Configs {
@ -17,59 +16,37 @@ pub struct Configs {
}
/// what the fuck serde why do i need this
pub(crate) fn default_example_value() -> i32 {
fn default_example_value() -> i32 {
43
}
pub(crate) fn default_no_startup_msg() -> bool {
fn default_no_startup_msg() -> bool {
false
}
pub(super) fn find_config_file() -> Result<PathBuf, ConfigError> {
let Some(config_path) = dirs::config_dir() else {
return Err(ConfigError::NoConfigDir);
};
let ron_path = config_path.with_file_name("config.ron");
let json_path = config_path.with_file_name("config.json");
if Path::new(&ron_path).exists() {
Ok(ron_path)
} else if Path::new(&json_path).exists() {
Ok(json_path)
} else {
Err(ConfigError::NoConfigFileFound)
}
}
impl Configs {
pub fn read(custom_path: Option<PathBuf>) -> Self {
use owo_colors::OwoColorize;
let p = match custom_path {
Some(p) => p,
None => {
let config_path = dirs::config_dir().expect("config dir should exist");
let ron_path = config_path.with_file_name("config.ron");
let json_path = config_path.with_file_name("config.json");
if Path::new(&ron_path).exists() {
ron_path
} else if Path::new(&json_path).exists() {
json_path
} else {
eprintln!("{}: couldn't find config file", "Fatal error".red());
process::exit(1)
}
}
};
pub fn read(p: PathBuf) -> Result<Self, ConfigError> {
match p.extension().map(|v| v.to_str().unwrap()) {
Some("ron") => Self::read_json(&fs::read_to_string(p).unwrap()),
Some("json") => Self::read_ron(&fs::read_to_string(p).unwrap()),
None | Some(_) => {
eprintln!(
"{}: couldn't determine config file type",
"Fatal error".red()
);
process::exit(1)
}
}
}
pub fn read_json(config_text: &str) -> Configs {
match serde_json::from_str(config_text) {
Ok(c) => c,
Err(e) => report_serde_json_err(config_text, e),
}
}
pub fn read_ron(config_text: &str) -> Configs {
match ron::from_str(config_text) {
Ok(c) => c,
Err(e) => report_serde_ron_err(config_text, e),
Some("ron") => Ok(serde_json::from_str(&fs::read_to_string(p)?)?),
Some("json") => Ok(ron::from_str(&fs::read_to_string(p)?)?),
e => Err(ConfigError::UnknownExtension(e.map(|v| v.to_owned()))),
}
}
}

View file

@ -2,11 +2,14 @@ use config::Config;
use welcome_msg::print_startup_msg;
mod config;
#[allow(unused)]
mod error_reporting;
mod welcome_msg;
fn main() {
let cfg = Config::read();
// TODO: proper error handling
let cfg = Config::read().unwrap();
if cfg.startup_msg {
multisamplednight marked this conversation as resolved Outdated

Might want to streamline those two, in order to avoid accidentally checking only one of them in future.

The figment crate might be worth looking at in that case.

Might want to streamline those two, in order to avoid accidentally checking only one of them in future. The `figment` crate might be worth looking at in that case.

i'd prefer to try at least to some degree reduce dependencies, but i'll work on something like that then

i'd prefer to try at least to some degree reduce dependencies, but i'll work on something like that then

You don't need to use figment. Custom merge logic works fine, too.

You don't _need_ to use `figment`. Custom merge logic works fine, too.
print_startup_msg();