• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Alorel / macroific-rs / 11197160670

05 Oct 2024 11:38PM UTC coverage: 81.468%. First build
11197160670

Pull #14

github

web-flow
Merge f481bde63 into 697e66c3a
Pull Request #14: v2

430 of 476 new or added lines in 16 files covered. (90.34%)

1099 of 1349 relevant lines covered (81.47%)

46.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

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

195
pub use macroific_attr_parse::*;
196
pub use macroific_macro::{AttributeOptions, ParseOption};
197

198
#[allow(missing_docs)]
199
pub mod prelude {
200
    pub use macroific_macro::{AttributeOptions, ParseOption};
201

202
    pub use super::__attr_parse_prelude::*;
203
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc