Skip to content

Commit a56f514

Browse files
committed
implement trait detection based on inherent method priority
1 parent dedc527 commit a56f514

File tree

8 files changed

+223
-87
lines changed

8 files changed

+223
-87
lines changed

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
[package]
22
name = "partialdebug"
33
description = "Derive Debug partially"
4-
version = "0.1.1"
4+
version = "0.2.0"
55
authors = ["Dimitri Polonski"]
66
edition = "2018"
77
repository = "https://github.com/dimpolo/partialdebug/"
88
license = "MIT OR Apache-2.0"
99

10+
11+
[features]
12+
unstable = ["partialdebug-derive/unstable"]
13+
1014
[dependencies]
11-
partialdebug-derive = "0.1.1"
15+
partialdebug-derive = "0.2.0"

README.md

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,13 @@
55

66
Derive Debug for types where not all fields implement Debug.
77

8-
This relies on specialization and thus requires nightly.
8+
This crate works on stable and with `no_std`.
9+
On nightly the `unstable` feature can be used for specialization based trait detection and/or `..` formatting.
910

10-
This crate works with 'no_std'.
11-
12-
#### Non Exhaustive
13-
14-
Requires the `debug_non_exhaustive` feature.
15-
Only available for structs with named fields.
11+
#### Placeholder with Type Info
1612

1713
```rust
18-
#![feature(debug_non_exhaustive)]
19-
use partialdebug::non_exhaustive::PartialDebug;
14+
use partialdebug::placeholder::PartialDebug;
2015

2116
#[derive(PartialDebug)]
2217
struct Dog {
@@ -25,40 +20,63 @@ struct Dog {
2520
dna: DNA,
2621
}
2722

28-
assert_eq!(format!("{:?}", Dog::new()), "Dog { legs: 4, eyes: 2, .. }");
23+
assert_eq!(format!("{:?}", Dog::new()), "Dog { legs: 4, eyes: 2, dna: DNA }");
2924
```
3025

31-
#### Placeholder with Type Info
26+
#### Placeholder with Custom Text
3227

3328
```rust
3429
use partialdebug::placeholder::PartialDebug;
3530

3631
#[derive(PartialDebug)]
32+
#[debug_placeholder = "Unknown"]
3733
struct Dog {
3834
legs: usize,
3935
eyes: usize,
4036
dna: DNA,
4137
}
4238

43-
assert_eq!(format!("{:?}", Dog::new()), "Dog { legs: 4, eyes: 2, dna: DNA }");
39+
assert_eq!(format!("{:?}", Dog::new()), "Dog { legs: 4, eyes: 2, dna: Unknown }");
4440
```
4541

46-
#### Placeholder with Custom Text
42+
#### Non Exhaustive
43+
44+
Only available on nightly after setting the `unstable` feature.
45+
46+
Requires the `debug_non_exhaustive` feature to be enabled in user code.
47+
48+
Only available for structs with named fields.
4749

4850
```rust
49-
use partialdebug::placeholder::PartialDebug;
51+
#![feature(debug_non_exhaustive)]
52+
use partialdebug::non_exhaustive::PartialDebug;
5053

5154
#[derive(PartialDebug)]
52-
#[debug_placeholder = "Unknown"]
5355
struct Dog {
5456
legs: usize,
5557
eyes: usize,
5658
dna: DNA,
5759
}
5860

59-
assert_eq!(format!("{:?}", Dog::new()), "Dog { legs: 4, eyes: 2, dna: Unknown }");
61+
assert_eq!(format!("{:?}", Dog::new()), "Dog { legs: 4, eyes: 2, .. }");
6062
```
6163

64+
#### Caveats
65+
66+
Trait detection for generic types requires specialization.
67+
To enable specialization based trait detection use a nightly compiler and enable the `unstable` feature.
68+
69+
```rust
70+
use partialdebug::placeholder::PartialDebug;
71+
72+
#[derive(PartialDebug)]
73+
struct Container<T>(T);
74+
75+
#[cfg(feature = "unstable")]
76+
assert_eq!(format!("{:?}", Container(42)), "Container(42)");
77+
#[cfg(not(feature = "unstable"))]
78+
assert_eq!(format!("{:?}", Container(42)), "Container(T)");
79+
```
6280
#### License
6381
<sup>
6482
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version

partialdebug-derive/Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
[package]
22
name = "partialdebug-derive"
33
description = "macros for partialdebug"
4-
version = "0.1.1"
4+
version = "0.2.0"
55
authors = ["Dimitri Polonski"]
66
edition = "2018"
77
repository = "https://github.com/dimpolo/partialdebug/"
88
license = "MIT OR Apache-2.0"
99

10+
[package.metadata.docs.rs]
11+
all-features = true
12+
13+
[features]
14+
unstable = []
15+
1016
[lib]
1117
proc-macro = true
1218

partialdebug-derive/src/lib.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ use syn::*;
88

99
/// The non exhaustive version of `PartialDebug`
1010
///
11-
/// Requires the `debug_non_exhaustive` feature.
11+
/// Requires the `unstable` feature.
1212
/// Only available for structs with named fields.
13+
#[cfg(feature = "unstable")]
1314
#[proc_macro_derive(NonExhaustivePartialDebug)]
1415
pub fn derive_non_exhaustive(input: TokenStream) -> TokenStream {
1516
let input = parse_macro_input!(input as ItemStruct);
@@ -29,7 +30,7 @@ pub fn derive_non_exhaustive(input: TokenStream) -> TokenStream {
2930
let as_debug_all_fields = fields.iter().map(|field| {
3031
let name = &field.ident;
3132
quote! {
32-
match ::partialdebug::AsDebug::as_debug(&self. #name) {
33+
match ::partialdebug::specialization::AsDebug::as_debug(&self. #name) {
3334
None => {
3435
__exhaustive = false;
3536
}
@@ -178,6 +179,7 @@ fn enum_field_conversions<'a>(
178179
})
179180
}
180181

182+
#[cfg(feature = "unstable")]
181183
fn gen_field_as_debug(
182184
field: &Field,
183185
placeholder: &Option<String>,
@@ -192,7 +194,31 @@ fn gen_field_as_debug(
192194
quote! {
193195
.field(
194196
#name_arg
195-
match ::partialdebug::AsDebug::as_debug(&#field_handle){
197+
match ::partialdebug::specialization::AsDebug::as_debug(&#field_handle){
198+
None => &::partialdebug::Placeholder(#placeholder_string),
199+
Some(__field) => __field,
200+
},
201+
)
202+
}
203+
}
204+
205+
#[cfg(not(feature = "unstable"))]
206+
fn gen_field_as_debug(
207+
field: &Field,
208+
placeholder: &Option<String>,
209+
field_handle: TokenStream2,
210+
name_arg: Option<TokenStream2>,
211+
) -> TokenStream2 {
212+
let type_name = get_type_name(&field.ty);
213+
let field_type = &field.ty;
214+
215+
// type name or given placeholder string
216+
let placeholder_string = placeholder.as_ref().unwrap_or(&type_name);
217+
218+
quote! {
219+
.field(
220+
#name_arg
221+
match ::partialdebug::no_specialization::DebugDetector::<#field_type>::as_debug(&#field_handle){
196222
None => &::partialdebug::Placeholder(#placeholder_string),
197223
Some(__field) => __field,
198224
},

src/lib.rs

Lines changed: 69 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
//! Derive Debug for types where not all fields implement Debug.
22
//!
3-
//! This relies on specialization and thus requires nightly.
3+
//! This crate works on stable and with `no_std`.
4+
//! On nightly the `unstable` feature can be used for specialization based trait detection and/or `..` formatting.
45
//!
5-
//! This crate works with 'no_std'.
6-
//!
7-
//! ### Non Exhaustive
8-
//!
9-
//! Requires the `debug_non_exhaustive` feature.
10-
//! Only available for structs with named fields.
6+
//! ### Placeholder with Type Info
117
//!
128
//! ```
13-
//! #![feature(debug_non_exhaustive)]
14-
//! use partialdebug::non_exhaustive::PartialDebug;
9+
//! use partialdebug::placeholder::PartialDebug;
1510
//!
1611
//! # struct DNA;
1712
//! #
@@ -32,17 +27,18 @@
3227
//! # }
3328
//! # }
3429
//! #
35-
//! assert_eq!(format!("{:?}", Dog::new()), "Dog { legs: 4, eyes: 2, .. }");
30+
//! assert_eq!(format!("{:?}", Dog::new()), "Dog { legs: 4, eyes: 2, dna: DNA }");
3631
//! ```
3732
//!
38-
//! ### Placeholder with Type Info
33+
//! ### Placeholder with Custom Text
3934
//!
4035
//! ```
4136
//! use partialdebug::placeholder::PartialDebug;
4237
//!
4338
//! # struct DNA;
4439
//! #
4540
//! #[derive(PartialDebug)]
41+
//! #[debug_placeholder = "Unknown"]
4642
//! struct Dog {
4743
//! legs: usize,
4844
//! eyes: usize,
@@ -59,69 +55,70 @@
5955
//! # }
6056
//! # }
6157
//! #
62-
//! assert_eq!(format!("{:?}", Dog::new()), "Dog { legs: 4, eyes: 2, dna: DNA }");
58+
//! assert_eq!(format!("{:?}", Dog::new()), "Dog { legs: 4, eyes: 2, dna: Unknown }");
6359
//! ```
6460
//!
65-
//! ### Placeholder with Custom Text
61+
//! ### Non Exhaustive
62+
//!
63+
//! Only available on nightly after setting the `unstable` feature.
64+
//!
65+
//! Requires the `debug_non_exhaustive` feature to be enabled in user code.
66+
//!
67+
//! Only available for structs with named fields.
68+
69+
#![cfg_attr(
70+
feature = "unstable",
71+
doc = r##"
72+
```
73+
#![feature(debug_non_exhaustive)]
74+
use partialdebug::non_exhaustive::PartialDebug;
75+
# struct DNA;
76+
#
77+
#[derive(PartialDebug)]
78+
struct Dog {
79+
legs: usize,
80+
eyes: usize,
81+
dna: DNA,
82+
}
83+
# impl Dog {
84+
# fn new() -> Dog {
85+
# Dog {
86+
# legs: 4,
87+
# eyes: 2,
88+
# dna: DNA,
89+
# }
90+
# }
91+
# }
92+
#
93+
assert_eq!(format!("{:?}", Dog::new()), "Dog { legs: 4, eyes: 2, .. }");
94+
```
95+
"##
96+
)]
97+
98+
//! ### Caveats
99+
//!
100+
//! Trait detection for generic types requires specialization.
101+
//! To enable specialization based trait detection use a nightly compiler and enable the `unstable` feature.
66102
//!
67103
//! ```
68104
//! use partialdebug::placeholder::PartialDebug;
69105
//!
70-
//! # struct DNA;
71-
//! #
72106
//! #[derive(PartialDebug)]
73-
//! #[debug_placeholder = "Unknown"]
74-
//! struct Dog {
75-
//! legs: usize,
76-
//! eyes: usize,
77-
//! dna: DNA,
78-
//! }
107+
//! struct Container<T>(T);
79108
//!
80-
//! # impl Dog {
81-
//! # fn new() -> Dog {
82-
//! # Dog {
83-
//! # legs: 4,
84-
//! # eyes: 2,
85-
//! # dna: DNA,
86-
//! # }
87-
//! # }
88-
//! # }
89-
//! #
90-
//! assert_eq!(format!("{:?}", Dog::new()), "Dog { legs: 4, eyes: 2, dna: Unknown }");
109+
//! #[cfg(feature = "unstable")]
110+
//! assert_eq!(format!("{:?}", Container(42)), "Container(42)");
111+
//! #[cfg(not(feature = "unstable"))]
112+
//! assert_eq!(format!("{:?}", Container(42)), "Container(T)");
91113
//! ```
92114
93115
#![no_std]
94-
#![allow(incomplete_features)]
95-
#![feature(specialization)]
96116
#![warn(missing_docs, trivial_casts, rust_2018_idioms)]
117+
#![cfg_attr(feature = "unstable", allow(incomplete_features))]
118+
#![cfg_attr(feature = "unstable", feature(specialization))]
97119

98120
use core::fmt::{Debug, Formatter, Result};
99121

100-
/// Specialized trait used to distinguish between types that implement Debug and one's that don't.
101-
/// ```
102-
/// # use partialdebug::AsDebug;
103-
/// # struct DNA;
104-
/// # let dna = DNA;
105-
/// assert!(42.as_debug().is_some());
106-
/// assert!(dna.as_debug().is_none());
107-
/// ```
108-
pub trait AsDebug {
109-
/// Try to get a reference to `self` as `dyn Debug`
110-
fn as_debug(&self) -> Option<&dyn Debug>;
111-
}
112-
113-
impl<T> AsDebug for T {
114-
default fn as_debug(&self) -> Option<&dyn Debug> {
115-
None
116-
}
117-
}
118-
119-
impl<T: Debug> AsDebug for T {
120-
fn as_debug(&self) -> Option<&dyn Debug> {
121-
Some(self)
122-
}
123-
}
124-
125122
/// Placeholder struct for types that do not implement Debug
126123
/// ```
127124
/// # use partialdebug::Placeholder;
@@ -135,12 +132,21 @@ impl Debug for Placeholder {
135132
}
136133
}
137134

138-
/// The non exhaustive version of `PartialDebug`
139-
pub mod non_exhaustive {
140-
pub use partialdebug_derive::NonExhaustivePartialDebug as PartialDebug;
141-
}
135+
#[cfg(any(not(feature = "unstable"), doc))]
136+
pub mod no_specialization;
137+
/// Trait detection logic using specialization
138+
#[cfg(feature = "unstable")]
139+
pub mod specialization;
142140

143141
/// The placeholder version of `PartialDebug`
144142
pub mod placeholder {
143+
#[cfg(not(feature = "unstable"))]
144+
pub use crate::no_specialization::NotDebug as PartialDebug; // needs to be in scope
145145
pub use partialdebug_derive::PlaceholderPartialDebug as PartialDebug;
146146
}
147+
148+
/// The non exhaustive version of `PartialDebug`
149+
#[cfg(feature = "unstable")]
150+
pub mod non_exhaustive {
151+
pub use partialdebug_derive::NonExhaustivePartialDebug as PartialDebug;
152+
}

0 commit comments

Comments
 (0)