1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! Utilities for parsing `syn` `Attribute`s.
//!
//! # Examples
//!
//! <details><summary>Basic usage</summary>
//!
//! ```
//! # use syn::punctuated::Punctuated;
//! # use proc_macro2::{Delimiter, Span};
//! # use quote::ToTokens;
//! use macroific::attr_parse::prelude::*;
//!
//! #[derive(AttributeOptions)]
//! struct MyOptions {
//! str_option: String,
//! optional: Option<syn::Lifetime>,
//! bool_option1: bool,
//! bool_option2: bool,
//! bool_option3: bool,
//! a_list: Punctuated<u32, syn::Token![,]>,
//! a_path: Option<syn::Path>,
//! }
//!
//! // You'd normally get this from `DeriveInput`
//! let attributes: Vec<syn::Attribute> = vec![
//! syn::parse_quote! { #[my_opts(str_option = "hello", bool_option2, bool_option3 = false)] },
//! syn::parse_quote! { #[my_opts(a_list(10, 20, 30), a_path(std::fs::File))] }
//! ];
//!
//! let opts = MyOptions::from_iter_named("my_opts", Span::call_site(), attributes).unwrap();
//!
//! // Strings & numbers can be converted straight from syn types
//! assert_eq!(opts.str_option, "hello");
//!
//! // Optionals are optional (:
//! assert!(opts.optional.is_none());
//!
//! // We didn't provide bool1 so it `Default::default()`ed to false
//! assert!(!opts.bool_option1);
//!
//! // Booleans can be provided with just the property name so bool2 is true
//! assert!(opts.bool_option2);
//!
//! // Though we can provide an explicit value too
//! assert!(!opts.bool_option3);
//!
//! // Punctuated lists are supported as the enclosed type implements ParseOption
//! assert_eq!(opts.a_list[0], 10);
//! assert_eq!(opts.a_list[1], 20);
//! assert_eq!(opts.a_list[2], 30);
//!
//! // The path is provided with an alternative syntax as an example, but it could've just as
//! // easily been provided as `a_path = std::fs::File`
//! assert_eq!(opts.a_path.unwrap().to_token_stream().to_string(), "std :: fs :: File");
//! ```
//!
//! </details>
//!
//! <details><summary>Renaming & default values</summary>
//!
//! ```
//! # use syn::{parse_quote, Attribute};
//! use macroific::attr_parse::prelude::*;
//!
//! #[derive(AttributeOptions, Debug)]
//! struct MyOptions {
//! #[attr_opts(
//! rename = "A",
//! default = false // fail if this attr is not provided
//! )]
//! num1: u8,
//!
//! #[attr_opts(default = some_module::default_num)] // use this function for the default value
//! num2: u8,
//! }
//!
//! mod some_module {
//! pub(super) fn default_num() -> u8 { u8::MAX }
//! }
//!
//! let opts = MyOptions::from_attr(parse_quote! { #[foo_attr(A = 10)] }).unwrap();
//! assert_eq!(opts.num1, 10);
//! assert_eq!(opts.num2, u8::MAX);
//!
//! let err = MyOptions::from_attr(parse_quote! { #[foo_attr()] }).unwrap_err();
//! assert_eq!(err.to_string(), r#"Missing required attribute: "A""#);
//! ```
//!
//! Full table on supported syntaxes for providing option values can be found
//! on [`parse_bool_attr`](ext::ParseBufferExt::parse_bool_attr) and
//! [`parse_valued_attr`](ext::ParseBufferExt::parse_valued_attr). See the
//! [derive macro](macroific_macro::AttributeOptions) doc page for options you can pass to it.
//!
//! </details>
//!
//! <details><summary>Nesting structs</summary>
//!
//! Nesting structs can be achieved by deriving the [`ParseOption`] trait which uses the same
//! options as `AttributeOptions`.
//!
//! ```
//! # use syn::{parse_quote, Attribute};
//! use macroific::attr_parse::prelude::*;
//!
//! #[derive(ParseOption, Debug, Eq, PartialEq)]
//! struct Nested {
//! required: bool,
//! foo: String,
//! count: Option<u8>,
//! }
//!
//! #[derive(AttributeOptions, Debug, Eq, PartialEq)]
//! struct Options {
//! #[attr_opts(default = false)]
//! nest: Nested,
//!
//! #[attr_opts(default = false)]
//! alt: Nested,
//!
//! root: String,
//! }
//!
//! let opts = Options::from_attr(parse_quote! { #[some_attr(root = "^", nest(count = 5, required), alt(foo = "Bar"))] })
//! .unwrap();
//!
//! let expect = Options {
//! nest: Nested { required: true, count: Some(5), foo: String::new() },
//! alt: Nested { required: false, count: None, foo: "Bar".into() },
//! root: "^".into(),
//! };
//!
//! assert_eq!(opts, expect);
//! ```
//!
//! </details>
//!
//! <details><summary>Enum option</summary>
//!
//! ```
//! # use syn::parse_quote;
//! # use syn::parse::{Parse, ParseStream};
//! # use proc_macro2::Span;
//! use macroific::attr_parse::prelude::*;
//!
//! // Say we want a subset of `syn::Lit` - we can define an enum like this and
//! // derive `ParseOption` with `from_parse`.
//! #[derive(ParseOption, Debug, PartialEq)]
//! #[attr_opts(from_parse)]
//! enum BoolOrStr {
//! Int(syn::LitInt),
//! Str(syn::LitStr)
//! }
//!
//! // Then implement the `Parse` trait `from_parse` expects.
//! impl Parse for BoolOrStr {
//! fn parse(input: ParseStream) -> syn::Result<Self> {
//! if input.peek(syn::LitInt) {
//! input.parse().map(Self::Int)
//! } else if input.peek(syn::LitStr) {
//! input.parse().map(Self::Str)
//! } else {
//! Err(input.error("Expected integer or string literal"))
//! }
//! }
//! }
//!
//! // Define an options struct that uses the enum
//! #[derive(AttributeOptions)]
//! struct MyOptions {
//! #[attr_opts(default = false)]
//! will_be_int: BoolOrStr, // Will provide an integer here
//!
//! #[attr_opts(default = false)]
//! will_be_str: BoolOrStr, // Will provide a string here
//! }
//!
//! // Initialise an example attribute
//! let attr: syn::Attribute = parse_quote! {
//! #[foo(will_be_int = 10, will_be_str = "hello")]
//! };
//!
//! // Parse it
//! let opts = MyOptions::from_attr(attr).unwrap();
//!
//! assert_eq!(opts.will_be_int, BoolOrStr::Int(syn::LitInt::new("10", Span::call_site())));
//! assert_eq!(opts.will_be_str, BoolOrStr::Str(syn::LitStr::new("hello", Span::call_site())));
//! ```
//!
//! </details>
//!
//! # Features
//!
//! Enable the `full` feature to implement [`ParseOption`] for syn types that require it.
pub use *;
pub use ;