diff --git a/.gitignore b/.gitignore index 3602419..3ac4e81 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target +dbg-pls/target .idea *.iml \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 9113388..2800612 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,7 +7,6 @@ name = "dbg-pls" version = "0.2.2" dependencies = [ "itoa", - "once_cell", "proc-macro2", "quote", "ryu", @@ -27,12 +26,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" -[[package]] -name = "once_cell" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" - [[package]] name = "proc-macro2" version = "1.0.37" diff --git a/dbg-pls/.gitignore b/dbg-pls/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/dbg-pls/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/dbg-pls/Cargo.lock b/dbg-pls/Cargo.lock new file mode 100644 index 0000000..d379ad4 --- /dev/null +++ b/dbg-pls/Cargo.lock @@ -0,0 +1,61 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "dbg-pls" +version = "0.2.2" +dependencies = [ + "itoa", + "proc-macro2", + "quote", + "ryu", + "syn", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "proc-macro2" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "syn" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" diff --git a/dbg-pls/Cargo.toml b/dbg-pls/Cargo.toml index e473a06..2ae9e48 100644 --- a/dbg-pls/Cargo.toml +++ b/dbg-pls/Cargo.toml @@ -1,6 +1,3 @@ -[workspace] -members = ["debug-derive"] - [package] name = "dbg-pls" version = "0.2.2" @@ -11,13 +8,6 @@ license = "MIT" repository = "https://github.com/conradludgate/dbg-pls" readme = "README.md" -include = [ - "src", - "README.md", - "assets/syntaxes/Rust/Rust.sublime-syntax", - "assets/themes/sublime-monokai-extended/Monokai Extended.tmTheme", -] - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] @@ -27,25 +17,8 @@ quote = "1" itoa = "1" ryu = "1" -# derive -dbg-pls-derive = { version = "0.2.1", path = "debug-derive", optional = true } - -# pretty -prettyplease = { version = "0.1", optional = true } -textwrap = { version = "0.15", optional = true } - -# colors -syntect = { version = "4.6.0", optional = true } -once_cell = "1" - -[dev-dependencies] -dbg-pls = { path = ".", features = ["derive", "pretty", "colors"] } - [features] default = [] -derive = ["dbg-pls-derive"] -pretty = ["prettyplease", "textwrap"] -colors = ["pretty", "syntect"] [package.metadata.docs.rs] all-features = true diff --git a/dbg-pls/debug-derive/Cargo.toml b/dbg-pls/debug-derive/Cargo.toml deleted file mode 100644 index 5a09aa9..0000000 --- a/dbg-pls/debug-derive/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "dbg-pls-derive" -version = "0.2.1" -authors = ["Conrad Ludgate "] -edition = "2018" -description = "derive(Debug)" -license = "MIT" -repository = "https://github.com/conradludgate/dbg-pls" -readme = "../README.md" - -[lib] -proc-macro = true - -[dependencies] -quote = "1.0" -syn = "1.0" -proc-macro2 = "1.0" diff --git a/dbg-pls/debug-derive/src/debug.rs b/dbg-pls/debug-derive/src/debug.rs deleted file mode 100644 index 58cbb93..0000000 --- a/dbg-pls/debug-derive/src/debug.rs +++ /dev/null @@ -1,117 +0,0 @@ -use crate::pat::{named_idents, unnamed_idents, PatternImpl}; -use proc_macro2::{Ident, TokenStream as TokenStream2}; -use quote::{quote, ToTokens}; -use syn::{spanned::Spanned, Data, DataEnum, DataStruct, DeriveInput, Fields, Path, Variant}; - -pub struct DebugImpl(pub T); - -impl ToTokens for DebugImpl<(Path, DeriveInput)> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let (path, input) = &self.0; - - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - - let ident = &input.ident; - let body = DebugImpl((ident, &input.data)); - - tokens.extend(quote! { - impl #impl_generics #path::DebugPls for #ident #ty_generics #where_clause { - fn fmt(&self, f: #path::Formatter<'_>) { - #body - } - } - }) - } -} - -impl<'a> ToTokens for DebugImpl<(&'a Ident, &'a Data)> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let (name, data) = self.0; - match data { - Data::Struct(s) => DebugImpl((name, s)).to_tokens(tokens), - Data::Enum(e) => DebugImpl((name, e)).to_tokens(tokens), - Data::Union(_) => tokens - .extend(syn::Error::new(self.span(), "unions not supported").into_compile_error()), - } - } -} - -impl<'a> ToTokens for DebugImpl<(&'a Ident, &'a DataStruct)> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let (name, data) = self.0; - let pat = PatternImpl(&data.fields); - tokens.extend(quote! { - let #name #pat = self; - }); - DebugImpl((name, &data.fields)).to_tokens(tokens) - } -} - -impl<'a> ToTokens for DebugImpl<(&'a Ident, &'a DataEnum)> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let (name, data) = self.0; - let variants = data.variants.iter().map(|v| DebugImpl((name, v))); - tokens.extend(quote! { - match self { - #( #variants )* - } - }); - } -} - -impl<'a> ToTokens for DebugImpl<(&'a Ident, &'a Variant)> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let (name, variant) = self.0; - let Variant { - attrs: _, - ident, - fields, - discriminant: _, - } = &variant; - let pattern = PatternImpl(fields); - let debug = DebugImpl((ident, fields)); - - tokens.extend(quote! { - #name::#ident #pattern => { #debug } - }); - } -} - -impl<'a> ToTokens for DebugImpl<(&'a Ident, &'a Fields)> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let (name, fields) = self.0; - let name = name.to_string(); - match fields { - Fields::Named(named) => { - tokens.extend(quote! { - f.debug_struct(#name) - }); - named_idents(named).for_each(|ident| { - let name = ident.to_string(); - tokens.extend(quote! { - .field(#name, #ident) - }) - }); - tokens.extend(quote! { - .finish() - }); - } - Fields::Unnamed(unnamed) => { - tokens.extend(quote! { - f.debug_tuple_struct(#name) - }); - unnamed_idents(unnamed).for_each(|ident| { - tokens.extend(quote! { - .field(#ident) - }) - }); - tokens.extend(quote! { - .finish() - }); - } - Fields::Unit => tokens.extend(quote! { - f.debug_ident(#name) - }), - } - } -} diff --git a/dbg-pls/debug-derive/src/lib.rs b/dbg-pls/debug-derive/src/lib.rs deleted file mode 100644 index 43d7643..0000000 --- a/dbg-pls/debug-derive/src/lib.rs +++ /dev/null @@ -1,61 +0,0 @@ -use debug::DebugImpl; -use predicate::predicate; -use proc_macro::TokenStream; -use quote::ToTokens; -use syn::{parse_macro_input, DeriveInput, parse_quote, Attribute, Path}; - -mod debug; -mod pat; -mod predicate; - -/// Derives the standard `DebugPls` implementation. -/// -/// Works exactly like [`Debug`] -#[proc_macro_derive(DebugPls, attributes(dbg_pls))] -pub fn derive(input: TokenStream) -> TokenStream { - let mut input = parse_macro_input!(input as DeriveInput); - - let path = match get_crate(&input.attrs) { - Ok(path) => path, - Err(err) => return err.into_compile_error().into_token_stream().into(), - }; - - predicate(&mut input, path.clone()); - DebugImpl((path, input)).into_token_stream().into() -} - -fn get_crate(attrs: &[Attribute]) -> syn::Result { - fn parse_crate(lit: syn::Lit) -> syn::Result { - match lit { - syn::Lit::Str(s) => syn::parse_str(&s.value()), - _ => Err(syn::Error::new(lit.span(), "invalid crate name")), - } - } - - fn parse_meta(meta: syn::Meta) -> Option> { - if let syn::Meta::List(list) = meta { - for meta in list.nested { - if let syn::NestedMeta::Meta(syn::Meta::NameValue(nv)) = meta { - if let Some(ident) = nv.path.get_ident() { - if *ident == "crate" { - return Some(parse_crate(nv.lit)); - } - } - } - } - } - None - } - - for attr in attrs { - if let Some(ident) = attr.path.get_ident() { - if *ident == "dbg_pls" { - if let Some(path) = parse_meta(Attribute::parse_meta(attr)?).transpose()? { - return Ok(path); - } - } - } - } - - Ok(parse_quote! { ::dbg_pls }) -} diff --git a/dbg-pls/debug-derive/src/pat.rs b/dbg-pls/debug-derive/src/pat.rs deleted file mode 100644 index 850d15d..0000000 --- a/dbg-pls/debug-derive/src/pat.rs +++ /dev/null @@ -1,46 +0,0 @@ -use proc_macro2::{Delimiter, Group, Ident, TokenStream as TokenStream2, TokenTree}; -use quote::{format_ident, quote, ToTokens}; -use syn::{spanned::Spanned, Fields, FieldsNamed, FieldsUnnamed}; - -pub struct PatternImpl(pub T); - -impl<'a> ToTokens for PatternImpl<&'a Fields> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - match &self.0 { - Fields::Named(named) => PatternImpl(named).to_tokens(tokens), - Fields::Unnamed(unnamed) => PatternImpl(unnamed).to_tokens(tokens), - Fields::Unit => {} - } - } -} - -impl<'a> ToTokens for PatternImpl<&'a FieldsNamed> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let idents = named_idents(self.0); - let inner = quote! { #(#idents),* }; - tokens.extend([TokenTree::Group(Group::new(Delimiter::Brace, inner))]) - } -} - -impl<'a> ToTokens for PatternImpl<&'a FieldsUnnamed> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let idents = unnamed_idents(self.0); - let inner = quote! { #(#idents),* }; - tokens.extend([TokenTree::Group(Group::new(Delimiter::Parenthesis, inner))]) - } -} - -pub fn unnamed_idents(fields: &FieldsUnnamed) -> impl Iterator + '_ { - fields - .unnamed - .iter() - .enumerate() - .map(|(i, field)| format_ident!("val{}", i, span = field.span())) -} - -pub fn named_idents(fields: &FieldsNamed) -> impl Iterator + '_ { - fields - .named - .iter() - .map(|field| field.ident.as_ref().unwrap()) -} diff --git a/dbg-pls/debug-derive/src/predicate.rs b/dbg-pls/debug-derive/src/predicate.rs deleted file mode 100644 index 6df829c..0000000 --- a/dbg-pls/debug-derive/src/predicate.rs +++ /dev/null @@ -1,57 +0,0 @@ -use quote::format_ident; -use syn::{ - punctuated::Punctuated, token, Data, DeriveInput, Field, Fields, Path, PredicateType, - TraitBound, TypeParamBound, WhereClause, WherePredicate, -}; - -pub fn predicate(input: &mut DeriveInput, path: Path) { - let mut pred = Pred { - wc: input.generics.make_where_clause(), - path, - }; - - match &input.data { - Data::Struct(s) => pred.fields(&s.fields), - Data::Enum(e) => e.variants.iter().for_each(|var| pred.fields(&var.fields)), - Data::Union(_) => todo!(), - } -} - -struct Pred<'a> { - wc: &'a mut WhereClause, - path: Path, -} - -impl<'a> Pred<'a> { - fn dbg_pls(&mut self, field: &Field) { - let mut bounds = Punctuated::new(); - - let mut path = self.path.clone(); - path.segments.push(syn::PathSegment { - ident: format_ident!("DebugPls"), - arguments: syn::PathArguments::None, - }); - - bounds.push(TypeParamBound::Trait(TraitBound { - paren_token: None, - modifier: syn::TraitBoundModifier::None, - lifetimes: None, - path, - })); - - self.wc.predicates.push(WherePredicate::Type(PredicateType { - lifetimes: None, - bounded_ty: field.ty.clone(), - colon_token: token::Colon::default(), - bounds, - })) - } - - fn fields(&mut self, fields: &Fields) { - match fields { - syn::Fields::Named(named) => named.named.iter().for_each(|f| self.dbg_pls(f)), - syn::Fields::Unnamed(unnamed) => unnamed.unnamed.iter().for_each(|f| self.dbg_pls(f)), - syn::Fields::Unit => {} - } - } -} diff --git a/dbg-pls/src/colors.rs b/dbg-pls/src/colors.rs deleted file mode 100644 index 2717720..0000000 --- a/dbg-pls/src/colors.rs +++ /dev/null @@ -1,175 +0,0 @@ -// use bat::PrettyPrinter; - -// use crate::{pretty::process, DebugPls}; - -// /// Prints the pretty printed code to stdout -// pub fn print_colorful(value: &dyn DebugPls) { -// let output = process(value); -// let _ = PrettyPrinter::new() -// .input_from_bytes(output.as_bytes()) -// .language("rust") -// .line_numbers(true) -// .print(); -// } - -use std::io::Cursor; - -use once_cell::sync::OnceCell; -use syntect::{ - easy::HighlightLines, - highlighting::{Theme, ThemeSet}, - parsing::{SyntaxDefinition, SyntaxSet, SyntaxSetBuilder}, - util::{as_24_bit_terminal_escaped, LinesWithEndings}, -}; - -use crate::{pretty::pretty_string, DebugPls, Formatter}; - -fn syntax() -> &'static SyntaxSet { - static INSTANCE: OnceCell = OnceCell::new(); - INSTANCE.get_or_init(|| { - let mut syntax_set = SyntaxSetBuilder::new(); - syntax_set.add( - SyntaxDefinition::load_from_str( - include_str!("../assets/syntaxes/Rust/Rust.sublime-syntax"), - true, - None, - ) - .unwrap(), - ); - syntax_set.build() - }) -} - -fn theme() -> &'static Theme { - static INSTANCE: OnceCell = OnceCell::new(); - INSTANCE.get_or_init(|| { - let s = include_str!("../assets/themes/sublime-monokai-extended/Monokai Extended.tmTheme"); - ThemeSet::load_from_reader(&mut Cursor::new(s.as_bytes())).unwrap() - }) -} - -fn highlight(s: &str, mut w: impl std::fmt::Write) -> std::fmt::Result { - let ps = syntax(); - let syntax = ps.find_syntax_by_name("Rust").unwrap(); - let theme = theme(); - - let mut h = HighlightLines::new(syntax, theme); - - for line in LinesWithEndings::from(s) { - let ranges = h.highlight(line, ps); - write!(w, "{}", as_24_bit_terminal_escaped(&ranges[..], false))?; - } - write!(w, "\x1b[0m") // reset the color -} - -/// Implementation detail for the `color!` macro -pub struct ColorStr<'a>(pub &'a str); - -impl<'a> std::fmt::Display for ColorStr<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let expr = syn::parse_str(self.0).map_err(|_| std::fmt::Error)?; - highlight(&pretty_string(expr), f) - } -} - -struct Color<'a>(&'a dyn DebugPls); - -impl<'a> std::fmt::Debug for Color<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - highlight(&pretty_string(Formatter::process(self.0)), f) - } -} - -impl<'a> std::fmt::Display for Color<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Debug::fmt(self, f) - } -} - -#[cfg_attr(docsrs, doc(cfg(feature = "colors")))] -/// Wraps a [`Debug`] type into a [`std::fmt::Debug`] type for use in regular [`format!`] -pub fn color(value: &impl DebugPls) -> impl std::fmt::Debug + std::fmt::Display + '_ { - Color(value) -} - -#[cfg_attr(docsrs, doc(cfg(feature = "colors")))] -#[macro_export] -/// Prints and returns the value of a given expression for quick and dirty -/// debugging. Same as [`std::dbg`] -/// -/// An example: -/// -/// ```rust -/// # use dbg_pls::color; -/// let a = 2; -/// let b = color!(a * 2) + 1; -/// // ^-- prints: [src/main.rs:2] a * 2 = 4 -/// // with syntax highlighting -/// assert_eq!(b, 5); -/// ``` -/// -/// The macro works by using the [`DebugPls`] implementation of the type of -/// the given expression to print the value to [stderr] along with the -/// source location of the macro invocation as well as the source code -/// of the expression. -/// -/// Invoking the macro on an expression moves and takes ownership of it -/// before returning the evaluated expression unchanged. If the type -/// of the expression does not implement `Copy` and you don't want -/// to give up ownership, you can instead borrow with `color!(&expr)` -/// for some expression `expr`. -/// -/// The `color!` macro works exactly the same in release builds. -/// This is useful when debugging issues that only occur in release -/// builds or when debugging in release mode is significantly faster. -/// -/// Note that the macro is intended as a debugging tool and therefore you -/// should avoid having uses of it in version control for long periods -/// (other than in tests and similar). -/// Debug output from production code is better done with other facilities -/// such as the [`debug!`] macro from the [`log`] crate. -/// -/// [stderr]: https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr) -/// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html -/// [`log`]: https://crates.io/crates/log -macro_rules! color { - () => { - ::std::eprintln!("[{}:{}]", ::std::file!(), ::std::line!()) - }; - ($val:expr $(,)?) => { - match $val { - tmp => { - ::std::eprintln!( - "[{}:{}] {} => {}", - ::std::file!(), - ::std::line!(), - $crate::__private::ColorStr(::std::stringify!($val)), - $crate::color(&tmp) - ); - tmp - } - } - }; - ($($val:expr),+ $(,)?) => { - ($($crate::color!($val)),+,) - }; -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use crate::color; - - #[test] - fn colors() { - let map = color! { - HashMap::from([ - ("hello", 1), - ("world", 2), - ]) - }; - // map is moved through properly - assert_eq!(map, HashMap::from([("hello", 1), ("world", 2),])); - } -} diff --git a/dbg-pls/src/impls.rs b/dbg-pls/src/impls.rs index fb2b84d..e1774b6 100644 --- a/dbg-pls/src/impls.rs +++ b/dbg-pls/src/impls.rs @@ -1,2 +1,2 @@ -mod syn_impls; mod std; +mod syn_impls; diff --git a/dbg-pls/src/impls/std/collections.rs b/dbg-pls/src/impls/std/collections.rs index 4ee6c7f..70e42b5 100644 --- a/dbg-pls/src/impls/std/collections.rs +++ b/dbg-pls/src/impls/std/collections.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque, LinkedList, BinaryHeap}; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; use crate::{DebugPls, Formatter}; diff --git a/dbg-pls/src/lib.rs b/dbg-pls/src/lib.rs index ba8188e..3d3a033 100644 --- a/dbg-pls/src/lib.rs +++ b/dbg-pls/src/lib.rs @@ -1,171 +1,6 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(clippy::pedantic)] #![forbid(unsafe_code)] -//! Syntax aware debug printing. -//! -//! Makes use of `syn` and `prettyplease` in order to provide the most -//! canonincal rust debug lines as possible, quickly. -//! -//! # Example usage -//! -//! ``` -//! use dbg_pls::{pretty, DebugPls}; -//! -//! #[derive(DebugPls, Copy, Clone)] -//! pub struct Demo { -//! foo: i32, -//! bar: &'static str, -//! } -//! -//! let mut val = [Demo { foo: 5, bar: "hello" }; 10]; -//! val[6].bar = "Hello, world! I am a very long string"; -//! -//! let output = format!("{}", pretty(&val)); -//! let expected = r#"[ -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { -//! foo: 5, -//! bar: "Hello, world! I am a very long string", -//! }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! ]"#; -//! -//! assert_eq!(output, expected); -//! ``` -//! -//! # Example with highlighting -//! -//! ``` -//! use dbg_pls::{color, DebugPls}; -//! -//! #[derive(DebugPls, Copy, Clone)] -//! pub struct Demo { -//! foo: i32, -//! bar: &'static str, -//! } -//! -//! let mut val = [Demo { foo: 5, bar: "hello" }; 10]; -//! val[6].bar = "Hello, world! I am a very long string"; -//! -//! println!("{}", color(&val)); -//! ``` -//! Outputs: -//! -//! ![](https://raw.githubusercontent.com/conradludgate/dbg-pls/5dee03187a3f83693739e0288d56da5980e1d486/readme/highlighted.png) -//! -//! # Why -//! -//! For the sake of demonstration, let's take a look at the snippet from above. -//! It provides an array of 10 `Demo` structs. You could imagine this to -//! be representative of a complex deep struct. -//! -//! ``` -//! #[derive(Debug, Copy, Clone)] -//! pub struct Demo { -//! foo: i32, -//! bar: &'static str, -//! } -//! -//! let mut val = [Demo { foo: 5, bar: "hello" }; 10]; -//! val[6].bar = "Hello, world! I am a very long string"; -//! -//! println!("{:?}", val); -//! ``` -//! -//! This outputs -//! -//! ```text -//! [Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "Hello, world! I am a very long string" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }] -//! ``` -//! -//! Switching to the alternative output format `{:#?}` you get the following -//! -//! ```text -//! [ -//! Demo { -//! foo: 5, -//! bar: "hello", -//! }, -//! Demo { -//! foo: 5, -//! bar: "hello", -//! }, -//! Demo { -//! foo: 5, -//! bar: "hello", -//! }, -//! Demo { -//! foo: 5, -//! bar: "hello", -//! }, -//! Demo { -//! foo: 5, -//! bar: "hello", -//! }, -//! Demo { -//! foo: 5, -//! bar: "hello", -//! }, -//! Demo { -//! foo: 5, -//! bar: "Hello, world! I am a very long string", -//! }, -//! Demo { -//! foo: 5, -//! bar: "hello", -//! }, -//! Demo { -//! foo: 5, -//! bar: "hello", -//! }, -//! Demo { -//! foo: 5, -//! bar: "hello", -//! }, -//! ] -//! ``` -//! -//! Both of these are very unweildy to read through. Compare that to our `pretty` formatting: -//! -//! ``` -//! # use dbg_pls::pretty; -//! # let val = 0; -//! println!("{}", pretty(&val)); -//! ``` -//! -//! And you will see -//! -//! ```text -//! [ -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { -//! foo: 5, -//! bar: "Hello, world! I am a very long string", -//! }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! Demo { foo: 5, bar: "hello" }, -//! ] -//! ``` -//! -//! # How it works -//! -//! All [`DebugPls`] implementations are forced to output only valid -//! [`syn::Expr`] values. These are then formatted using [`prettyplease::unparse`]. -//! Finally, it uses [`syntect`] to provide syntax highlighting, with theme provided by -//! use syn::__private::{Span, TokenStream2}; @@ -184,102 +19,10 @@ pub use debug_struct::DebugStruct; pub use debug_tuple::DebugTuple; pub use debug_tuple_struct::DebugTupleStruct; -#[cfg(feature = "pretty")] -mod pretty; -#[cfg(feature = "pretty")] -pub use pretty::pretty; - -#[cfg(feature = "colors")] -mod colors; -#[cfg(feature = "colors")] -pub use colors::color; - -#[cfg(feature = "derive")] -#[cfg_attr(docsrs, doc(cfg(feature = "derive")))] -pub use dbg_pls_derive::DebugPls; - -#[doc(hidden)] -pub mod __private { - #[cfg(feature = "pretty")] - pub use crate::pretty::Str as PrettyStr; - #[cfg(feature = "colors")] - pub use crate::colors::ColorStr; -} - -/// Syntax aware pretty-printed debug formatting. -/// -/// `DebugPls` should format the output in a programmer-facing, debugging context. -/// -/// Generally speaking, you should just `derive` a `Debug` implementation. -/// -/// # Examples -/// -/// Deriving an implementation: -/// -/// ``` -/// use dbg_pls::{pretty, DebugPls}; -/// #[derive(DebugPls)] -/// struct Point { -/// x: i32, -/// y: i32, -/// } -/// -/// let origin = Point { x: 0, y: 0 }; -/// -/// assert_eq!(format!("The origin is: {}", pretty(&origin)), "The origin is: Point { x: 0, y: 0 }"); -/// ``` -/// -/// Manually implementing: -/// -/// ``` -/// use dbg_pls::{pretty, DebugPls, Formatter}; -/// struct Point { -/// x: i32, -/// y: i32, -/// } -/// -/// impl DebugPls for Point { -/// fn fmt(&self, f: Formatter<'_>) { -/// f.debug_struct("Point") -/// .field("x", &self.x) -/// .field("y", &self.y) -/// .finish() -/// } -/// } -/// -/// let origin = Point { x: 0, y: 0 }; -/// -/// assert_eq!(format!("The origin is: {}", pretty(&origin)), "The origin is: Point { x: 0, y: 0 }"); -/// ``` pub trait DebugPls { - /// Formats the value using the given formatter. - /// - /// # Examples - /// - /// ``` - /// use dbg_pls::{pretty, DebugPls, Formatter}; - /// - /// struct Position { - /// longitude: f32, - /// latitude: f32, - /// } - /// - /// impl DebugPls for Position { - /// fn fmt(&self, f: Formatter<'_>) { - /// f.debug_tuple() - /// .field(&self.longitude) - /// .field(&self.latitude) - /// .finish() - /// } - /// } - /// - /// let position = Position { longitude: 1.987, latitude: 2.983 }; - /// assert_eq!(format!("{}", pretty(&position)), "(1.987, 2.983)"); - /// ``` fn fmt(&self, f: Formatter<'_>); } -/// Tool for formatting, used within [`DebugPls`] implementations pub struct Formatter<'a> { expr: &'a mut syn::Expr, } @@ -291,208 +34,40 @@ impl<'a> Formatter<'a> { expr } - /// Writes a wrap expression into the formatter. - /// This is typically reserved for more advanced uses pub fn write_expr(self, expr: impl Into) { *self.expr = expr.into(); } - /// Creates a [`DebugStruct`] builder designed to assist with creation of - /// [`DebugPls`] implementations for structs. - /// - /// # Examples - /// - /// ```rust - /// use dbg_pls::{pretty, DebugPls, Formatter}; - /// - /// struct Foo { - /// bar: i32, - /// baz: String, - /// } - /// - /// impl DebugPls for Foo { - /// fn fmt(&self, f: Formatter) { - /// f.debug_struct("Foo") - /// .field("bar", &self.bar) - /// .field("baz", &self.baz) - /// .finish() - /// } - /// } - /// let value = Foo { - /// bar: 10, - /// baz: "Hello World".to_string(), - /// }; - /// assert_eq!( - /// format!("{}", pretty(&value)), - /// "Foo { bar: 10, baz: \"Hello World\" }", - /// ); - /// ``` #[must_use] pub fn debug_struct(self, name: &str) -> DebugStruct<'a> { DebugStruct::new(self, name) } - /// Creates a [`DebugTuple`] builder designed to assist with creation of - /// [`DebugPls`] implementations for tuples. - /// - /// # Examples - /// - /// ```rust - /// use dbg_pls::{pretty, DebugPls, Formatter}; - /// - /// struct Foo(i32, String); - /// - /// impl DebugPls for Foo { - /// fn fmt(&self, f: Formatter) { - /// f.debug_tuple() - /// .field(&self.0) - /// .field(&self.1) - /// .finish() - /// } - /// } - /// - /// let value = Foo(10, "Hello".to_string()); - /// assert_eq!(format!("{}", pretty(&value)), "(10, \"Hello\")"); - /// ``` #[must_use] pub fn debug_tuple(self) -> DebugTuple<'a> { DebugTuple::new(self) } - /// Creates a [`DebugTupleStruct`] builder designed to assist with creation of - /// [`DebugPls`] implementations for tuple structs. - /// - /// # Examples - /// - /// ```rust - /// use dbg_pls::{pretty, DebugPls, Formatter}; - /// - /// struct Foo(i32, String); - /// - /// impl DebugPls for Foo { - /// fn fmt(&self, f: Formatter) { - /// f.debug_tuple_struct("Foo") - /// .field(&self.0) - /// .field(&self.1) - /// .finish() - /// } - /// } - /// - /// let value = Foo(10, "Hello".to_string()); - /// assert_eq!(format!("{}", pretty(&value)), "Foo(10, \"Hello\")"); - /// ``` #[must_use] pub fn debug_tuple_struct(self, name: &str) -> DebugTupleStruct<'a> { DebugTupleStruct::new(self, name) } - /// Creates a [`DebugList`] builder designed to assist with creation of - /// [`DebugPls`] implementations for list-like structures. - /// - /// # Examples - /// - /// ```rust - /// use dbg_pls::{pretty, DebugPls, Formatter}; - /// - /// struct Foo(Vec); - /// - /// impl DebugPls for Foo { - /// fn fmt(&self, f: Formatter<'_>) { - /// f.debug_list().entries(&self.0).finish() - /// } - /// } - /// - /// let value = Foo(vec![10, 11]); - /// assert_eq!(format!("{}", pretty(&value)), "[10, 11]"); - /// ``` #[must_use] pub fn debug_list(self) -> DebugList<'a> { DebugList::new(self) } - /// Creates a [`DebugMap`] builder designed to assist with creation of - /// [`DebugPls`] implementations for maps. - /// - /// # Examples - /// - /// ```rust - /// use dbg_pls::{pretty, DebugPls, Formatter}; - /// use std::collections::BTreeMap; - /// - /// struct Foo(BTreeMap); - /// - /// impl DebugPls for Foo { - /// fn fmt(&self, f: Formatter) { - /// f.debug_map().entries(&self.0).finish() - /// } - /// } - /// let mut value = Foo(BTreeMap::from([ - /// ("Hello".to_string(), 5), - /// ("World".to_string(), 10), - /// ])); - /// assert_eq!( - /// format!("{}", pretty(&value)), - /// "{ - /// [\"Hello\"] = 5; - /// [\"World\"] = 10; - /// }", - /// ); - /// ``` #[must_use] pub fn debug_map(self) -> DebugMap<'a> { DebugMap::new(self) } - /// Creates a [`DebugSet`] builder designed to assist with creation of - /// [`DebugPls`] implementations for sets. - /// - /// # Examples - /// - /// ```rust - /// use dbg_pls::{pretty, DebugPls, Formatter}; - /// use std::collections::BTreeSet; - /// - /// struct Foo(BTreeSet); - /// - /// impl DebugPls for Foo { - /// fn fmt(&self, f: Formatter) { - /// f.debug_set().entries(&self.0).finish() - /// } - /// } - /// let mut value = Foo(BTreeSet::from([ - /// "Hello".to_string(), - /// "World".to_string(), - /// ])); - /// assert_eq!( - /// format!("{}", pretty(&value)), - /// "{ - /// \"Hello\"; - /// \"World\" - /// }", - /// ); - /// ``` #[must_use] pub fn debug_set(self) -> DebugSet<'a> { DebugSet::new(self) } - /// Writes an identifier into the formatter. Useful for unit structs/variants - /// - /// # Examples - /// - /// ```rust - /// use dbg_pls::{pretty, DebugPls, Formatter}; - /// - /// struct Foo; - /// - /// impl DebugPls for Foo { - /// fn fmt(&self, f: Formatter<'_>) { - /// f.debug_ident("Foo"); - /// } - /// } - /// - /// assert_eq!(format!("{}", pretty(&Foo)), "Foo"); - /// ``` pub fn debug_ident(self, name: &str) { let path: syn::Path = syn::Ident::new(name, Span::call_site()).into(); self.write_expr(syn::ExprPath { @@ -502,206 +77,3 @@ impl<'a> Formatter<'a> { }); } } - -#[cfg(test)] -mod tests { - use std::collections::{BTreeMap, BTreeSet}; - - use super::*; - - #[derive(DebugPls, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] - #[dbg_pls(crate = "crate")] - pub struct Demo { - foo: i32, - bar: &'static str, - } - - #[test] - fn debug_struct() { - let val = Demo { - foo: 5, - bar: "hello", - }; - assert_eq!(pretty(&val).to_string(), r#"Demo { foo: 5, bar: "hello" }"#); - } - - #[test] - fn debug_struct_big() { - let val = Demo { - foo: 5, - bar: "Hello, world! I am a very long string", - }; - assert_eq!( - pretty(&val).to_string(), - r#"Demo { - foo: 5, - bar: "Hello, world! I am a very long string", -}"# - ); - } - - #[test] - fn debug_nested_struct() { - let mut val = [Demo { - foo: 5, - bar: "hello", - }; 10]; - val[6].bar = "Hello, world! I am a very long string"; - - assert_eq!( - pretty(&val).to_string(), - r#"[ - Demo { foo: 5, bar: "hello" }, - Demo { foo: 5, bar: "hello" }, - Demo { foo: 5, bar: "hello" }, - Demo { foo: 5, bar: "hello" }, - Demo { foo: 5, bar: "hello" }, - Demo { foo: 5, bar: "hello" }, - Demo { - foo: 5, - bar: "Hello, world! I am a very long string", - }, - Demo { foo: 5, bar: "hello" }, - Demo { foo: 5, bar: "hello" }, - Demo { foo: 5, bar: "hello" }, -]"# - ); - } - - #[test] - fn debug_small_set() { - let set = BTreeSet::from([420, 69]); - - assert_eq!( - pretty(&set).to_string(), - r#"{ - 69; - 420 -}"# - ); - } - - #[test] - fn debug_nested_set() { - let set = BTreeSet::from([ - Demo { - foo: 5, - bar: "hello", - }, - Demo { - foo: 5, - bar: "Hello, world! I am a very long string", - }, - ]); - - assert_eq!( - pretty(&set).to_string(), - r#"{ - Demo { - foo: 5, - bar: "Hello, world! I am a very long string", - }; - Demo { foo: 5, bar: "hello" } -}"# - ); - } - - #[test] - fn debug_map() { - let map = BTreeMap::from([("hello", 60), ("Hello, world! I am a very long string", 12)]); - - assert_eq!( - pretty(&map).to_string(), - r#"{ - ["Hello, world! I am a very long string"] = 12; - ["hello"] = 60; -}"# - ); - } - - #[test] - fn debug_nested_map() { - let map = BTreeMap::from([ - ( - Demo { - foo: 5, - bar: "hello", - }, - 60, - ), - ( - Demo { - foo: 5, - bar: "Hello, world! I am a very long string", - }, - 12, - ), - ]); - - assert_eq!( - pretty(&map).to_string(), - r#"{ - [ - Demo { - foo: 5, - bar: "Hello, world! I am a very long string", - }, - ] = 12; - [Demo { foo: 5, bar: "hello" }] = 60; -}"# - ); - } - - #[derive(DebugPls)] - #[dbg_pls(crate = "crate")] - pub struct Generic { - arg: T, - } - - #[test] - fn debug_generic() { - let generic = Generic { arg: "string" }; - assert_eq!(pretty(&generic).to_string(), r#"Generic { arg: "string" }"#); - } - - #[derive(DebugPls)] - #[dbg_pls(crate = "crate")] - pub struct Generic2 { - arg: Wrapped, - } - - pub struct Wrapped(T); - impl DebugPls for Wrapped { - fn fmt(&self, f: Formatter<'_>) { - f.debug_struct("Wrapped").finish_non_exhaustive(); - } - } - - pub struct NonDebug; - - #[test] - fn debug_generic2() { - let generic = Generic { arg: Wrapped(NonDebug) }; - assert_eq!(pretty(&generic).to_string(), r#"Generic { arg: Wrapped {} }"#); - } - - #[derive(DebugPls)] - #[dbg_pls(crate = "crate")] - pub enum Option2 { - Some(T), - None, - Wtf { foo: i32 } - } - - #[test] - fn debug_enum_generic() { - let some = Option2::Some(42); - assert_eq!(pretty(&some).to_string(), r#"Some(42)"#); - - let none = Option2::::None; - assert_eq!(pretty(&none).to_string(), r#"None"#); - - let wtf = Option2::::Wtf { foo: 42 }; - assert_eq!(pretty(&wtf).to_string(), r#"Wtf { foo: 42 }"#); - } -} diff --git a/dbg-pls/src/pretty.rs b/dbg-pls/src/pretty.rs deleted file mode 100644 index 953bf38..0000000 --- a/dbg-pls/src/pretty.rs +++ /dev/null @@ -1,149 +0,0 @@ -use syn::__private::Span; - -use crate::{DebugPls, Formatter}; - -pub(crate) fn pretty_string(expr: syn::Expr) -> String { - // unparse requires a `syn::File`, so we are forced to wrap - // our expression in some junk. This is equivalent to - // ```rust - // const _: () = { - // #expr - // }; - // ``` - let file = syn::File { - shebang: None, - attrs: vec![], - items: vec![syn::Item::Const(syn::ItemConst { - expr: Box::new(expr), - // junk... - attrs: vec![], - vis: syn::Visibility::Inherited, - const_token: syn::token::Const::default(), - ident: syn::Ident::new("_", Span::call_site()), - colon_token: syn::token::Colon::default(), - ty: Box::new(syn::Type::Tuple(syn::TypeTuple { - paren_token: syn::token::Paren::default(), - elems: syn::punctuated::Punctuated::default(), - })), - eq_token: syn::token::Eq::default(), - semi_token: syn::token::Semi::default(), - })], - }; - let output = prettyplease::unparse(&file); - - // strip out the junk - let output = &output[14..]; - let output = &output[..output.len() - 2]; - textwrap::dedent(output) -} - -/// Implementation detail for the `pretty!` macro -pub struct Str<'a>(pub &'a str); - -impl<'a> std::fmt::Display for Str<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let expr = syn::parse_str(self.0).map_err(|_| std::fmt::Error)?; - f.write_str(&pretty_string(expr)) - } -} - -struct Pretty<'a>(&'a dyn DebugPls); - -impl<'a> std::fmt::Debug for Pretty<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(&pretty_string(Formatter::process(self.0))) - } -} - -impl<'a> std::fmt::Display for Pretty<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Debug::fmt(self, f) - } -} - -#[cfg_attr(docsrs, doc(cfg(feature = "pretty")))] -/// Wraps a [`Debug`] type into a [`std::fmt::Debug`] type for use in regular [`format!`] -pub fn pretty(value: &impl DebugPls) -> impl std::fmt::Debug + std::fmt::Display + '_ { - Pretty(value) -} - -#[cfg_attr(docsrs, doc(cfg(feature = "pretty")))] -#[macro_export] -/// Prints and returns the value of a given expression for quick and dirty -/// debugging. Same as [`std::dbg`] -/// -/// An example: -/// -/// ```rust -/// # use dbg_pls::pretty; -/// let a = 2; -/// let b = pretty!(a * 2) + 1; -/// // ^-- prints: [src/main.rs:2] a * 2 = 4 -/// assert_eq!(b, 5); -/// ``` -/// -/// The macro works by using the [`DebugPls`] implementation of the type of -/// the given expression to print the value to [stderr] along with the -/// source location of the macro invocation as well as the source code -/// of the expression. -/// -/// Invoking the macro on an expression moves and takes ownership of it -/// before returning the evaluated expression unchanged. If the type -/// of the expression does not implement `Copy` and you don't want -/// to give up ownership, you can instead borrow with `pretty!(&expr)` -/// for some expression `expr`. -/// -/// The `pretty!` macro works exactly the same in release builds. -/// This is useful when debugging issues that only occur in release -/// builds or when debugging in release mode is significantly faster. -/// -/// Note that the macro is intended as a debugging tool and therefore you -/// should avoid having uses of it in version control for long periods -/// (other than in tests and similar). -/// Debug output from production code is better done with other facilities -/// such as the [`debug!`] macro from the [`log`] crate. -/// -/// [stderr]: https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr) -/// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html -/// [`log`]: https://crates.io/crates/log -macro_rules! pretty { - () => { - ::std::eprintln!("[{}:{}]", ::std::file!(), ::std::line!()) - }; - ($val:expr $(,)?) => { - match $val { - tmp => { - ::std::eprintln!( - "[{}:{}] {} => {}", - ::std::file!(), - ::std::line!(), - $crate::__private::PrettyStr(::std::stringify!($val)), - $crate::pretty(&tmp) - ); - tmp - } - } - }; - ($($val:expr),+ $(,)?) => { - ($($crate::pretty!($val)),+,) - }; -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use crate::pretty; - - #[test] - fn pretty_macro() { - let map = pretty! { - HashMap::from([ - ("hello", 1), - ("world", 2), - ]) - }; - // map is moved through properly - assert_eq!(map, HashMap::from([("hello", 1), ("world", 2),])); - } -} diff --git a/src/lib.rs b/src/lib.rs index d57a06c..0943d4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,4 +35,4 @@ where Parent<'a>: dbg_pls::DebugPls, { fn fmt(&self, f: dbg_pls::Formatter<'_>) {} -} \ No newline at end of file +}