diff --git a/Cargo.lock b/Cargo.lock index f64e37e..aaced43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,6 +121,8 @@ dependencies = [ "clap", "env_logger", "log", + "serde", + "serde_yaml_ng", ] [[package]] @@ -146,6 +148,12 @@ dependencies = [ "log", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.14" @@ -156,18 +164,40 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "indexmap" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "jiff" version = "0.2.15" @@ -297,6 +327,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + [[package]] name = "serde" version = "1.0.228" @@ -304,6 +340,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", + "serde_derive", ] [[package]] @@ -326,6 +363,19 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_yaml_ng" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4db627b98b36d4203a7b458cf3573730f2bb591b28871d916dfa9efabfd41f" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "strsim" version = "0.11.1" @@ -359,6 +409,12 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "utf8parse" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index 7715434..3b86cd5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,5 @@ edition = "2024" clap = { version = "4.5.50", features = ["derive", "wrap_help"] } env_logger = "0.11.8" log = "0.4.28" +serde = { version = "1.0.228", features = ["derive"] } +serde_yaml_ng = "0.10.0" diff --git a/README.md b/README.md new file mode 100644 index 0000000..7b266a2 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# csm - Checkmk synthetic monitoring + +(Under active development, not yet usable.) + +## Configuration: `~/.csmrc` + +You can optionally create a file, `~/.csmrc` (`%UserProfile%\.csmrc` on Windows) +to override certain defaults. This is a YAML file with the following keys +available: + +* `mamba_root_prefix` - A string which sets where the Mamba environment(s) will + be created on disk. By default, this is left up to `micromamba` and its + default root prefix is used. diff --git a/src/csmrc.rs b/src/csmrc.rs new file mode 100644 index 0000000..09e2dbd --- /dev/null +++ b/src/csmrc.rs @@ -0,0 +1,37 @@ +/// Module for reading a user's ~/.csmrc, if it exists. +use crate::util; + +use log::debug; +use serde::Deserialize; +use std::default::Default; +use std::io::{Error, ErrorKind}; +use std::path::PathBuf; + +#[derive(Debug, Default, Deserialize)] +pub struct Config { + /// Override the $MAMBA_ROOT_PREFIX when shelling out to micromamba. + #[serde(default)] + #[allow(dead_code)] + pub mamba_root_prefix: Option, +} + +impl Config { + /// Read the user's ~/.csmrc if it exists, merging with the Default instance for + /// Config. Return Err if a config file was found but failed to parse, otherwise + /// Ok with the result of merging the config file values with the Default (and + /// simply the Default if no config file exists). + pub fn from_csmrc() -> Result { + let Ok(home) = util::homedir() else { + return Ok(Self::default()); + }; + let csmrc_path = PathBuf::from(home).join(".csmrc"); + let Ok(csmrc_data) = std::fs::read_to_string(csmrc_path) else { + debug!("No .csmrc found, using defaults"); + return Ok(Config::default()); + }; + let config = + serde_yaml_ng::from_str(&csmrc_data).map_err(|e| Error::new(ErrorKind::InvalidData, e)); + debug!("config: {:?}", config); + config + } +} diff --git a/src/env.rs b/src/env.rs index aad9b99..42454ba 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,3 +1,5 @@ +use crate::csmrc::Config; + #[derive(Debug, clap::Subcommand)] pub enum Subcommand { /// Create an environment @@ -27,6 +29,7 @@ pub struct CreateArgs { name: Option, } -pub fn run(subcommand: Subcommand) { +pub fn run(config: Config, subcommand: Subcommand) { + println!("{:?}", config); println!("{:?}", subcommand); } diff --git a/src/main.rs b/src/main.rs index f2b104d..905a60c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,10 @@ +mod csmrc; mod env; mod robot; +mod util; use clap::{Parser, Subcommand}; -use log::{LevelFilter, debug}; +use log::{LevelFilter, debug, error}; use std::fs::File; use std::io::Write; use std::path::PathBuf; @@ -29,7 +31,7 @@ enum Command { Robot(robot::Subcommand), } -fn main() { +fn main() -> Result<(), std::io::Error> { let cli = Cli::parse(); // Set up logging @@ -41,15 +43,26 @@ fn main() { let mut env_logger_builder = env_logger::Builder::new(); env_logger_builder.filter_level(default_verbosity); env_logger_builder.parse_default_env(); + env_logger_builder.format_timestamp(None); env_logger_builder.init(); let _ = create_mambarc(); + let config = match csmrc::Config::from_csmrc() { + Ok(config) => config, + Err(err) => { + error!("Failed to parse .csmrc: {}", err); + panic!("Failed to parse .csmrc as valid YAML"); + } + }; match cli.command { - Command::Env(sub) => env::run(sub), - Command::Robot(sub) => robot::run(sub), + Command::Env(sub) => env::run(config, sub), + Command::Robot(sub) => robot::run(config, sub), } + Ok(()) } +/// Create a ~/.mambarc (%UserProfile%\.mambarc on Windows) if it does not +/// exist. fn create_mambarc() -> std::io::Result<()> { let mambarc = r#" # Show the active environment in the shell prompt @@ -64,9 +77,7 @@ changeps1: True # ssl_verify: mycorpcert.crt # ssl_no_revoke: true "#; - let home = std::env::var("HOME") - .or_else(|_| std::env::var("USERPROFILE")) - .expect("Cannot determine home directory"); + let home = util::homedir().expect("Cannot determine home directory"); let mambarc_path = PathBuf::from(home).join(".mambarc"); match File::create_new(&mambarc_path) { Ok(mut file) => file.write_all(mambarc.trim_start().as_bytes())?, diff --git a/src/robot.rs b/src/robot.rs index 6dbfc3b..e3c81af 100644 --- a/src/robot.rs +++ b/src/robot.rs @@ -1,3 +1,5 @@ +use crate::csmrc::Config; + #[derive(Debug, clap::Subcommand)] pub enum Subcommand { /// Create a Robotmk robot @@ -13,6 +15,7 @@ pub struct CreateArgs { path: String, } -pub fn run(subcommand: Subcommand) { +pub fn run(config: Config, subcommand: Subcommand) { + println!("{:?}", config); println!("{:?}", subcommand); } diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..305ee12 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,5 @@ +use std::env; + +pub fn homedir() -> Result { + env::var("HOME").or_else(|_| env::var("USERPROFILE")) +}