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
//! Utilities for parsing [`Attribute`](syn::Attribute)s.
//!
//! # Examples
//!
//! <details><summary>Basic usage</summary>
//!
//! ```
//! use macroific::attr_parse::prelude::*;
//! # use syn::punctuated::Punctuated;
//! # use proc_macro2::{Delimiter, Span};
//! # use quote::ToTokens;
//!
//! #[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 macroific::attr_parse::prelude::*;
//! # use syn::{parse_quote, Attribute};
//!
//! #[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 macroific::attr_parse::prelude::*;
//! # use syn::{parse_quote, Attribute};
//! #[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>
//!
//! # Features
//!
//! Enable the `full` feature to implement [`ParseOption`] for syn types that require it.

pub use macroific_attr_parse::*;
#[cfg(feature = "derive")]
pub use macroific_macro::{AttributeOptions, ParseOption};

#[allow(missing_docs)]
pub mod prelude {
    #[cfg(feature = "derive")]
    pub use macroific_macro::{AttributeOptions, ParseOption};

    pub use super::__attr_parse_prelude::*;
}