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
//! Module: node::def
//! Responsibility: derive-side node parsing.
//! Does not own: runtime schema semantics.
//! Boundary: macro metadata to node models.
use crate::prelude::*;
///
/// Def
///
/// the default gets overridden after the initial darling::from_list() call
/// Schema declarations are concrete types; generic parameters are rejected before emission.
///
#[derive(Debug, Default)]
pub struct Def {
pub(crate) item: Option<ItemStruct>,
paths: Option<CratePaths>,
}
impl Def {
/// Refuse authored state that generated schema types cannot preserve.
pub(crate) fn validate_declaration(item: &ItemStruct) -> Result<(), DarlingError> {
if !item.generics.params.is_empty() {
return Err(DarlingError::custom(
"schema declarations must not have generic parameters",
)
.with_span(&item.generics));
}
if !item.fields.is_empty() {
return Err(DarlingError::custom(
"schema declarations must have empty bodies; declare fields in the macro arguments",
)
.with_span(&item.fields));
}
Ok(())
}
/// Build one derive-side definition wrapper from the parsed item.
#[must_use]
pub const fn new(item: ItemStruct, paths: CratePaths) -> Self {
Self {
item: Some(item),
paths: Some(paths),
}
}
/// Preserve authored outer attributes on the main generated type.
/// Only the macro's own debug switch is consumed instead of forwarded.
pub(crate) fn type_attributes(&self) -> impl Iterator<Item = &syn::Attribute> {
self.item
.iter()
.flat_map(|item| &item.attrs)
.filter(|attr| !attr.path().is_ident("debug"))
}
/// Return the invocation paths attached before validation and emission.
///
/// # Panics
/// Panics before parsed definition metadata has been attached.
pub(crate) const fn paths(&self) -> &CratePaths {
self.paths
.as_ref()
.expect("derive definition must include resolved crate paths")
}
/// Return the parsed item identifier.
///
/// # Panics
///
/// Panics if called before darling has attached the parsed item metadata.
pub fn ident(&self) -> Ident {
self.item
.as_ref()
.unwrap_or_else(|| panic!("derive definition metadata must include the parsed item"))
.ident
.clone()
}
}
impl HasSchemaPart for Def {
fn schema_part(&self, paths: &CratePaths) -> TokenStream {
let model = &paths.model;
let ident = quote_one(&self.ident(), to_str_lit);
// quote
quote! {
#model::node::Def::new(module_path!(), #ident)
}
}
}
#[cfg(test)]
mod tests {
use super::Def;
#[test]
fn schema_declarations_reject_authored_fields_before_emission() {
for item in [
syn::parse_quote!(
pub struct Empty {}
),
syn::parse_quote!(
pub struct Empty();
),
syn::parse_quote!(
pub struct Empty;
),
] {
assert!(Def::validate_declaration(&item).is_ok());
}
for item in [
syn::parse_quote!(
pub struct Named {
pub note: String,
}
),
syn::parse_quote!(
pub struct Private {
note: String,
}
),
syn::parse_quote!(
pub struct Tuple(pub String, u64);
),
] {
let error = Def::validate_declaration(&item).unwrap_err();
assert_eq!(error.len(), 1);
assert!(!error.write_errors().is_empty());
}
}
#[test]
fn schema_declarations_reject_generic_parameters_before_emission() {
for item in [
syn::parse_quote!(
pub struct Concrete {}
),
syn::parse_quote!(
pub struct Concrete;
),
syn::parse_quote!(
pub struct Concrete
where
String: Clone, {}
),
] {
assert!(Def::validate_declaration(&item).is_ok());
}
for item in [
syn::parse_quote!(
pub struct Generic<T> {}
),
syn::parse_quote!(
pub struct Generic<'a> {}
),
syn::parse_quote!(
pub struct Generic<const N: usize> {}
),
syn::parse_quote!(
pub struct Generic<T: Clone = String> {}
),
syn::parse_quote!(
pub struct Generic<T>
where
T: Clone, {}
),
] {
let error = Def::validate_declaration(&item).unwrap_err();
assert_eq!(error.len(), 1);
assert!(!error.write_errors().is_empty());
}
}
}