Skip to content

Commit 84379ef

Browse files
committed
Flag support
1 parent fb67963 commit 84379ef

File tree

4 files changed

+56
-25
lines changed

4 files changed

+56
-25
lines changed

README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
# attribute-derive
22

3-
This is just a quick implementation to be able to implement better macros for
4-
[bonsaidb](https://github.com/khonsulabs/bonsaidb).
5-
6-
Actual documentation will be added at some point.
3+
You will be able to find the documentation on docs.rs.

macro/src/lib.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,19 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
153153
parsing.push(quote! {
154154
#ident_str => {
155155
__options.#ident = #some(
156-
#syn::parse::Parse::parse(__input)#error?
156+
if let #some(#some(__value)) = __is_flag.then(|| <#ty as ::attribute_derive::ConvertParsed>::as_flag()) {
157+
__value
158+
} else {
159+
__input.step(|__cursor| match __cursor.punct() {
160+
#some((__punct, __rest))
161+
if __punct.as_char() == '=' && __punct.spacing() == #pm2::Spacing::Alone =>
162+
{
163+
#ok(((), __rest))
164+
}
165+
_ => #err(__cursor.error("Expected assignment `=`")),
166+
})?;
167+
#syn::parse::Parse::parse(__input)#error?
168+
}
157169
);
158170
}
159171
});
@@ -219,15 +231,7 @@ pub fn attribute_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
219231

220232
let __variable = #syn::Ident::parse(__input)?;
221233

222-
// Parse `=`
223-
__input.step(|__cursor| match __cursor.punct() {
224-
#some((__punct, __rest))
225-
if __punct.as_char() == '=' && __punct.spacing() == #pm2::Spacing::Alone =>
226-
{
227-
#ok(((), __rest))
228-
}
229-
_ => #err(__cursor.error("Expected assignment `=`")),
230-
})?;
234+
let __is_flag = !__input.peek(#syn::Token!(=));
231235

232236
match __variable.to_string().as_str() {
233237
#parsing

src/lib.rs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! Basic gist is a struct like this:
2-
//! ```
2+
//! ```ignore
33
//! #[derive(Attribute)]
44
//! #[attribute(ident = "collection")]
55
//! #[attribute(invalid_field = "Error when an unsupported value is set (e.g. meaning=42")]
@@ -17,7 +17,7 @@
1717
//! ```
1818
//!
1919
//! Will be able to parse an attribute like this:
20-
//! ```
20+
//! ```ignore
2121
//! #[collection(authority="Some String", name = r#"Another string"#, views = [Option, ()])]
2222
//! ```
2323
//!
@@ -34,11 +34,10 @@ use proc_macro2::{Literal, Span};
3434
pub use r#macro::Attribute;
3535
use syn::{
3636
bracketed, parse::Parse, punctuated::Punctuated, Expr, Lit, LitBool, LitByteStr, LitChar,
37-
LitFloat, LitInt, LitStr, Path, Result, Token, Type, __private::ToTokens,
37+
LitFloat, LitInt, LitStr, Path, Result, Token, Type, __private::ToTokens, parse_quote,
3838
};
3939

4040
#[deny(missing_docs)]
41-
4241
#[doc(hidden)]
4342
pub mod __private {
4443
pub use proc_macro2;
@@ -48,7 +47,7 @@ pub mod __private {
4847
/// The trait you actually derive on your attribute struct.
4948
///
5049
/// Basic gist is a struct like this:
51-
/// ```
50+
/// ```ignore
5251
/// #[derive(Attribute)]
5352
/// #[attribute(ident = "collection")]
5453
/// #[attribute(invalid_field = "Error when an unsupported value is set (e.g. meaning=42")]
@@ -62,12 +61,15 @@ pub mod __private {
6261
/// #[attribute(default)]
6362
/// #[attribute(expected = "Error when an error occured while parsing")]
6463
/// views: Vec<Type>,
64+
/// // Booleans can be used without assiging a value. as a flag.
65+
/// // If omitted they are set to false
66+
/// some_flag: bool
6567
/// }
6668
/// ```
6769
///
6870
/// Will be able to parse an attribute like this:
69-
/// ```
70-
/// #[collection(authority="Some String", name = r#"Another string"#, views = [Option, ()])]
71+
/// ```ignore
72+
/// #[collection(authority="Some String", name = r#"Another string"#, views = [Option, ()], some_flag)]
7173
/// ```
7274
pub trait Attribute
7375
where
@@ -77,7 +79,7 @@ where
7779
/// [`Vec<Attribute>`](Vec).
7880
///
7981
/// It can therefore parse fields set over multiple attributes like:
80-
/// ```
82+
/// ```ignore
8183
/// #[collection(authority = "Authority", name = "Name")]
8284
/// #[collection(views = [A, B])]
8385
/// ```
@@ -118,6 +120,11 @@ where
118120
fn default() -> Self {
119121
unreachable!("default_by_default should only return true if this is overridden")
120122
}
123+
/// Should values of this type be able to be defined as flag i.e. just `#[attr(default)]`
124+
/// instead of `#[attr(default=true)]`
125+
fn as_flag() -> Option<Self::Type> {
126+
None
127+
}
121128
}
122129

123130
/// Helper trait to generate sensible errors
@@ -214,6 +221,26 @@ where
214221
}
215222
}
216223

224+
impl ConvertParsed for bool {
225+
type Type = LitBool;
226+
227+
fn convert(value: Self::Type) -> Result<Self> {
228+
Ok(value.value)
229+
}
230+
231+
fn default_by_default() -> bool {
232+
true
233+
}
234+
235+
fn default() -> Self {
236+
false
237+
}
238+
239+
fn as_flag() -> Option<Self::Type> {
240+
Some(parse_quote!(true))
241+
}
242+
}
243+
217244
/// Helper struct to parse array literals:
218245
/// `[a, b, c]`
219246
pub struct Array<T> {
@@ -253,7 +280,6 @@ convert_parsed!(LitStr => String: LitStr::value);
253280
convert_parsed!(LitChar => char: LitChar::value);
254281
convert_parsed!(LitInt => u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize:? LitInt::base10_parse);
255282
convert_parsed!(LitFloat => f32, f64:? LitFloat::base10_parse);
256-
convert_parsed!(LitBool => bool: LitBool::value);
257283

258284
// TODO convert most of these
259285
// impl Parse for Group

tests/derive.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ fn test() {
1515
d: Type,
1616
e: Expr,
1717
f: Vec<Type>,
18+
g: bool,
19+
h: bool,
1820
}
1921

2022
let parsed = Test::from_attributes([
21-
parse_quote!(#[test(b="hi", c="ho", oc="xD", d=(), e=if true{ "a" } else { "b" }, f= [(), Debug])]),
23+
parse_quote!(#[test(b="hi", c="ho", oc="xD", d=(), e=if true{ "a" } else { "b" }, f= [(), Debug], g)]),
2224
])
2325
.unwrap();
2426
assert_eq!(parsed.b.value(), "hi");
@@ -28,6 +30,8 @@ fn test() {
2830
assert!(matches!(parsed.d, Type::Tuple(_)));
2931
assert!(matches!(parsed.e, Expr::If(_)));
3032
assert!(parsed.f.len() == 2);
33+
assert!(parsed.g);
34+
assert!(!parsed.h);
3135
}
3236

3337
#[test]
@@ -57,7 +61,7 @@ fn error() {
5761
Test::from_attributes([parse_quote!(#[test(invalid_attribute)])])
5862
.unwrap_err()
5963
.to_string(),
60-
"unexpected end of input, Expected assignment `=`"
64+
"Expected supported field `s`"
6165
);
6266

6367
assert_eq!(

0 commit comments

Comments
 (0)