muffy_validation_macro/
lib.rs1extern crate alloc;
4
5mod attribute;
6mod compiler;
7mod content;
8mod definition;
9mod error;
10mod name;
11mod pattern;
12
13use self::{
14 attribute::AttributeSet,
15 compiler::Compiler,
16 content::{children, generate_content},
17 definition::load_definitions,
18 error::MacroError,
19 name::class_names,
20 pattern::Pattern,
21};
22use alloc::collections::BTreeMap;
23use itertools::Itertools;
24use muffy_rnc::{NameClass, Pattern as RncPattern};
25use proc_macro::TokenStream;
26use proc_macro2::Span;
27use quote::{format_ident, quote};
28
29#[proc_macro]
31pub fn html(_input: TokenStream) -> TokenStream {
32 generate_validation("html", &["html.rnc"]).unwrap_or_else(|error| {
33 syn::Error::new(Span::call_site(), error)
34 .to_compile_error()
35 .into()
36 })
37}
38
39fn generate_validation(language: &str, files: &[&str]) -> Result<TokenStream, MacroError> {
40 let definitions = load_definitions(files)?;
41 let mut compiler = Compiler::new(&definitions);
42 let mut element_rules = BTreeMap::<String, Vec<(Vec<AttributeSet>, Pattern)>>::new();
43
44 for definition in definitions.values() {
45 for (name_class, pattern) in collect_elements(definition) {
46 let names = class_names(name_class, false);
47
48 if names.is_empty() {
49 continue;
50 }
51
52 for (attribute_sets, content_pattern) in compiler.compile(pattern)? {
53 for name in &names {
54 let variants = element_rules.entry(name.clone()).or_default();
55 let variant = (attribute_sets.clone(), content_pattern.clone());
56
57 if !variants.contains(&variant) {
58 variants.push(variant);
59 }
60 }
61 }
62 }
63 }
64
65 let mut attribute_set_indexes = BTreeMap::<Vec<AttributeSet>, usize>::new();
66 let mut content_indexes = BTreeMap::<Pattern, usize>::new();
67 let mut element_matches = vec![];
68
69 for (name, variants) in &element_rules {
70 let attributes = variants
71 .iter()
72 .flat_map(|(sets, _)| sets)
73 .flat_map(|set| set.required.iter().chain(&set.optional))
74 .unique()
75 .sorted()
76 .map(|name| quote!(#name));
77 let children = variants
78 .iter()
79 .flat_map(|(_, content)| children(content))
80 .unique()
81 .sorted()
82 .map(|name| quote!(#name));
83
84 let variants = variants
85 .iter()
86 .map(|(sets, content)| {
87 let index = attribute_set_indexes.len();
88 let sets = format_ident!(
89 "ATTRIBUTE_SETS_{}",
90 *attribute_set_indexes.entry(sets.clone()).or_insert(index)
91 );
92
93 let index = content_indexes.len();
94 let content = format_ident!(
95 "CONTENT_{}",
96 *content_indexes.entry(content.clone()).or_insert(index)
97 );
98
99 quote!(Variant { attributes: #sets, content: &#content })
100 })
101 .collect::<Vec<_>>();
102
103 element_matches.push(quote! {
104 #name => {
105 const RULE: Rule = Rule {
106 attributes: &[#(#attributes),*],
107 children: &[#(#children),*],
108 variants: &[#(#variants),*],
109 };
110
111 validate_rule(element, ignored_attributes, ignored_elements, &RULE)
112 }
113 });
114 }
115
116 let attribute_set_definitions = sort_by_index(attribute_set_indexes).map(|(sets, index)| {
117 let identifier = format_ident!("ATTRIBUTE_SETS_{index}");
118 let sets = sets.iter().map(|set| {
119 let required = set.required.iter().map(|name| quote!(#name));
120 let optional = set.optional.iter().map(|name| quote!(#name));
121
122 quote!(AttributeSet {
123 required: &[#(#required),*],
124 optional: &[#(#optional),*],
125 })
126 });
127
128 quote!(const #identifier: &[AttributeSet] = &[#(#sets),*];)
129 });
130 let content_definitions = sort_by_index(content_indexes)
131 .map(|(content, index)| {
132 let identifier = format_ident!("CONTENT_{index}");
133 let content = generate_content(&content)?;
134
135 Ok(quote!(const #identifier: Content = #content;))
136 })
137 .collect::<Result<Vec<_>, MacroError>>()?;
138
139 let function_name = format_ident!("validate_{language}_element");
140 let documentation = format!("Validates an {} element.", language.to_uppercase());
141
142 Ok(quote! {
143 #[doc = #documentation]
144 pub fn #function_name(
145 element: &Element,
146 ignored_attributes: &[::regex::Regex],
147 ignored_elements: &[::regex::Regex],
148 ) -> Result<(), MarkupError> {
149 #(#attribute_set_definitions)*
150 #(#content_definitions)*
151
152 match element.name() {
153 name if ignored_elements.iter().any(|pattern| pattern.is_match(name)) => Ok(()),
154 #(#element_matches)*
155 _ => Err(MarkupError::UnknownTag(element.name().to_string())),
156 }
157 }
158 }
159 .into())
160}
161
162fn sort_by_index<T>(indexes: BTreeMap<T, usize>) -> impl Iterator<Item = (T, usize)> {
163 let mut entries = indexes.into_iter().collect::<Vec<_>>();
164
165 entries.sort_by_key(|(_, index)| *index);
166
167 entries.into_iter()
168}
169
170fn collect_elements(pattern: &RncPattern) -> Vec<(&NameClass, &RncPattern)> {
172 match pattern {
173 RncPattern::Element {
174 name_class,
175 pattern,
176 } => vec![(name_class, pattern)],
177 RncPattern::Choice(patterns)
178 | RncPattern::Group(patterns)
179 | RncPattern::Interleave(patterns) => patterns.iter().flat_map(collect_elements).collect(),
180 RncPattern::Many0(pattern) | RncPattern::Many1(pattern) | RncPattern::Optional(pattern) => {
181 collect_elements(pattern)
182 }
183 RncPattern::Attribute { .. }
184 | RncPattern::Data { .. }
185 | RncPattern::Empty
186 | RncPattern::External(_)
187 | RncPattern::Grammar(_)
188 | RncPattern::List(_)
189 | RncPattern::Name(_)
190 | RncPattern::NotAllowed
191 | RncPattern::Text
192 | RncPattern::Value { .. } => vec![],
193 }
194}