rapx/verify/
attr_parser.rs1use syn::{
16 Expr, ExprCall, ExprPath, Lit, Result as SynResult, Token,
17 parse::{Parse, ParseStream},
18};
19
20#[derive(Debug, Clone)]
22pub struct ParsedProperty {
23 pub tag: String,
25 pub args: Vec<Expr>,
27 pub kind: Option<String>,
29}
30
31#[derive(Debug, Clone, Default)]
33pub struct ParsedRapxAttr {
34 pub properties: Vec<ParsedProperty>,
36}
37
38impl Parse for ParsedProperty {
39 fn parse(input: ParseStream<'_>) -> SynResult<Self> {
45 let expr: Expr = input.parse()?;
46 let mut property = parse_property_expr(expr)?;
47
48 if input.peek(Token![,]) {
49 let fork = input.fork();
50 let _: Token![,] = fork.parse()?;
51 if fork.peek(syn::Ident) && fork.peek2(Token![=]) {
52 let _: Token![,] = input.parse()?;
53 let ident: syn::Ident = input.parse()?;
54 let _: Token![=] = input.parse()?;
55 let value: Expr = input.parse()?;
56
57 if ident == "kind" {
58 if let Expr::Lit(ref expr_lit) = value
59 && let Lit::Str(ref kind) = expr_lit.lit
60 {
61 property.kind = Some(kind.value());
62 } else {
63 return Err(syn::Error::new_spanned(
64 value,
65 "RAPx requires attribute kind must be a string literal",
66 ));
67 }
68 } else {
69 return Err(syn::Error::new(
70 ident.span(),
71 "unsupported named RAPx requires attribute argument",
72 ));
73 }
74 }
75 }
76
77 Ok(property)
78 }
79}
80
81struct RequireOuterAttribute {
83 attr: syn::Attribute,
84}
85
86impl Parse for RequireOuterAttribute {
87 fn parse(input: ParseStream<'_>) -> SynResult<Self> {
89 Ok(Self {
90 attr: input
91 .call(syn::Attribute::parse_outer)?
92 .into_iter()
93 .next()
94 .ok_or_else(|| input.error("expected exactly one outer attribute"))?,
95 })
96 }
97}
98
99pub fn parse_rapx_attr(attr_str: &str, expected_name: &str) -> SynResult<ParsedRapxAttr> {
104 let attr = syn::parse_str::<RequireOuterAttribute>(attr_str)?.attr;
106 if !is_expected_syn_rapx_attr(&attr, expected_name) {
107 return Ok(ParsedRapxAttr::default());
108 }
109
110 let syn::Meta::List(meta_list) = &attr.meta else {
112 return Ok(ParsedRapxAttr::default());
113 };
114
115 let property = meta_list.parse_args::<ParsedProperty>()?;
116 Ok(ParsedRapxAttr {
117 properties: vec![property],
118 })
119}
120
121fn is_expected_syn_rapx_attr(attr: &syn::Attribute, expected_name: &str) -> bool {
123 let mut segments = attr.path().segments.iter();
124 matches!(
125 (segments.next(), segments.next(), segments.next()),
126 (Some(first), Some(second), None)
127 if first.ident == "rapx" && second.ident == expected_name
128 )
129}
130
131fn parse_property_expr(expr: Expr) -> SynResult<ParsedProperty> {
133 match expr {
134 Expr::Call(ExprCall { func, args, .. }) => {
135 let tag = match *func {
137 Expr::Path(ExprPath { path, .. }) => path
138 .segments
139 .last()
140 .map(|seg| seg.ident.to_string())
141 .ok_or_else(|| syn::Error::new_spanned(path, "missing property name"))?,
142 other => {
143 return Err(syn::Error::new_spanned(
144 other,
145 "unsupported RAPx property callee expression",
146 ));
147 }
148 };
149
150 Ok(ParsedProperty {
151 tag,
152 args: args.into_iter().collect(),
153 kind: None,
154 })
155 }
156 other => Err(syn::Error::new_spanned(
157 other,
158 "unsupported RAPx property expression",
159 )),
160 }
161}