mirror of
https://github.com/Noratrieb/dilaria.git
synced 2026-01-16 18:35:02 +01:00
delete a bunch of stuff in dbg-pls
This commit is contained in:
parent
57ba4cef3c
commit
1ec9a9df34
16 changed files with 65 additions and 1289 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
||||||
/target
|
/target
|
||||||
|
dbg-pls/target
|
||||||
|
|
||||||
.idea
|
.idea
|
||||||
*.iml
|
*.iml
|
||||||
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -7,7 +7,6 @@ name = "dbg-pls"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"once_cell",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
|
@ -27,12 +26,6 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.37"
|
version = "1.0.37"
|
||||||
|
|
|
||||||
2
dbg-pls/.gitignore
vendored
2
dbg-pls/.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
/target
|
|
||||||
Cargo.lock
|
|
||||||
61
dbg-pls/Cargo.lock
generated
Normal file
61
dbg-pls/Cargo.lock
generated
Normal file
|
|
@ -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"
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
[workspace]
|
|
||||||
members = ["debug-derive"]
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "dbg-pls"
|
name = "dbg-pls"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
|
@ -11,13 +8,6 @@ license = "MIT"
|
||||||
repository = "https://github.com/conradludgate/dbg-pls"
|
repository = "https://github.com/conradludgate/dbg-pls"
|
||||||
readme = "README.md"
|
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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
@ -27,25 +17,8 @@ quote = "1"
|
||||||
itoa = "1"
|
itoa = "1"
|
||||||
ryu = "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]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
derive = ["dbg-pls-derive"]
|
|
||||||
pretty = ["prettyplease", "textwrap"]
|
|
||||||
colors = ["pretty", "syntect"]
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "dbg-pls-derive"
|
|
||||||
version = "0.2.1"
|
|
||||||
authors = ["Conrad Ludgate <conradludgate@gmail.com>"]
|
|
||||||
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"
|
|
||||||
|
|
@ -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<T>(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)
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<Path> {
|
|
||||||
fn parse_crate(lit: syn::Lit) -> syn::Result<Path> {
|
|
||||||
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<syn::Result<Path>> {
|
|
||||||
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 })
|
|
||||||
}
|
|
||||||
|
|
@ -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<T>(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<Item = Ident> + '_ {
|
|
||||||
fields
|
|
||||||
.unnamed
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, field)| format_ident!("val{}", i, span = field.span()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn named_idents(fields: &FieldsNamed) -> impl Iterator<Item = &Ident> + '_ {
|
|
||||||
fields
|
|
||||||
.named
|
|
||||||
.iter()
|
|
||||||
.map(|field| field.ident.as_ref().unwrap())
|
|
||||||
}
|
|
||||||
|
|
@ -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 => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<SyntaxSet> = 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<Theme> = 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),]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
mod syn_impls;
|
|
||||||
mod std;
|
mod std;
|
||||||
|
mod syn_impls;
|
||||||
|
|
|
||||||
|
|
@ -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};
|
use crate::{DebugPls, Formatter};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,171 +1,6 @@
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![warn(clippy::pedantic)]
|
#![warn(clippy::pedantic)]
|
||||||
#![forbid(unsafe_code)]
|
#![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:
|
|
||||||
//!
|
|
||||||
//! 
|
|
||||||
//!
|
|
||||||
//! # 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
|
|
||||||
//! <https://github.com/jonschlinkert/sublime-monokai-extended>
|
|
||||||
|
|
||||||
use syn::__private::{Span, TokenStream2};
|
use syn::__private::{Span, TokenStream2};
|
||||||
|
|
||||||
|
|
@ -184,102 +19,10 @@ pub use debug_struct::DebugStruct;
|
||||||
pub use debug_tuple::DebugTuple;
|
pub use debug_tuple::DebugTuple;
|
||||||
pub use debug_tuple_struct::DebugTupleStruct;
|
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 {
|
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<'_>);
|
fn fmt(&self, f: Formatter<'_>);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tool for formatting, used within [`DebugPls`] implementations
|
|
||||||
pub struct Formatter<'a> {
|
pub struct Formatter<'a> {
|
||||||
expr: &'a mut syn::Expr,
|
expr: &'a mut syn::Expr,
|
||||||
}
|
}
|
||||||
|
|
@ -291,208 +34,40 @@ impl<'a> Formatter<'a> {
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes a wrap expression into the formatter.
|
|
||||||
/// This is typically reserved for more advanced uses
|
|
||||||
pub fn write_expr(self, expr: impl Into<syn::Expr>) {
|
pub fn write_expr(self, expr: impl Into<syn::Expr>) {
|
||||||
*self.expr = expr.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]
|
#[must_use]
|
||||||
pub fn debug_struct(self, name: &str) -> DebugStruct<'a> {
|
pub fn debug_struct(self, name: &str) -> DebugStruct<'a> {
|
||||||
DebugStruct::new(self, name)
|
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]
|
#[must_use]
|
||||||
pub fn debug_tuple(self) -> DebugTuple<'a> {
|
pub fn debug_tuple(self) -> DebugTuple<'a> {
|
||||||
DebugTuple::new(self)
|
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]
|
#[must_use]
|
||||||
pub fn debug_tuple_struct(self, name: &str) -> DebugTupleStruct<'a> {
|
pub fn debug_tuple_struct(self, name: &str) -> DebugTupleStruct<'a> {
|
||||||
DebugTupleStruct::new(self, name)
|
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<i32>);
|
|
||||||
///
|
|
||||||
/// 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]
|
#[must_use]
|
||||||
pub fn debug_list(self) -> DebugList<'a> {
|
pub fn debug_list(self) -> DebugList<'a> {
|
||||||
DebugList::new(self)
|
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<String, i32>);
|
|
||||||
///
|
|
||||||
/// 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]
|
#[must_use]
|
||||||
pub fn debug_map(self) -> DebugMap<'a> {
|
pub fn debug_map(self) -> DebugMap<'a> {
|
||||||
DebugMap::new(self)
|
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<String>);
|
|
||||||
///
|
|
||||||
/// 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]
|
#[must_use]
|
||||||
pub fn debug_set(self) -> DebugSet<'a> {
|
pub fn debug_set(self) -> DebugSet<'a> {
|
||||||
DebugSet::new(self)
|
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) {
|
pub fn debug_ident(self, name: &str) {
|
||||||
let path: syn::Path = syn::Ident::new(name, Span::call_site()).into();
|
let path: syn::Path = syn::Ident::new(name, Span::call_site()).into();
|
||||||
self.write_expr(syn::ExprPath {
|
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<T> {
|
|
||||||
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<T> {
|
|
||||||
arg: Wrapped<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Wrapped<T>(T);
|
|
||||||
impl<T> DebugPls for Wrapped<T> {
|
|
||||||
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<T> {
|
|
||||||
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::<i32>::None;
|
|
||||||
assert_eq!(pretty(&none).to_string(), r#"None"#);
|
|
||||||
|
|
||||||
let wtf = Option2::<i32>::Wtf { foo: 42 };
|
|
||||||
assert_eq!(pretty(&wtf).to_string(), r#"Wtf { foo: 42 }"#);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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),]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue