safety_parser/safety/
mod.rs1use crate::{
2 Str,
3 configuration::{TagType, config_exists, get_tag},
4};
5use indexmap::IndexMap;
6use proc_macro2::TokenStream;
7use quote::quote;
8use syn::{
9 parse::{Parse, ParseStream},
10 punctuated::Punctuated,
11 token::Paren,
12 *,
13};
14
15mod utils;
16
17#[cfg(test)]
18mod tests;
19
20#[derive(Debug)]
21pub struct SafetyAttr {
22 pub attr: Attribute,
23 pub args: SafetyAttrArgs,
24}
25
26impl Parse for SafetyAttr {
27 fn parse(input: ParseStream) -> Result<Self> {
28 let mut attrs = Attribute::parse_outer(input)?;
29
30 assert!(attrs.len() == 1, "Given input must be a single #[safety] attribute.");
31 let attr = attrs.remove(0);
32 drop(attrs);
33
34 let args = attr.parse_args()?;
38 Ok(SafetyAttr { attr, args })
39 }
40}
41
42pub fn parse_attr_and_get_properties(attr: &str) -> Box<[PropertiesAndReason]> {
44 let attr: SafetyAttr = parse_str(attr)
45 .unwrap_or_else(|e| panic!("Failed to parse {attr:?} as a safety attribute:\n{e}"));
46 attr.args.args.into_iter().collect()
47}
48
49#[derive(Debug)]
50pub struct SafetyAttrArgs {
51 pub args: Punctuated<PropertiesAndReason, Token![;]>,
52}
53
54impl Parse for SafetyAttrArgs {
55 fn parse(input: ParseStream) -> Result<Self> {
56 Ok(SafetyAttrArgs { args: Punctuated::parse_terminated(input)? })
57 }
58}
59
60impl SafetyAttrArgs {
61 pub fn property_reason(&self) -> impl Iterator<Item = (&Property, Option<&str>)> {
62 self.args.iter().flat_map(|arg| arg.tags.iter().map(|prop| (prop, arg.desc.as_deref())))
63 }
64}
65
66#[derive(Debug)]
67pub struct PropertiesAndReason {
68 pub tags: Box<[Property]>,
69 pub desc: Option<Str>,
70}
71
72impl Parse for PropertiesAndReason {
73 fn parse(input: ParseStream) -> Result<Self> {
74 let mut tags = Vec::<Property>::new();
75 let mut desc = None;
76
77 while !input.cursor().eof() {
78 let tag: TagNameType = input.parse()?;
79 if config_exists() {
80 tag.check_type();
81 }
82 let sp = if input.peek(Paren) {
83 let content;
84 parenthesized!(content in input);
85 let args = Punctuated::<Expr, Token![,]>::parse_terminated(&content)?;
86 let args = args.into_iter().collect();
87 Property { tag, args }
88 } else {
89 Property { tag, args: Default::default() }
90 };
91 tags.push(sp);
92
93 if input.peek(Token![,]) {
94 let _: Token![,] = input.parse()?;
96 }
97 if input.peek(Token![:]) {
98 let _: Token![:] = input.parse()?;
99 let s: LitStr = input.parse()?;
101 desc = Some(s.value().into());
102 break;
103 }
104 if input.peek(Token![;]) {
105 break;
107 }
108 }
109 Ok(PropertiesAndReason { tags: tags.into(), desc })
110 }
111}
112
113impl PropertiesAndReason {
114 pub fn gen_doc(&self) -> TokenStream {
122 let mut ts = TokenStream::default();
123 if let Some(desc) = self.desc.as_deref() {
124 ts.extend(quote! { #[doc = #desc] });
125 }
126 for tag in &self.tags {
127 let name = tag.tag.name();
128 let tokens = if let Some(desc) = tag.gen_doc() {
129 quote! { #[doc = concat!("* ", #name, ": ", #desc)] }
130 } else {
131 quote! { #[doc = concat!("* ", #name)] }
132 };
133 ts.extend(tokens);
134 }
135 ts
136 }
137}
138
139#[derive(Debug)]
140pub struct Property {
141 pub tag: TagNameType,
143 pub args: Box<[Expr]>,
145}
146
147impl Property {
148 pub fn gen_doc(&self) -> Option<String> {
151 let defined_tag = get_tag(self.tag.name());
152 let args_len = self.args.len().min(defined_tag.args.len());
154
155 let defined_args = defined_tag.args[..args_len].iter().map(|s| &**s);
157 let input_args = self.args[..args_len].iter().map(utils::expr_to_string);
158 let mut map_defined_arg_input_arg: IndexMap<_, _> = defined_args.zip(input_args).collect();
159 for defined_arg in &defined_tag.args {
161 if !map_defined_arg_input_arg.contains_key(&**defined_arg) {
162 map_defined_arg_input_arg.insert(defined_arg, String::new());
163 }
164 }
165
166 defined_tag.desc.as_deref().map(|desc| utils::template(desc, &map_defined_arg_input_arg))
167 }
168}
169
170#[derive(Debug)]
172pub struct TagNameType {
173 typ: Option<TagType>,
177 name: Str,
179}
180
181impl Parse for TagNameType {
182 fn parse(input: ParseStream) -> Result<Self> {
183 let ident: Ident = input.parse()?;
184 let first = ident.to_string();
185 Ok(if input.peek(Token![.]) {
186 let _: Token![.] = input.parse()?;
187 let second: Ident = input.parse()?;
188 let name = second.to_string().into();
189 TagNameType { name, typ: Some(TagType::new(&first)) }
190 } else {
191 TagNameType { name: first.into(), typ: None }
192 })
193 }
194}
195
196impl TagNameType {
197 pub fn name(&self) -> &str {
198 &self.name
199 }
200
201 pub fn typ(&self) -> Option<TagType> {
205 self.typ
206 }
207
208 pub fn name_type(&self) -> (&str, Option<TagType>) {
209 (&self.name, self.typ)
210 }
211
212 pub fn check_type(&self) {
214 let (name, typ) = self.name_type();
215 let defined_types = &get_tag(name).types;
216 if let Some(typ) = typ {
217 assert!(
218 defined_types.contains(&typ),
219 "For tag {name:?}, defined_types is {defined_types:?}, \
220 while user's {typ:?} doesn't exist."
221 );
222 } else {
223 assert_eq!(
224 defined_types.len(),
225 1,
226 "For tag {name:?} without explicit type, \
227 the default type is the single defined type.\n\
228 But defined_types has multiple types: {defined_types:?}.\n\
229 So choose a type to be `type.{name}`."
230 );
231 }
232 }
233}