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
pub(crate) mod attrs {
use proc_macro2::TokenStream as TokenStream2;
use quote::TokenStreamExt;
use lazy_static::lazy_static;
use regex::Regex;
lazy_static! {
static ref REG: regex::Regex = Regex::new(r"\$([\u0041-\u323AF|_]*)").unwrap();
}
#[derive(Clone)]
pub enum IoAttr {
Satisfy(syn::Expr),
Require(syn::Ident),
IfPresent(syn::Ident),
Doc(syn::Attribute),
Skip,
Unknown,
}
/// Parses the attributes of a struct or enum.
/// The attributes are returned in the order they were parsed in, you can return errors if you want to.
/// Some attributes do not allow conflicting attributes, such as #[skip]
pub fn parse_attribute<'a>(
attr: &'a syn::Attribute,
error_stream: &mut TokenStream2,
) -> Result<IoAttr, ()> {
let path = attr.path();
if path.is_ident("doc") {
return Ok(IoAttr::Doc(attr.clone()));
}
if path.is_ident("satisfy") {
// Satisfy is an attribute that allows an expression to be specified
// this is polyfilled later with `self.EXPRESSION`
match attr.parse_args::<syn::Expr>() {
Ok(expr) => {
return Ok(IoAttr::Satisfy(expr));
}
Err(e) => {
error_stream.append_all(
syn::Error::new_spanned(attr, format!("'satisfy' attribute requires an Expression!\n Example: #[satisfy(self.field == 0)]\n Error: {}", e))
.to_compile_error(),
);
}
}
} else if path.is_ident("require") {
// Require is an attribute that allows an identifier to be specified
// this is polyfilled later with `self.IDENTIFIER.is_some()`
match attr.parse_args::<syn::Ident>() {
Ok(ident) => {
return Ok(IoAttr::Require(ident));
}
Err(_) => {
error_stream.append_all(
syn::Error::new_spanned(attr, "'require' attribute requires an Identifier! \n Example: #[require(self.field)]")
.to_compile_error(),
);
}
}
} else if path.is_ident("if_present") {
// Require is an attribute that allows an identifier to be specified
// this is polyfilled later with `self.IDENTIFIER.is_some()`
match attr.parse_args::<syn::Ident>() {
Ok(ident) => {
return Ok(IoAttr::IfPresent(ident));
}
Err(_) => {
error_stream.append_all(
syn::Error::new_spanned(attr, "'if_present' attribute requires an Identifier! \n Example: #[if_present(self.field)]")
.to_compile_error(),
);
}
}
} else if path.is_ident("skip") {
// skip is a special attribute, it cannot be used with any other attribute
// therefore we can just return early, however we need to validate that
// there are no other attributes
return Ok(IoAttr::Skip);
} else {
return Ok(IoAttr::Unknown);
// error_stream.append_all(
// syn::Error::new_spanned(
// attr,
// "Unknown attribute, did you mean 'satisfy', 'require', 'if_present', or 'skip'?",
// )
// .to_compile_error(),
// );
}
Err(())
}
/// Parses the attributes of a struct or enum.
/// todo: this is a bit of a mess, and should be cleaned up.
/// todo: There's probably a better way to resolve the type without having to do this.
pub fn resolve_generic_type<'a>(
ty: &'a syn::Type,
name: &str,
error_stream: &mut TokenStream2,
) -> Option<syn::Type> {
match *ty {
syn::Type::Path(ref tp) => {
if let Some(first) = tp.path.segments.first() {
if first.ident.to_string() == name {
if let syn::PathArguments::AngleBracketed(args) = &first.arguments {
if let Some(syn::GenericArgument::Type(inner)) = args.args.first() {
return Some(inner.clone());
} else {
error_stream.append_all(syn::Error::new_spanned(
ty,
format!("{} type must have a generic argument in order to be required!", name)
).to_compile_error());
}
}
}
}
return None;
}
_ => None,
}
}
}