1mod content;
10mod item;
11mod property;
12mod view;
13
14use proc_macro::TokenStream;
15use proc_macro2::{Span, TokenStream as TokenStream2};
16use quote::ToTokens;
17use std::{iter::Map, slice::Iter};
18use syn::{punctuated::Punctuated, visit_mut::VisitMut};
19
20#[proc_macro]
21pub fn block(stream: TokenStream) -> TokenStream {
39 if stream.is_empty() {
40 let error = syn::Error::new(Span::call_site(), "this view block has no content");
41 return TokenStream::from(error.into_compile_error())
42 }
43
44 let mut structs = vec![];
45 let view::Streaming::Roots(roots) = syn::parse_macro_input!(stream) else { panic!() };
46 let (mut stream, bindings) = view::expand(&mut structs, roots, false);
47
48 bindings_error(&mut stream, bindings.spans);
49 for strukt in structs { strukt.to_tokens(&mut stream) }
50 TokenStream::from(stream)
51}
52
53#[proc_macro_attribute]
54pub fn view(stream: TokenStream, code: TokenStream) -> TokenStream {
92 let item = &mut syn::parse_macro_input!(code);
93 let mut output = TokenStream2::new();
94
95 let fill = |item: &mut _, output: &mut _, structs: &mut Vec<_>| {
96 if let syn::Item::Mod(mod_) = item {
97 if let Some((_, items)) = &mut mod_.content {
98 items.reserve(structs.len());
99 while let Some(item) = structs.pop() {
100 items.push(syn::Item::Struct(item))
101 } return
102 }
103 }
104 while let Some(item) = structs.pop() { item.to_tokens(output) }
105 };
106
107 match syn::parse_macro_input!(stream) {
108 view::Streaming::Struct { vis, ident, generics, fields } => {
109 let errable = !matches!(vis, syn::Visibility::Inherited)
110 || ident.is_some() || generics.lt_token.is_some() || !fields.is_empty();
111
112 let structs = vec![syn::ItemStruct {
113 vis, fields: syn::Fields::Named(syn::FieldsNamed {
114 brace_token: Default::default(), named: fields
115 }),
116 attrs: vec![], struct_token: Default::default(),
117 ident: ident.unwrap_or(syn::Ident::new("_", Span::call_site())),
118 generics, semi_token: Default::default(),
119 }];
120
121 let mut visitor = view::Visitor::Ok { structs, errable, deque: Default::default() };
122 visitor.visit_item_mut(item);
123
124 match visitor {
125 view::Visitor::Ok { mut structs, mut deque, .. } => {
126 if deque.is_empty() {
127 let error = syn::Error::new(Span::call_site(), "if no view code is \
128 written as the content of this attribute, at least one view must \
129 be created with `view!` in the scope of a `mod`, `impl` or `trait`");
130 return TokenStream::from(error.into_compile_error())
131 }
132 while let Some((spans, stream, bindings)) = deque.pop_front() {
133 view::parse(item, &mut output, spans, stream, bindings);
134 fill(item, &mut output, &mut structs)
135 }
136 }
137 view::Visitor::Error(error) => return TokenStream::from(error.into_compile_error())
138 }
139 }
140 view::Streaming::Roots(roots) => {
141 let (range, mut structs) = (Range(Span::call_site(), Span::call_site()), vec![]);
142 let (stream, bindings) = view::expand(&mut structs, roots, false);
143 view::parse(item, &mut output, range, stream, bindings);
144 fill(item, &mut output, &mut structs)
145 }
146 }
147
148 item.to_tokens(&mut output); TokenStream::from(output)
149}
150
151#[derive(Copy, Clone)]
152enum Assignee<'a> {
153 Field (Option<&'a Assignee<'a>>, &'a Punctuated<syn::Ident, syn::Token![.]>),
154 Ident (Option<&'a Assignee<'a>>, &'a syn::Ident),
155}
156
157#[derive(Copy, Clone)]
158enum Attributes<T: AsRef<[syn::Attribute]>> { Some(T), None(usize) }
159
160#[derive(Default)]
161struct Bindings { spans: Vec<Span>, stream: TokenStream2 }
162
163enum Construction {
164 BuilderPattern {
165 left: TokenStream2,
166 right: TokenStream2,
167 span: Span,
168 tilde: Option<syn::Token![~]>,
169 },
170 StructLiteral {
171 left: TokenStream2,
172 ty: TokenStream2,
173 fields: TokenStream2,
174 span: Span,
175 tilde: Option<syn::Token![~]>,
176 },
177}
178
179enum Path {
180 Type(syn::TypePath), Field {
181 access: Punctuated<syn::Ident, syn::Token![.]>,
182 gens: Option<syn::AngleBracketedGenericArguments>,
183 }
184}
185
186struct Range(Span, Span);
187
188enum Visitor<'a, 'b> {
189 #[allow(clippy::type_complexity)]
190 Ok { items: Option<&'a mut Map<Iter<'b, item::Item>, fn(&item::Item) -> Assignee>>,
191 assignee: &'a mut Option<Assignee<'b>>,
192 placeholder: &'static str,
193 stream: &'a mut TokenStream2 },
194
195 Error(syn::Error)
196}
197
198fn bindings_error(stream: &mut TokenStream2, spans: Vec<Span>) {
199 for span in spans { stream.extend(syn::Error::new(span, BINDINGS_ERROR).to_compile_error()) }
200}
201
202fn extend_attributes(attrs: &mut Vec<syn::Attribute>, pattrs: &[syn::Attribute]) {
203 let current = std::mem::take(attrs);
204 attrs.reserve(pattrs.len() + current.len());
205 attrs.extend_from_slice(pattrs);
206 attrs.extend(current);
207}
208
209fn parse_unterminated<T, P>(input: syn::parse::ParseStream) -> syn::Result<Punctuated<T, P>>
210where T: syn::parse::Parse, P: syn::parse::Parse {
211 let mut punctuated = Punctuated::new();
212 punctuated.push_value(input.parse()?);
213
214 while let Ok(punct) = input.parse() {
215 punctuated.push_punct(punct);
216 punctuated.push_value(input.parse()?)
217 }
218 Ok(punctuated)
219}
220
221const BINDINGS_ERROR: &str = "bindings must be consumed with the `bindings!` placeholder macro";
222
223struct ConstrError(&'static str);
224
225impl std::fmt::Display for ConstrError {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 f.write_str(self.0)?;
228 f.write_str(" in the initial content of an item whose definition should be expanded in a builder pattern or a struct literal")
229 }
230}