diff --git a/.gitignore b/.gitignore index ab951f8..620f8f6 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,8 @@ Cargo.lock # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + + +# Added by cargo + +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b6e3b22 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "motdgen" +version = "0.1.0" +edition = "2024" + +[dependencies] +rand = "0.10.1" +serde = { version = "1.0.228", features = ["derive"] } +toml = "1.1.2" + +[profile.release] +lto = true +codegen-units = 1 +strip = true \ No newline at end of file diff --git a/src/ansi.rs b/src/ansi.rs new file mode 100644 index 0000000..b13597f --- /dev/null +++ b/src/ansi.rs @@ -0,0 +1,9 @@ +pub const RESET: &str = "\x1b[0m"; + +pub fn fg([r, g, b]: [u8; 3]) -> String { + format!("\x1b[38;2;{r};{g};{b}m") +} + +pub fn bg([r, g, b]: [u8; 3]) -> String { + format!("\x1b[48;2;{r};{g};{b}m") +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..d6525f7 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,25 @@ +use serde::Deserialize; +use std::path::PathBuf; + +#[derive(Debug, Deserialize)] +pub struct Config { + pub description: String, + pub ascii_art: String, + pub output: PathBuf, + pub colors: Colors, +} + +#[derive(Debug, Deserialize)] +pub struct Colors { + pub background: [u8; 3], + pub description: [u8; 3], + pub random_art: RandomColor, +} + +#[derive(Debug, Deserialize)] +pub struct RandomColor { + pub enabled: bool, + pub r: [u8; 2], + pub g: [u8; 2], + pub b: [u8; 2], +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..31e1d3c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,64 @@ +mod ansi; +mod config; + +use ansi::{RESET, bg, fg}; +use config::{Config, RandomColor}; + +use rand::RngExt; +use std::{fmt::Write, fs, process}; + +const CONFIG: &str = "/etc/motdgen/config.toml"; + +fn random_rgb(cfg: &RandomColor) -> [u8; 3] { + let mut rng = rand::rng(); + + [ + rng.random_range(cfg.r[0]..=cfg.r[1]), + rng.random_range(cfg.g[0]..=cfg.g[1]), + rng.random_range(cfg.b[0]..=cfg.b[1]), + ] +} + +fn load_config() -> Result> { + let raw = fs::read_to_string(CONFIG)?; + Ok(toml::from_str(&raw)?) +} + +fn build_motd(cfg: &Config) -> String { + let art_fg = if cfg.colors.random_art.enabled { + fg(random_rgb(&cfg.colors.random_art)) + } else { + fg(cfg.colors.description) + }; + + let art_bg = bg(cfg.colors.background); + let desc_fg = fg(cfg.colors.description); + + let mut out = String::new(); + + for line in cfg.ascii_art.lines() { + let _ = writeln!(out, "{art_fg}{art_bg} {line} {RESET}"); + } + + out.push_str(&desc_fg); + out.push_str(&cfg.description); + out.push_str(RESET); + out.push('\n'); + + out +} + +fn main() { + let cfg = match load_config() { + Ok(c) => c, + Err(e) => { + eprintln!("config error: {e}"); + process::exit(1); + } + }; + + if let Err(e) = fs::write(&cfg.output, build_motd(&cfg)) { + eprintln!("write failed: {e}"); + process::exit(1); + } +}