From 9768d7ed66dbf058acee12cc358753cce0f314fd Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Mon, 4 Nov 2024 23:51:24 -0500 Subject: [PATCH 01/17] Colored Macro Logging Basic --- src/lib/derive_macros/Cargo.toml | 7 ++++- src/lib/derive_macros/src/net/packets/mod.rs | 32 +++++++++++++++----- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/lib/derive_macros/Cargo.toml b/src/lib/derive_macros/Cargo.toml index 9a30f5ba..66a4e34c 100644 --- a/src/lib/derive_macros/Cargo.toml +++ b/src/lib/derive_macros/Cargo.toml @@ -7,8 +7,13 @@ edition = "2021" proc-macro = true [dependencies] +colored = { version = "2.0" } +supports-color = { version = "2.0" } quote = { workspace = true } syn = { workspace = true, features = ["full"] } thiserror = { workspace = true } proc-macro2 = { workspace = true } -proc-macro-crate = { workspace = true } \ No newline at end of file +proc-macro-crate = { workspace = true } + +[features] +macro-color = [] diff --git a/src/lib/derive_macros/src/net/packets/mod.rs b/src/lib/derive_macros/src/net/packets/mod.rs index b0e39c33..1794d518 100644 --- a/src/lib/derive_macros/src/net/packets/mod.rs +++ b/src/lib/derive_macros/src/net/packets/mod.rs @@ -3,10 +3,14 @@ use quote::quote; use std::env; use std::ops::Add; use syn::{parse_macro_input, LitInt, LitStr}; +use colored::Colorize; /// Essentially, this just reads all the files in the directory and generates a match arm for each packet. /// (packet_id, state) => { ... } pub fn bake_registry(input: TokenStream) -> TokenStream { + #[cfg(feature = "macro-color")] + colored::control::set_override(true); + let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); let module_path = parse_macro_input!(input as syn::LitStr).value(); @@ -18,7 +22,11 @@ pub fn bake_registry(input: TokenStream) -> TokenStream { let base_path = module_path.split("\\").collect::>()[2..].join("::"); let base_path = format!("crate::{}", base_path); - println!("[FERRUMC_MACROS] Parsing packets in {}", dir_path.display()); + println!( + "{} {}", + "[FERRUMC_MACROS]".blue().bold(), + format!("Parsing packets in {}", dir_path.display()).white().bold() + ); if !std::fs::metadata(dir_path).unwrap().is_dir() { return TokenStream::from(quote! { @@ -90,17 +98,20 @@ pub fn bake_registry(input: TokenStream) -> TokenStream { let struct_name = &item_struct.ident; println!( - "[FERRUMC_MACROS] Found Packet (ID: 0x{:02X}, State: {}, Struct Name: {})", - packet_id, state, struct_name + "{} {} (ID: {}, State: {}, Struct Name: {})", + "[FERRUMC_MACROS]".bold().blue(), + "Found Packet".white().bold(), + "0x00".cyan(), + "status".green(), + "StatusRequestPacket".yellow() ); - + let path = format!( // "crate::net::packets::incoming::{}", "{}::{}", base_path, file_name.to_string_lossy().replace(".rs", "") ); - let struct_path = format!("{}::{}", path, struct_name); let struct_path = syn::parse_str::(&struct_path).expect("parse_str failed"); @@ -119,10 +130,15 @@ pub fn bake_registry(input: TokenStream) -> TokenStream { } let elapsed = start.elapsed(); - println!("[FERRUMC_MACROS] Found {} packets", match_arms.len()); println!( - "[FERRUMC_MACROS] It took: {:?} to parse all the files and generate the packet registry", - elapsed + "{} {}", + "[FERRUMC_MACROS]".bold().blue(), + format!("Found {} packets", match_arms.len()).purple().bold() + ); + println!( + "{} {}", + "[FERRUMC_MACROS]".bold().blue(), + format!("It took: {:?} to parse all the files and generate the packet registry", elapsed).red().bold() ); let match_arms = match_arms.into_iter(); From 91d0c393c16bc0621bfb588fe560e241d4d6565f Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Mon, 4 Nov 2024 23:55:58 -0500 Subject: [PATCH 02/17] Indent Logs --- src/lib/derive_macros/src/net/packets/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/derive_macros/src/net/packets/mod.rs b/src/lib/derive_macros/src/net/packets/mod.rs index 1794d518..42a58381 100644 --- a/src/lib/derive_macros/src/net/packets/mod.rs +++ b/src/lib/derive_macros/src/net/packets/mod.rs @@ -23,7 +23,7 @@ pub fn bake_registry(input: TokenStream) -> TokenStream { let base_path = format!("crate::{}", base_path); println!( - "{} {}", + " {} {}", "[FERRUMC_MACROS]".blue().bold(), format!("Parsing packets in {}", dir_path.display()).white().bold() ); @@ -98,7 +98,7 @@ pub fn bake_registry(input: TokenStream) -> TokenStream { let struct_name = &item_struct.ident; println!( - "{} {} (ID: {}, State: {}, Struct Name: {})", + " {} {} (ID: {}, State: {}, Struct Name: {})", "[FERRUMC_MACROS]".bold().blue(), "Found Packet".white().bold(), "0x00".cyan(), @@ -131,12 +131,12 @@ pub fn bake_registry(input: TokenStream) -> TokenStream { let elapsed = start.elapsed(); println!( - "{} {}", + " {} {}", "[FERRUMC_MACROS]".bold().blue(), format!("Found {} packets", match_arms.len()).purple().bold() ); println!( - "{} {}", + " {} {}", "[FERRUMC_MACROS]".bold().blue(), format!("It took: {:?} to parse all the files and generate the packet registry", elapsed).red().bold() ); From 9dce9991d59381b11464d1166d7738ddcc4953dd Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 10:01:53 -0500 Subject: [PATCH 03/17] Color Crate --- Cargo.toml | 2 +- src/lib/derive_macros/Cargo.toml | 3 +- src/lib/derive_macros/src/net/packets/mod.rs | 10 +- src/lib/utils/color/Cargo.toml | 7 ++ src/lib/utils/color/src/lib.rs | 125 +++++++++++++++++++ 5 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 src/lib/utils/color/Cargo.toml create mode 100644 src/lib/utils/color/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index f2b92861..8e385668 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ members = [ "src/lib/net/crates/codec", "src/lib/plugins", "src/lib/storage", - "src/lib/utils", "src/lib/utils/logging", "src/lib/utils/profiling", "src/lib/utils/general_purpose", + "src/lib/utils", "src/lib/utils/logging", "src/lib/utils/profiling", "src/lib/utils/general_purpose", "src/lib/utils/color", "src/lib/world", "src/lib/derive_macros", "src/lib/adapters/nbt", "src/lib/adapters/mca", diff --git a/src/lib/derive_macros/Cargo.toml b/src/lib/derive_macros/Cargo.toml index 66a4e34c..d33f2af6 100644 --- a/src/lib/derive_macros/Cargo.toml +++ b/src/lib/derive_macros/Cargo.toml @@ -8,7 +8,8 @@ proc-macro = true [dependencies] colored = { version = "2.0" } -supports-color = { version = "2.0" } +color = { path = "../utils/color" } + quote = { workspace = true } syn = { workspace = true, features = ["full"] } thiserror = { workspace = true } diff --git a/src/lib/derive_macros/src/net/packets/mod.rs b/src/lib/derive_macros/src/net/packets/mod.rs index 42a58381..4e356ebd 100644 --- a/src/lib/derive_macros/src/net/packets/mod.rs +++ b/src/lib/derive_macros/src/net/packets/mod.rs @@ -5,11 +5,17 @@ use std::ops::Add; use syn::{parse_macro_input, LitInt, LitStr}; use colored::Colorize; +use color::ColorSupport; + + /// Essentially, this just reads all the files in the directory and generates a match arm for each packet. /// (packet_id, state) => { ... } pub fn bake_registry(input: TokenStream) -> TokenStream { - #[cfg(feature = "macro-color")] - colored::control::set_override(true); + let color_support = ColorSupport::new(); + let color_bool = color_support.supports_color(); + if color_bool { + colored::control::set_override(true); + } let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); let module_path = parse_macro_input!(input as syn::LitStr).value(); diff --git a/src/lib/utils/color/Cargo.toml b/src/lib/utils/color/Cargo.toml new file mode 100644 index 00000000..0eb388ca --- /dev/null +++ b/src/lib/utils/color/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "color" +version = "0.1.0" +edition = "2021" + +[dependencies] +atty = "0.2.14" diff --git a/src/lib/utils/color/src/lib.rs b/src/lib/utils/color/src/lib.rs new file mode 100644 index 00000000..90b10a56 --- /dev/null +++ b/src/lib/utils/color/src/lib.rs @@ -0,0 +1,125 @@ +use std::env; +use atty::Stream; + +#[derive(Debug, PartialEq)] +pub enum ColorLevel { + None, + Basic, + Enhanced, + TrueColor, +} + +impl ColorLevel { + pub fn to_bool(&self) -> bool { + matches!(self, ColorLevel::Basic | ColorLevel::Enhanced | ColorLevel::TrueColor) + } + + pub fn new() -> Self { + Self::None + } + + pub fn basic(&self) -> Self { + match self { + ColorLevel::Enhanced => ColorLevel::Enhanced, + ColorLevel::TrueColor => ColorLevel::TrueColor, + _ => ColorLevel::Basic, + } + } + + pub fn enhanced(&self) -> Self { + match self { + ColorLevel::TrueColor => ColorLevel::TrueColor, + _ => ColorLevel::Enhanced, + } + } + + pub fn true_color(&self) -> Self { + Self::TrueColor + } + + pub fn force_none() -> Self { + Self::None + } +} +pub struct ColorSupport { + pub stdout: ColorLevel, + pub stderr: ColorLevel, +} + +impl ColorSupport { + pub fn new() -> Self { + Self { + stdout: determine_color_level(Stream::Stdout), + stderr: determine_color_level(Stream::Stderr), + } + } + + pub fn supports_color(&self) -> bool { + self.stdout.to_bool() || self.stderr.to_bool() + } +} + +fn determine_color_level(stream: Stream) -> ColorLevel { + // Check FORCE_COLOR environment variable first + let mut color_level = ColorLevel::new(); + + if let Ok(force_color) = env::var("FORCE_COLOR") { + match force_color.as_str() { + "0" => color_level = ColorLevel::force_none(), + "1" => color_level = ColorLevel::basic(&color_level), + "2" => color_level = ColorLevel::enhanced(&color_level), + "3" => color_level = ColorLevel::true_color(&color_level), + _ => (), + }; + if color_level == ColorLevel::None { + return color_level; + } + } + + // Handle specific CI environments + if env::var("CI").is_ok() { + if env::var("GITHUB_ACTIONS").is_ok() || env::var("GITEA_ACTIONS").is_ok() { + return ColorLevel::TrueColor; + } + + let ci_providers = ["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE", "codeship"]; + if ci_providers.iter().any(|ci| env::var(ci).is_ok()) { + return ColorLevel::Basic; + } + } + + // Check terminal types and other environment variables + match env::var("TERM").as_deref() { + Ok("dumb") => (), + Ok("xterm-kitty") | Ok("truecolor") | Ok("ansi") => color_level = ColorLevel::true_color(&color_level), + Ok(term) if term.ends_with("-256color") => color_level = ColorLevel::enhanced(&color_level), + Ok(term) if term.starts_with("xterm") || term.starts_with("screen") => color_level = ColorLevel::basic(&color_level), + _ => (), + } + + // Final fallback based on whether the stream is a TTY + if atty::is(stream) { + color_level = ColorLevel::basic(&color_level); + } + + color_level +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default_color_level() { + let color_support = ColorSupport::new(); + assert!(matches!(color_support.stdout, ColorLevel::Basic) || matches!(color_support.stdout, ColorLevel::None)); + assert!(matches!(color_support.stderr, ColorLevel::Basic) || matches!(color_support.stderr, ColorLevel::None)); + } + + #[test] + fn test_force_color_env() { + env::set_var("FORCE_COLOR", "3"); + assert_eq!(determine_color_level(Stream::Stdout), ColorLevel::TrueColor); + env::remove_var("FORCE_COLOR"); + } +} From 6c76cfe682486f6f3120d686602dd89c8b02a273 Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 10:04:28 -0500 Subject: [PATCH 04/17] Remove Old feature flag --- src/lib/derive_macros/Cargo.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/lib/derive_macros/Cargo.toml b/src/lib/derive_macros/Cargo.toml index d33f2af6..b818644f 100644 --- a/src/lib/derive_macros/Cargo.toml +++ b/src/lib/derive_macros/Cargo.toml @@ -14,7 +14,4 @@ quote = { workspace = true } syn = { workspace = true, features = ["full"] } thiserror = { workspace = true } proc-macro2 = { workspace = true } -proc-macro-crate = { workspace = true } - -[features] -macro-color = [] +proc-macro-crate = { workspace = true } \ No newline at end of file From b89bbd265cd1e9c1df74c3996f5e8a180690445d Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:38:37 -0500 Subject: [PATCH 05/17] TODO TESTS --- src/lib/utils/color/src/lib.rs | 65 +++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/src/lib/utils/color/src/lib.rs b/src/lib/utils/color/src/lib.rs index 90b10a56..c59f7715 100644 --- a/src/lib/utils/color/src/lib.rs +++ b/src/lib/utils/color/src/lib.rs @@ -18,29 +18,38 @@ impl ColorLevel { Self::None } - pub fn basic(&self) -> Self { - match self { - ColorLevel::Enhanced => ColorLevel::Enhanced, - ColorLevel::TrueColor => ColorLevel::TrueColor, - _ => ColorLevel::Basic, + pub fn update(self, new_level: Self) -> Self { + match (&self, new_level) { + (ColorLevel::None, level) => level, + (_, level) if level > self => level, + _ => self, } } - pub fn enhanced(&self) -> Self { - match self { - ColorLevel::TrueColor => ColorLevel::TrueColor, - _ => ColorLevel::Enhanced, - } - } - - pub fn true_color(&self) -> Self { - Self::TrueColor - } - pub fn force_none() -> Self { Self::None } } + +impl PartialOrd for ColorLevel { + fn partial_cmp(&self, other: &Self) -> Option { + Some(match (self, other) { + (Self::None, _) => std::cmp::Ordering::Less, + (_, Self::None) => std::cmp::Ordering::Greater, + (Self::Basic, Self::Basic) => std::cmp::Ordering::Equal, + (Self::Basic, Self::Enhanced) => std::cmp::Ordering::Less, + (Self::Basic, Self::TrueColor) => std::cmp::Ordering::Less, + (Self::Enhanced, Self::Basic) => std::cmp::Ordering::Greater, + (Self::Enhanced, Self::Enhanced) => std::cmp::Ordering::Equal, + (Self::Enhanced, Self::TrueColor) => std::cmp::Ordering::Less, + (Self::TrueColor, Self::Basic) => std::cmp::Ordering::Greater, + (Self::TrueColor, Self::Enhanced) => std::cmp::Ordering::Greater, + (Self::TrueColor, Self::TrueColor) => std::cmp::Ordering::Equal, + }) + } +} + + pub struct ColorSupport { pub stdout: ColorLevel, pub stderr: ColorLevel, @@ -60,16 +69,17 @@ impl ColorSupport { } fn determine_color_level(stream: Stream) -> ColorLevel { - // Check FORCE_COLOR environment variable first + let mut color_level = ColorLevel::new(); + // Check FORCE_COLOR environment variable first if let Ok(force_color) = env::var("FORCE_COLOR") { - match force_color.as_str() { - "0" => color_level = ColorLevel::force_none(), - "1" => color_level = ColorLevel::basic(&color_level), - "2" => color_level = ColorLevel::enhanced(&color_level), - "3" => color_level = ColorLevel::true_color(&color_level), - _ => (), + color_level = match force_color.as_str() { + "0" => ColorLevel::force_none(), + "1" => color_level.update(ColorLevel::Basic), + "2" => color_level.update(ColorLevel::Enhanced), + "3" => color_level.update(ColorLevel::TrueColor), + _ => color_level, }; if color_level == ColorLevel::None { return color_level; @@ -81,7 +91,6 @@ fn determine_color_level(stream: Stream) -> ColorLevel { if env::var("GITHUB_ACTIONS").is_ok() || env::var("GITEA_ACTIONS").is_ok() { return ColorLevel::TrueColor; } - let ci_providers = ["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE", "codeship"]; if ci_providers.iter().any(|ci| env::var(ci).is_ok()) { return ColorLevel::Basic; @@ -91,15 +100,15 @@ fn determine_color_level(stream: Stream) -> ColorLevel { // Check terminal types and other environment variables match env::var("TERM").as_deref() { Ok("dumb") => (), - Ok("xterm-kitty") | Ok("truecolor") | Ok("ansi") => color_level = ColorLevel::true_color(&color_level), - Ok(term) if term.ends_with("-256color") => color_level = ColorLevel::enhanced(&color_level), - Ok(term) if term.starts_with("xterm") || term.starts_with("screen") => color_level = ColorLevel::basic(&color_level), + Ok("xterm-kitty") | Ok("truecolor") | Ok("ansi") => color_level = color_level.update(ColorLevel::TrueColor), + Ok(term) if term.ends_with("-256color") => color_level = color_level.update(ColorLevel::Enhanced), + Ok(term) if term.starts_with("xterm") || term.starts_with("screen") => color_level = color_level.update(ColorLevel::Basic), _ => (), } // Final fallback based on whether the stream is a TTY if atty::is(stream) { - color_level = ColorLevel::basic(&color_level); + color_level = color_level.update(ColorLevel::Basic); } color_level From f3818745634ba6bd99968b938a1edf0df1fb4c4e Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:59:33 -0500 Subject: [PATCH 06/17] Fix for standard --- Cargo.toml | 3 +++ src/lib/derive_macros/Cargo.toml | 4 ++-- src/lib/derive_macros/src/net/packets/mod.rs | 2 +- src/lib/utils/color/Cargo.toml | 4 ++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8e385668..764915c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,6 +83,7 @@ ferrumc-profiling = { path = "src/lib/utils/profiling" } ferrumc-logging = { path = "src/lib/utils/logging" } ferrumc-config = { path = "src/lib/utils/config" } ferrumc-general-purpose = { path = "src/lib/utils/general_purpose" } +ferrumc-color = { path = "src/lib/utils/color" } ferrumc-macros = { path = "src/lib/derive_macros" } ferrumc-world = { path = "src/lib/world" } ferrumc-nbt = { path = "src/lib/adapters/nbt" } @@ -154,6 +155,8 @@ bzip2 = "0.4.1" # CLI clap = "4.5.20" indicatif = "0.17.8" +colored = "2.1.0" +atty = "0.2.14" # Misc deepsize = "0.2.0" diff --git a/src/lib/derive_macros/Cargo.toml b/src/lib/derive_macros/Cargo.toml index b818644f..c0bf1630 100644 --- a/src/lib/derive_macros/Cargo.toml +++ b/src/lib/derive_macros/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" proc-macro = true [dependencies] -colored = { version = "2.0" } -color = { path = "../utils/color" } +colored = { workspace = true } +ferrumc-color = { workspace = true } quote = { workspace = true } syn = { workspace = true, features = ["full"] } diff --git a/src/lib/derive_macros/src/net/packets/mod.rs b/src/lib/derive_macros/src/net/packets/mod.rs index 4e356ebd..564ef112 100644 --- a/src/lib/derive_macros/src/net/packets/mod.rs +++ b/src/lib/derive_macros/src/net/packets/mod.rs @@ -5,7 +5,7 @@ use std::ops::Add; use syn::{parse_macro_input, LitInt, LitStr}; use colored::Colorize; -use color::ColorSupport; +use ferrumc_color::ColorSupport; /// Essentially, this just reads all the files in the directory and generates a match arm for each packet. diff --git a/src/lib/utils/color/Cargo.toml b/src/lib/utils/color/Cargo.toml index 0eb388ca..fe2999aa 100644 --- a/src/lib/utils/color/Cargo.toml +++ b/src/lib/utils/color/Cargo.toml @@ -1,7 +1,7 @@ [package] -name = "color" +name = "ferrumc-color" version = "0.1.0" edition = "2021" [dependencies] -atty = "0.2.14" +atty = { workspace = true } From 752cfee800060c9755da372e00ddb9cc49df7e37 Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:29:22 -0500 Subject: [PATCH 07/17] Clean up the code, remove old tests --- src/lib/derive_macros/src/net/packets/mod.rs | 6 +- src/lib/utils/color/src/lib.rs | 103 +++++++++---------- 2 files changed, 51 insertions(+), 58 deletions(-) diff --git a/src/lib/derive_macros/src/net/packets/mod.rs b/src/lib/derive_macros/src/net/packets/mod.rs index 564ef112..446fa3c3 100644 --- a/src/lib/derive_macros/src/net/packets/mod.rs +++ b/src/lib/derive_macros/src/net/packets/mod.rs @@ -5,15 +5,13 @@ use std::ops::Add; use syn::{parse_macro_input, LitInt, LitStr}; use colored::Colorize; -use ferrumc_color::ColorSupport; +use ferrumc_color::supports_color; /// Essentially, this just reads all the files in the directory and generates a match arm for each packet. /// (packet_id, state) => { ... } pub fn bake_registry(input: TokenStream) -> TokenStream { - let color_support = ColorSupport::new(); - let color_bool = color_support.supports_color(); - if color_bool { + if supports_color() { colored::control::set_override(true); } diff --git a/src/lib/utils/color/src/lib.rs b/src/lib/utils/color/src/lib.rs index c59f7715..91eebe71 100644 --- a/src/lib/utils/color/src/lib.rs +++ b/src/lib/utils/color/src/lib.rs @@ -1,5 +1,4 @@ use std::env; -use atty::Stream; #[derive(Debug, PartialEq)] pub enum ColorLevel { @@ -49,86 +48,82 @@ impl PartialOrd for ColorLevel { } } - -pub struct ColorSupport { - pub stdout: ColorLevel, - pub stderr: ColorLevel, +pub fn supports_color() -> bool { + determine_color_level().to_bool() } -impl ColorSupport { - pub fn new() -> Self { - Self { - stdout: determine_color_level(Stream::Stdout), - stderr: determine_color_level(Stream::Stderr), - } + +fn determine_color_level() -> ColorLevel { + + // Check FORCE_COLOR environment variable first + if let Some(color) = force_color_check() { + return color; } - pub fn supports_color(&self) -> bool { - self.stdout.to_bool() || self.stderr.to_bool() + // CI check + if let Some(color) = ci_color_check() { + return color; } -} -fn determine_color_level(stream: Stream) -> ColorLevel { + // Term check + if let Some(color) = term_color_check() { + return color; + } - let mut color_level = ColorLevel::new(); + // Final fallback based on whether the stream is a TTY + if let Some(color) = tty_color_check() { + return color; + } - // Check FORCE_COLOR environment variable first - if let Ok(force_color) = env::var("FORCE_COLOR") { - color_level = match force_color.as_str() { - "0" => ColorLevel::force_none(), - "1" => color_level.update(ColorLevel::Basic), - "2" => color_level.update(ColorLevel::Enhanced), - "3" => color_level.update(ColorLevel::TrueColor), - _ => color_level, - }; - if color_level == ColorLevel::None { - return color_level; + ColorLevel::None +} + +fn force_color_check() -> Option { + if env::var("FORCE_COLOR").is_ok() { + match env::var("FORCE_COLOR").unwrap().as_str() { + "0" => Some(ColorLevel::None), + "1" => Some(ColorLevel::Basic), + "2" => Some(ColorLevel::Enhanced), + "3" => Some(ColorLevel::TrueColor), + _ => None, } + } else { + None } +} - // Handle specific CI environments +fn ci_color_check() -> Option { if env::var("CI").is_ok() { if env::var("GITHUB_ACTIONS").is_ok() || env::var("GITEA_ACTIONS").is_ok() { - return ColorLevel::TrueColor; + return Some(ColorLevel::TrueColor); } let ci_providers = ["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE", "codeship"]; if ci_providers.iter().any(|ci| env::var(ci).is_ok()) { - return ColorLevel::Basic; + return Some(ColorLevel::Basic); } } + None +} - // Check terminal types and other environment variables +fn term_color_check() -> Option { match env::var("TERM").as_deref() { - Ok("dumb") => (), - Ok("xterm-kitty") | Ok("truecolor") | Ok("ansi") => color_level = color_level.update(ColorLevel::TrueColor), - Ok(term) if term.ends_with("-256color") => color_level = color_level.update(ColorLevel::Enhanced), - Ok(term) if term.starts_with("xterm") || term.starts_with("screen") => color_level = color_level.update(ColorLevel::Basic), - _ => (), + Ok("dumb") => None, + Ok("xterm-kitty") | Ok("truecolor") | Ok("ansi") => Some(ColorLevel::TrueColor), + Ok(term) if term.ends_with("-256color") => Some(ColorLevel::Enhanced), + Ok(term) if term.starts_with("xterm") || term.starts_with("screen") => Some(ColorLevel::Basic), + _ => None, } +} - // Final fallback based on whether the stream is a TTY - if atty::is(stream) { - color_level = color_level.update(ColorLevel::Basic); +fn tty_color_check() -> Option { + if atty::is(atty::Stream::Stdout) { + return Some(ColorLevel::TrueColor); } - - color_level + None } #[cfg(test)] mod tests { use super::*; - #[test] - fn test_default_color_level() { - let color_support = ColorSupport::new(); - assert!(matches!(color_support.stdout, ColorLevel::Basic) || matches!(color_support.stdout, ColorLevel::None)); - assert!(matches!(color_support.stderr, ColorLevel::Basic) || matches!(color_support.stderr, ColorLevel::None)); - } - - #[test] - fn test_force_color_env() { - env::set_var("FORCE_COLOR", "3"); - assert_eq!(determine_color_level(Stream::Stdout), ColorLevel::TrueColor); - env::remove_var("FORCE_COLOR"); - } } From 010b73b80cd2b321975ba846ea1f84c0f4df181e Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:46:24 -0500 Subject: [PATCH 08/17] Add Tests --- src/lib/utils/color/src/lib.rs | 69 ++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/lib/utils/color/src/lib.rs b/src/lib/utils/color/src/lib.rs index 91eebe71..b19655d1 100644 --- a/src/lib/utils/color/src/lib.rs +++ b/src/lib/utils/color/src/lib.rs @@ -125,5 +125,74 @@ fn tty_color_check() -> Option { #[cfg(test)] mod tests { use super::*; + use std::env; + + // test to_bool + #[test] + fn test_to_bool() { + assert!(!ColorLevel::None.to_bool()); + assert!(ColorLevel::Basic.to_bool()); + assert!(ColorLevel::Enhanced.to_bool()); + assert!(ColorLevel::TrueColor.to_bool()); + } + + // test new + #[test] + fn test_new() { + assert_eq!(ColorLevel::None, ColorLevel::new()); + } + + // test update + #[test] + fn test_update() { + assert_eq!(ColorLevel::Basic, ColorLevel::None.update(ColorLevel::Basic)); + assert_eq!(ColorLevel::Enhanced, ColorLevel::Basic.update(ColorLevel::Enhanced)); + assert_eq!(ColorLevel::TrueColor, ColorLevel::Enhanced.update(ColorLevel::TrueColor)); + // should never downgrade + // updating to none should not change the color level + assert_eq!(ColorLevel::Basic, ColorLevel::Basic.update(ColorLevel::None)); + assert_eq!(ColorLevel::Enhanced, ColorLevel::Enhanced.update(ColorLevel::None)); + assert_eq!(ColorLevel::TrueColor, ColorLevel::TrueColor.update(ColorLevel::None)); + // updating to a lower level should not change the color level + assert_eq!(ColorLevel::Enhanced, ColorLevel::Enhanced.update(ColorLevel::Basic)); + assert_eq!(ColorLevel::TrueColor, ColorLevel::TrueColor.update(ColorLevel::Enhanced)); + + } + #[test] + fn test_force_color_levels() { + use std::env; + + // Define test cases as tuples: (env_var_value, expected_color_level) + let test_cases = [ + ("0", ColorLevel::None), + ("1", ColorLevel::Basic), + ("2", ColorLevel::Enhanced), + ("3", ColorLevel::TrueColor), + // Add a case for an invalid test that should fail + ("0", ColorLevel::Basic), // this case is intentionally wrong + ]; + + for (value, expected) in test_cases.iter() { + env::set_var("FORCE_COLOR", value); + let color_level = determine_color_level(); + + // If the expected color level is ColorLevel::Basic, this case should fail + if *value == "0" && *expected == ColorLevel::Basic { + assert_ne!(color_level, *expected, "Expected failure for FORCE_COLOR = {}", value); + } else { + assert_eq!(color_level, *expected, "Unexpected color level for FORCE_COLOR = {}", value); + } + + env::remove_var("FORCE_COLOR"); + } + } + + // Test CI color level + #[test] + fn test_ci_color_levels() { + env::set_var("CI", "GITHUB_ACTIONS"); + assert_eq!(determine_color_level(), ColorLevel::TrueColor); + env::remove_var("CI"); + } } From b872c18b1c1852108d450669d652e39ce0c95c4d Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:49:33 -0500 Subject: [PATCH 09/17] Clippy --- src/lib/utils/color/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/utils/color/src/lib.rs b/src/lib/utils/color/src/lib.rs index b19655d1..8f864d3e 100644 --- a/src/lib/utils/color/src/lib.rs +++ b/src/lib/utils/color/src/lib.rs @@ -30,6 +30,12 @@ impl ColorLevel { } } +impl Default for ColorLevel { + fn default() -> Self { + Self::None + } +} + impl PartialOrd for ColorLevel { fn partial_cmp(&self, other: &Self) -> Option { Some(match (self, other) { From ba73daae4c62ccc872a45a319d139d72d9b38101 Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:00:43 -0500 Subject: [PATCH 10/17] Fix CI Test --- src/lib/utils/color/src/lib.rs | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/lib/utils/color/src/lib.rs b/src/lib/utils/color/src/lib.rs index 8f864d3e..5c6ac658 100644 --- a/src/lib/utils/color/src/lib.rs +++ b/src/lib/utils/color/src/lib.rs @@ -197,8 +197,38 @@ mod tests { // Test CI color level #[test] fn test_ci_color_levels() { - env::set_var("CI", "GITHUB_ACTIONS"); - assert_eq!(determine_color_level(), ColorLevel::TrueColor); + // Define a list of CI providers and their expected ColorLevel + let ci_providers = [ + ("GITHUB_ACTIONS", ColorLevel::TrueColor), + ("GITEA_ACTIONS", ColorLevel::TrueColor), + ("TRAVIS", ColorLevel::Basic), + ("CIRCLECI", ColorLevel::Basic), + ("APPVEYOR", ColorLevel::Basic), + ("GITLAB_CI", ColorLevel::Basic), + ("BUILDKITE", ColorLevel::Basic), + ("DRONE", ColorLevel::Basic), + ("codeship", ColorLevel::Basic), + ]; + + // Outer loop: Set the main "CI" environment variable to enable CI mode + env::set_var("CI", "true"); + + for (provider, expected_level) in ci_providers.iter() { + // Set the specific CI provider environment variable + env::set_var(provider, "true"); + + // Check if the color level matches the expected level + assert_eq!( + &ci_color_check().unwrap_or(ColorLevel::None), + expected_level + ); + + // Clean up by removing the provider-specific environment variable + env::remove_var(provider); + } + + // Remove the "CI" environment variable to clean up env::remove_var("CI"); } + } From b0edc47a56b9d0a789090dfd1f5222c53dec1497 Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:25:13 -0500 Subject: [PATCH 11/17] Term Program --- src/lib/utils/color/src/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/lib/utils/color/src/lib.rs b/src/lib/utils/color/src/lib.rs index 5c6ac658..813aa416 100644 --- a/src/lib/utils/color/src/lib.rs +++ b/src/lib/utils/color/src/lib.rs @@ -76,6 +76,11 @@ fn determine_color_level() -> ColorLevel { return color; } + // Term Program check + if let Some(color) = term_program_check() { + return color; + } + // Final fallback based on whether the stream is a TTY if let Some(color) = tty_color_check() { return color; @@ -121,6 +126,15 @@ fn term_color_check() -> Option { } } +fn term_program_check() -> Option { + match env::var("TERM_PROGRAM").as_deref() { + Ok("iTerm.app") => Some(ColorLevel::TrueColor), + Ok("Apple_Terminal") => Some(ColorLevel::Enhanced), + _ => None, + } +} + + fn tty_color_check() -> Option { if atty::is(atty::Stream::Stdout) { return Some(ColorLevel::TrueColor); From 163788073d6600c71d565681e30e46ea36147c50 Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:30:21 -0500 Subject: [PATCH 12/17] Updated test panic --- src/lib/utils/color/src/lib.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/lib/utils/color/src/lib.rs b/src/lib/utils/color/src/lib.rs index 813aa416..45998b94 100644 --- a/src/lib/utils/color/src/lib.rs +++ b/src/lib/utils/color/src/lib.rs @@ -150,32 +150,32 @@ mod tests { // test to_bool #[test] fn test_to_bool() { - assert!(!ColorLevel::None.to_bool()); - assert!(ColorLevel::Basic.to_bool()); - assert!(ColorLevel::Enhanced.to_bool()); - assert!(ColorLevel::TrueColor.to_bool()); + assert!(!ColorLevel::None.to_bool(), "None should not be true"); + assert!(ColorLevel::Basic.to_bool(), "Basic should be true"); + assert!(ColorLevel::Enhanced.to_bool(), "Enhanced should be true"); + assert!(ColorLevel::TrueColor.to_bool(), "TrueColor should be true"); } // test new #[test] fn test_new() { - assert_eq!(ColorLevel::None, ColorLevel::new()); + assert_eq!(ColorLevel::None, ColorLevel::new(), "None should be the default color level"); } // test update #[test] fn test_update() { - assert_eq!(ColorLevel::Basic, ColorLevel::None.update(ColorLevel::Basic)); - assert_eq!(ColorLevel::Enhanced, ColorLevel::Basic.update(ColorLevel::Enhanced)); - assert_eq!(ColorLevel::TrueColor, ColorLevel::Enhanced.update(ColorLevel::TrueColor)); + assert_eq!(ColorLevel::Basic, ColorLevel::None.update(ColorLevel::Basic), "Updating from None to Basic should change the color level"); + assert_eq!(ColorLevel::Enhanced, ColorLevel::Basic.update(ColorLevel::Enhanced), "Updating from Basic to Enhanced should change the color level"); + assert_eq!(ColorLevel::TrueColor, ColorLevel::Enhanced.update(ColorLevel::TrueColor), "Updating from Enhanced to TrueColor should change the color level"); // should never downgrade // updating to none should not change the color level - assert_eq!(ColorLevel::Basic, ColorLevel::Basic.update(ColorLevel::None)); - assert_eq!(ColorLevel::Enhanced, ColorLevel::Enhanced.update(ColorLevel::None)); - assert_eq!(ColorLevel::TrueColor, ColorLevel::TrueColor.update(ColorLevel::None)); + assert_eq!(ColorLevel::Basic, ColorLevel::Basic.update(ColorLevel::None), "Updating from Basic to None should not change the color level"); + assert_eq!(ColorLevel::Enhanced, ColorLevel::Enhanced.update(ColorLevel::None), "Updating from Enhanced to None should not change the color level"); + assert_eq!(ColorLevel::TrueColor, ColorLevel::TrueColor.update(ColorLevel::None), "Updating from TrueColor to None should not change the color level"); // updating to a lower level should not change the color level - assert_eq!(ColorLevel::Enhanced, ColorLevel::Enhanced.update(ColorLevel::Basic)); - assert_eq!(ColorLevel::TrueColor, ColorLevel::TrueColor.update(ColorLevel::Enhanced)); + assert_eq!(ColorLevel::Enhanced, ColorLevel::Enhanced.update(ColorLevel::Basic), "Updating from Enhanced to Basic should not change the color level"); + assert_eq!(ColorLevel::TrueColor, ColorLevel::TrueColor.update(ColorLevel::Enhanced), "Updating from TrueColor to Enhanced should not change the color level"); } @@ -234,7 +234,9 @@ mod tests { // Check if the color level matches the expected level assert_eq!( &ci_color_check().unwrap_or(ColorLevel::None), - expected_level + expected_level, + "Unexpected color level for CI provider {}", + provider ); // Clean up by removing the provider-specific environment variable From 9a20e5f5a32d11a6d56ce0829fb2f5da7bc860e0 Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 20:23:01 -0500 Subject: [PATCH 13/17] Fix stupid hardcoded mistake --- src/lib/derive_macros/src/net/packets/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/derive_macros/src/net/packets/mod.rs b/src/lib/derive_macros/src/net/packets/mod.rs index 446fa3c3..03fd4338 100644 --- a/src/lib/derive_macros/src/net/packets/mod.rs +++ b/src/lib/derive_macros/src/net/packets/mod.rs @@ -105,9 +105,9 @@ pub fn bake_registry(input: TokenStream) -> TokenStream { " {} {} (ID: {}, State: {}, Struct Name: {})", "[FERRUMC_MACROS]".bold().blue(), "Found Packet".white().bold(), - "0x00".cyan(), - "status".green(), - "StatusRequestPacket".yellow() + format!("0x{:02X}", packet_id).cyan(), + format!("{}", state).green(), + format!("{}", struct_name).yellow() ); let path = format!( From 5efebe0fe0d12c5dc898d286836438160ff666e8 Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 20:53:50 -0500 Subject: [PATCH 14/17] ColorLevel easier to use, only one access to env::var("FORCE_COLOR") --- src/lib/utils/color/src/lib.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/lib/utils/color/src/lib.rs b/src/lib/utils/color/src/lib.rs index 45998b94..d6f37392 100644 --- a/src/lib/utils/color/src/lib.rs +++ b/src/lib/utils/color/src/lib.rs @@ -1,6 +1,7 @@ use std::env; -#[derive(Debug, PartialEq)] + +#[derive(Debug, PartialEq, Clone, Copy)] pub enum ColorLevel { None, Basic, @@ -90,17 +91,13 @@ fn determine_color_level() -> ColorLevel { } fn force_color_check() -> Option { - if env::var("FORCE_COLOR").is_ok() { - match env::var("FORCE_COLOR").unwrap().as_str() { - "0" => Some(ColorLevel::None), - "1" => Some(ColorLevel::Basic), - "2" => Some(ColorLevel::Enhanced), - "3" => Some(ColorLevel::TrueColor), - _ => None, - } - } else { - None - } + env::var("FORCE_COLOR").ok().and_then(|val| match val.as_str() { + "0" => Some(ColorLevel::None), + "1" => Some(ColorLevel::Basic), + "2" => Some(ColorLevel::Enhanced), + "3" => Some(ColorLevel::TrueColor), + _ => None, + }) } fn ci_color_check() -> Option { From 9ca6515f389018dc8334b676df1b1dc8094de26c Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 21:20:17 -0500 Subject: [PATCH 15/17] Put terminal color level into a static --- src/lib/utils/color/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/utils/color/src/lib.rs b/src/lib/utils/color/src/lib.rs index d6f37392..20a86512 100644 --- a/src/lib/utils/color/src/lib.rs +++ b/src/lib/utils/color/src/lib.rs @@ -1,5 +1,5 @@ use std::env; - +use std::sync::LazyLock; #[derive(Debug, PartialEq, Clone, Copy)] pub enum ColorLevel { @@ -55,11 +55,12 @@ impl PartialOrd for ColorLevel { } } +static COLOR_LEVEL: LazyLock = LazyLock::new(|| determine_color_level()); + pub fn supports_color() -> bool { - determine_color_level().to_bool() + COLOR_LEVEL.to_bool() } - fn determine_color_level() -> ColorLevel { // Check FORCE_COLOR environment variable first From f0717a6dc0b3a5fbe4ab5f8bb7ae365390f1261f Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 22:17:26 -0500 Subject: [PATCH 16/17] Windows --- src/lib/utils/color/src/lib.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/lib/utils/color/src/lib.rs b/src/lib/utils/color/src/lib.rs index 20a86512..02010f2d 100644 --- a/src/lib/utils/color/src/lib.rs +++ b/src/lib/utils/color/src/lib.rs @@ -68,6 +68,11 @@ fn determine_color_level() -> ColorLevel { return color; } + // Windows checks + if let Some(color) = windows_color_check() { + return color; + } + // CI check if let Some(color) = ci_color_check() { return color; @@ -101,6 +106,13 @@ fn force_color_check() -> Option { }) } +fn windows_color_check() -> Option { + if std::env::consts::OS == "windows" { + return Some(ColorLevel::Basic); + } + None +} + fn ci_color_check() -> Option { if env::var("CI").is_ok() { if env::var("GITHUB_ACTIONS").is_ok() || env::var("GITEA_ACTIONS").is_ok() { @@ -120,6 +132,7 @@ fn term_color_check() -> Option { Ok("xterm-kitty") | Ok("truecolor") | Ok("ansi") => Some(ColorLevel::TrueColor), Ok(term) if term.ends_with("-256color") => Some(ColorLevel::Enhanced), Ok(term) if term.starts_with("xterm") || term.starts_with("screen") => Some(ColorLevel::Basic), + Ok("cygwin") => Some(ColorLevel::Basic), _ => None, } } @@ -132,7 +145,6 @@ fn term_program_check() -> Option { } } - fn tty_color_check() -> Option { if atty::is(atty::Stream::Stdout) { return Some(ColorLevel::TrueColor); From 3b5e1d12c67887c3cf3f12a82740ad45e43f9d69 Mon Sep 17 00:00:00 2001 From: 0xnim <0xnim@users.noreply.github.com> Date: Tue, 5 Nov 2024 22:32:17 -0500 Subject: [PATCH 17/17] Fix clippy --- src/lib/derive_macros/src/net/packets/mod.rs | 4 ++-- src/lib/utils/color/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/derive_macros/src/net/packets/mod.rs b/src/lib/derive_macros/src/net/packets/mod.rs index 03fd4338..f8e5c213 100644 --- a/src/lib/derive_macros/src/net/packets/mod.rs +++ b/src/lib/derive_macros/src/net/packets/mod.rs @@ -106,8 +106,8 @@ pub fn bake_registry(input: TokenStream) -> TokenStream { "[FERRUMC_MACROS]".bold().blue(), "Found Packet".white().bold(), format!("0x{:02X}", packet_id).cyan(), - format!("{}", state).green(), - format!("{}", struct_name).yellow() + state.green(), + struct_name.to_string().yellow() ); let path = format!( diff --git a/src/lib/utils/color/src/lib.rs b/src/lib/utils/color/src/lib.rs index 02010f2d..f893480d 100644 --- a/src/lib/utils/color/src/lib.rs +++ b/src/lib/utils/color/src/lib.rs @@ -55,7 +55,7 @@ impl PartialOrd for ColorLevel { } } -static COLOR_LEVEL: LazyLock = LazyLock::new(|| determine_color_level()); +static COLOR_LEVEL: LazyLock = LazyLock::new(determine_color_level); pub fn supports_color() -> bool { COLOR_LEVEL.to_bool()