From ee08ffb28d465cfb7021d42a605504d2388ec026 Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Sun, 8 Dec 2024 13:01:58 +0100 Subject: [PATCH] Create the `fuga` package --- Cargo.toml | 2 +- tools/fuga/Cargo.toml | 16 +++++++++++++ tools/fuga/src/bin/fuga.rs | 30 ++++++++++++++++++++++++ tools/fuga/src/color.rs | 21 +++++++++++++++++ tools/fuga/src/lib.rs | 6 +++++ tools/fuga/src/logger.rs | 47 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 tools/fuga/Cargo.toml create mode 100644 tools/fuga/src/bin/fuga.rs create mode 100644 tools/fuga/src/color.rs create mode 100644 tools/fuga/src/lib.rs create mode 100644 tools/fuga/src/logger.rs diff --git a/Cargo.toml b/Cargo.toml index 4783b49..f8861c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["onihime"] +members = ["onihime", "tools/*"] [workspace.package] authors = ["Jesse Braham "] diff --git a/tools/fuga/Cargo.toml b/tools/fuga/Cargo.toml new file mode 100644 index 0000000..3ab7422 --- /dev/null +++ b/tools/fuga/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "fuga" +version = "0.0.0" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true + +[[bin]] +name = "fuga" +path = "src/bin/fuga.rs" + +[dependencies] +clap = { version = "4.5.21", features = ["derive", "wrap_help"] } +log = { version = "0.4.22", features = ["std"] } diff --git a/tools/fuga/src/bin/fuga.rs b/tools/fuga/src/bin/fuga.rs new file mode 100644 index 0000000..57a6929 --- /dev/null +++ b/tools/fuga/src/bin/fuga.rs @@ -0,0 +1,30 @@ +use clap::{ + builder::{styling::Style, Styles}, + Parser, + Subcommand, +}; +use fuga::{color, command, AppResult}; + +const HEADER_STYLE: Style = Style::new().fg_color(Some(color::RED)).bold().underline(); +const LITERAL_STYLE: Style = Style::new().fg_color(Some(color::PURPLE)).bold(); + +const STYLES: Styles = Styles::styled() + .usage(HEADER_STYLE) + .header(HEADER_STYLE) + .literal(LITERAL_STYLE); + +#[derive(Debug, Parser)] +#[command(styles = STYLES, version)] +struct Cli { + #[command(subcommand)] + command: Command, +} + +#[derive(Debug, Subcommand)] +enum Command {} + +fn main() -> AppResult<()> { + fuga::logger::init()?; + + match Cli::parse().command {} +} diff --git a/tools/fuga/src/color.rs b/tools/fuga/src/color.rs new file mode 100644 index 0000000..8908bd0 --- /dev/null +++ b/tools/fuga/src/color.rs @@ -0,0 +1,21 @@ +pub use clap::builder::styling::Reset; +use clap::builder::styling::{Color, RgbColor}; + +pub const RED: Color = Color::Rgb(RgbColor(225, 55, 55)); // Red +pub const ORANGE: Color = Color::Rgb(RgbColor(215, 140, 100)); // Orange +pub const WHITE: Color = Color::Rgb(RgbColor(255, 255, 255)); // White +pub const BLUE: Color = Color::Rgb(RgbColor(60, 140, 185)); // Blue +pub const PURPLE: Color = Color::Rgb(RgbColor(180, 130, 215)); // Purple + +pub trait EscapeSequence { + fn to_escape_sequence(&self) -> String; +} + +impl EscapeSequence for Color { + fn to_escape_sequence(&self) -> String { + match self { + Color::Rgb(RgbColor(r, g, b)) => format!("\x1b[1;38;2;{r};{g};{b}m"), + _ => unimplemented!(), + } + } +} diff --git a/tools/fuga/src/lib.rs b/tools/fuga/src/lib.rs new file mode 100644 index 0000000..d70b93a --- /dev/null +++ b/tools/fuga/src/lib.rs @@ -0,0 +1,6 @@ +#![deny(rust_2018_idioms, unsafe_code)] + +pub mod color; +pub mod logger; + +pub type AppResult = std::result::Result>; diff --git a/tools/fuga/src/logger.rs b/tools/fuga/src/logger.rs new file mode 100644 index 0000000..5c8a41d --- /dev/null +++ b/tools/fuga/src/logger.rs @@ -0,0 +1,47 @@ +use std::str::FromStr as _; + +use crate::color::{self, EscapeSequence as _}; + +struct FugaLogger { + level: log::LevelFilter, +} + +impl log::Log for FugaLogger { + fn enabled(&self, metadata: &log::Metadata<'_>) -> bool { + metadata.level() <= self.level + } + + fn log(&self, record: &log::Record<'_>) { + if self.enabled(record.metadata()) { + let style = match record.level() { + log::Level::Error => color::RED.to_escape_sequence(), + log::Level::Warn => color::ORANGE.to_escape_sequence(), + log::Level::Info => color::WHITE.to_escape_sequence(), + log::Level::Debug => color::BLUE.to_escape_sequence(), + log::Level::Trace => color::PURPLE.to_escape_sequence(), + }; + + eprintln!( + "{style}{: <5}{} {}", + record.level(), + color::Reset.render(), + record.args() + ); + } + } + + fn flush(&self) {} +} + +pub fn init() -> Result<(), log::SetLoggerError> { + let level = if let Some(level) = std::option_env!("FUGA_LOG") { + log::LevelFilter::from_str(level).unwrap_or(log::LevelFilter::Off) + } else { + log::LevelFilter::Info + }; + + let logger = FugaLogger { level }; + log::set_boxed_logger(Box::new(logger)).map(|()| log::set_max_level(level))?; + + Ok(()) +}