a better coloring crate
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs223
1 files changed, 223 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..491dea9
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,223 @@
+//! smart crate for terminal coloring.
+//!
+//! uses macros instead of methods.
+//!
+//! ## usage
+//!
+//! heres how it works:
+//! ```rust
+//! # use comat::cprintln;
+//! # use std::time::{Duration, Instant};
+//! cprintln!("the traffic light is {bold_red}red.{reset}");
+//! cprintln!("the traffic light will be {green}green{reset} at {:?}.", Instant::now() + Duration::from_secs(40));
+//! ```
+//!
+//! ## why you should use comat instead of {yansi, owo_colors, colored, ..}
+//!
+//! - no method pollution, your intellisense remains fine
+//! - compact: shorter than even raw ansi. see:
+//! ```
+//! # use comat::cprint;
+//! # let thing = 0;
+//! cprint!("{thing:red}.");
+//! ```
+//! vs
+//! ```
+//! print!("\x1b[0;34;31mred\x1b[0m.");
+//! ```
+//! vs
+//! ```ignore
+//! print!("{}.", "red".red());
+//! ```
+//! - intuitive: you dont have to
+//! ```ignore
+//! println!("{} {} {}", thing1.red().on_blue(), thing2.red().on_blue(), thing3.italic());.
+//! ```
+//! instead, simply
+//! ```
+//! # use comat::cprintln;
+//! # let thing1 = 0; let thing2 = 5; let thing3 = 4;
+//! cprintln!("{red}{on_blue}{thing1} {thing2} {thing3:italic}");
+//! ```
+//!
+//! ## syntax
+//!
+//! `{{` gives you a `{`, to get a `{{` use `{{{{`.
+//!
+//! `{color}` adds that effect/color to the string. it does not reset afterwards.
+//!
+//! if the color inside a `{}` is not found, it doesnt touch the block, for convenience.
+//!
+//! `{thing:color}` will reset everything before the block, color it, and reset that color. similar to `thing.color()` with other libs.
+#![forbid(unsafe_code)]
+#![warn(clippy::pedantic, clippy::dbg_macro, missing_docs)]
+use proc_macro::TokenStream;
+use quote::{quote, ToTokens, TokenStreamExt};
+use syn::{parse::Parse, parse_macro_input, punctuated::Punctuated, Expr, Result, Token};
+
+mod cfstr;
+use cfstr::CFStr;
+
+#[proc_macro]
+/// Macro that simply modifies the format string to have colors.
+/// Mostly for testing. Use [`cformat_args!`] instead where possible.
+pub fn comat(input: TokenStream) -> TokenStream {
+ let str = parse_macro_input!(input as CFStr);
+ str.to_token_stream().into()
+}
+
+struct One {
+ cfstr: CFStr,
+ args: Punctuated<Expr, Token![,]>,
+}
+
+impl Parse for One {
+ fn parse(input: syn::parse::ParseStream) -> Result<Self> {
+ let cfstr = input.parse::<CFStr>()?;
+ let _ = input.parse::<Token![,]>();
+ Ok(Self {
+ cfstr,
+ args: Punctuated::<Expr, Token![,]>::parse_terminated(input)?,
+ })
+ }
+}
+
+impl ToTokens for One {
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+ self.cfstr.to_tokens(tokens);
+ tokens.append(proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone));
+ self.args.to_tokens(tokens);
+ }
+}
+
+// NOTE: many of these can be made as decl macros, but decl macros can't be exported from proc macro crates yet.
+
+#[proc_macro]
+/// Print text, colorfully, to stdout, with a newline.
+///
+/// See also [`println`].
+/// ```
+/// # use comat::*;
+/// let magic = 4;
+/// cprintln!("{red}look its red{reset}! {bold_blue}{magic}{reset} is the magic number!");
+/// ```
+pub fn cprintln(input: TokenStream) -> TokenStream {
+ let f = parse_macro_input!(input as One);
+ quote! { println!(#f) }.into()
+}
+
+#[proc_macro]
+/// Print text, colorfully, to stdout, without a newline.
+///
+/// See also [`print`].
+/// ```
+/// # use comat::*;
+/// cprint!("{yellow}i am a warning. {reset}why do you dislike me?");
+/// ```
+pub fn cprint(input: TokenStream) -> TokenStream {
+ let f = parse_macro_input!(input as One);
+ quote! { print!(#f) }.into()
+}
+
+#[proc_macro]
+/// Format text, colorfully.
+///
+/// See also [`format`].
+/// ```
+/// # use comat::*;
+/// let favorite_thing = "teddy bears";
+/// let message = cformat!("the {red}bogeymen{reset} will get your {favorite_thing:underline}");
+/// # assert_eq!(message, "the \x1b[0;34;31mbogeymen\x1b[0m will get your \x1b[0m\x1b[24mteddy bears\x1b[0m");
+/// ```
+pub fn cformat(input: TokenStream) -> TokenStream {
+ let f = parse_macro_input!(input as One);
+ quote! { format!(#f) }.into()
+}
+
+#[proc_macro]
+/// Produce [`fmt::Arguments`](std::fmt::Arguments). Sometimes functions take these.
+///
+/// See also [`format_args`].
+/// ```
+/// # use comat::*;
+/// let args = cformat_args!("{bold_red}fatal error. {reset}killing {blue}everything{reset}");
+/// // NOTE: do not do this. instead use cprintln.
+/// println!("{}", args);
+pub fn cformat_args(input: TokenStream) -> TokenStream {
+ let f = parse_macro_input!(input as One);
+ quote! { format_args!(#f) }.into()
+}
+/// Colorfully panic.
+///
+/// See also [`panic`].
+/// ```should_panic
+/// # use comat::cpanic;
+/// cpanic!("why is the bound {red}bad");
+/// ```
+#[proc_macro]
+pub fn cpanic(input: TokenStream) -> TokenStream {
+ let f = parse_macro_input!(input as One);
+ quote! { panic!(#f) }.into()
+}
+
+struct Two {
+ a: Expr,
+ cfstr: CFStr,
+ args: Punctuated<Expr, Token![,]>,
+}
+
+impl Parse for Two {
+ fn parse(input: syn::parse::ParseStream) -> Result<Self> {
+ let a = input.parse::<Expr>()?;
+ input.parse::<Token![,]>()?;
+ let cfstr = input.parse::<CFStr>()?;
+ let _ = input.parse::<Token![,]>();
+ Ok(Self {
+ a,
+ cfstr,
+ args: Punctuated::<Expr, Token![,]>::parse_terminated(input)?,
+ })
+ }
+}
+
+impl ToTokens for Two {
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+ self.a.to_tokens(tokens);
+ tokens.append(proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone));
+ self.cfstr.to_tokens(tokens);
+ tokens.append(proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone));
+ self.args.to_tokens(tokens);
+ }
+}
+
+#[proc_macro]
+/// Write to a buffer colorfully, with no newline.
+///
+/// See also [`write`]
+/// ```
+/// # use comat::cwrite;
+/// use std::io::Write;
+/// let mut buf = vec![];
+/// cwrite!(buf, "{green}omg there's going to be ansi sequences in a {black}Vec<u8>{reset}!");
+/// # assert_eq!(buf, [27, 91, 48, 59, 51, 52, 59, 51, 50, 109, 111, 109, 103, 32, 116, 104, 101, 114, 101, 39, 115, 32, 103, 111, 105, 110, 103, 32, 116, 111, 32, 98, 101, 32, 97, 110, 115, 105, 32, 115, 101, 113, 117, 101, 110, 99, 101, 115, 32, 105, 110, 32, 97, 32, 27, 91, 48, 59, 51, 52, 59, 51, 48, 109, 86, 101, 99, 60, 117, 56, 62, 27, 91, 48, 109, 33]);
+/// ```
+pub fn cwrite(input: TokenStream) -> TokenStream {
+ let f = parse_macro_input!(input as Two);
+ quote! { write!(#f) }.into()
+}
+
+#[proc_macro]
+/// Write to a buffer colorfully, with newline.
+///
+/// See also [`writeln`]
+/// ```
+/// # use comat::cwriteln;
+/// use std::io::Write;
+/// let mut buf = vec![];
+/// cwriteln!(buf, "hey look: {strike}strike'd text{reset}!");
+/// # assert_eq!(buf, [104, 101, 121, 32, 108, 111, 111, 107, 58, 32, 27, 91, 57, 109, 115, 116, 114, 105, 107, 101, 39, 100, 32, 116, 101, 120, 116, 27, 91, 48, 109, 33, 10]);
+/// ```
+pub fn cwriteln(input: TokenStream) -> TokenStream {
+ let f = parse_macro_input!(input as Two);
+ quote! { writeln!(#f) }.into()
+}