xidl_parser/typed_ast/
annotation.rs1use super::*;
2use serde::{Deserialize, Serialize};
3use xidl_parser_derive::Parser;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct AnnotationAppl {
7 pub name: AnnotationName,
8 pub params: Option<AnnotationParams>,
9 pub builtin: Option<BuiltinAnnotation>,
10 pub is_extend: bool,
11 pub extra: Vec<AnnotationAppl>,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub enum AnnotationName {
16 ScopedName(ScopedName),
17 Builtin(String),
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub enum AnnotationParams {
22 ConstExpr(ConstExpr),
23 Params(Vec<AnnotationApplParam>),
24 Raw(String),
25}
26
27#[derive(Debug, Clone, Parser, Serialize, Deserialize)]
28pub struct AnnotationApplParam {
29 pub ident: Identifier,
30 pub value: Option<ConstExpr>,
31}
32
33impl<'a> crate::parser::FromTreeSitter<'a> for AnnotationAppl {
34 fn from_node(
35 node: tree_sitter::Node<'a>,
36 ctx: &mut crate::parser::ParseContext<'a>,
37 ) -> crate::error::ParserResult<Self> {
38 let kind_id = node.kind_id();
39 let is_extend = kind_id == xidl_parser_derive::node_id!("extend_annotation_appl");
40 if !is_extend {
41 assert_eq!(kind_id, xidl_parser_derive::node_id!("annotation_appl"));
42 }
43 let raw = ctx.node_text(&node)?.to_string();
44
45 let mut custom_body = None;
46 let mut builtin_body = None;
47 let mut extra = Vec::new();
48 for ch in node.children(&mut node.walk()) {
49 match ch.kind_id() {
50 xidl_parser_derive::node_id!("annotation_appl_custom_body") => {
51 custom_body = Some(ch);
52 }
53 xidl_parser_derive::node_id!("annotation_appl_builtin_body") => {
54 builtin_body = Some(ch);
55 }
56 xidl_parser_derive::node_id!("annotation_appl")
57 | xidl_parser_derive::node_id!("extend_annotation_appl") => {
58 extra.push(AnnotationAppl::from_node(ch, ctx)?);
59 }
60 _ => {}
61 }
62 }
63
64 if requires_raw_annotation_parse(&raw) {
65 return parse_annotation_from_raw(&raw, is_extend, extra);
66 }
67
68 if let Some(custom_body) = custom_body {
69 let mut scoped_name = None;
70 let mut params = None;
71 for ch in custom_body.children(&mut custom_body.walk()) {
72 match ch.kind_id() {
73 xidl_parser_derive::node_id!("scoped_name") => {
74 scoped_name = Some(ScopedName::from_node(ch, ctx)?);
75 }
76 xidl_parser_derive::node_id!("annotation_appl_params") => {
77 params = Some(AnnotationParams::from_node(ch, ctx)?);
78 }
79 _ => {}
80 }
81 }
82 let scoped_name = scoped_name.ok_or_else(|| {
83 crate::error::ParseError::UnexpectedNode(
84 "annotation_appl_custom_body missing scoped_name".to_string(),
85 )
86 })?;
87 return Ok(Self {
88 name: AnnotationName::ScopedName(scoped_name),
89 params,
90 builtin: None,
91 is_extend,
92 extra,
93 });
94 }
95
96 let builtin = builtin_body
97 .map(|node| parse_builtin_annotation(node, ctx))
98 .transpose()?;
99 let source = builtin_body
100 .map(|node| ctx.node_text(&node))
101 .transpose()?
102 .unwrap_or(raw.as_str());
103 let source = source.trim().strip_prefix("//@").unwrap_or(source);
104 let raw = source.trim().strip_prefix('@').unwrap_or(source).trim();
105 let (name, args) = match raw.split_once('(') {
106 Some((name, rest)) => {
107 let args = rest.strip_suffix(')').unwrap_or(rest).trim();
108 (name.trim(), Some(args))
109 }
110 None => (raw, None),
111 };
112
113 Ok(Self {
114 name: AnnotationName::Builtin(name.to_string()),
115 params: args.map(|value| AnnotationParams::Raw(value.to_string())),
116 builtin,
117 is_extend,
118 extra,
119 })
120 }
121}
122
123fn requires_raw_annotation_parse(raw: &str) -> bool {
124 let source = raw.trim();
125 let source = source.strip_prefix("//@").unwrap_or(source);
126 let source = source.strip_prefix('@').unwrap_or(source);
127 let name = source
128 .split_once('(')
129 .map(|(name, _)| name)
130 .unwrap_or(source);
131 name.contains('-') || source.contains('[') || source.contains(']')
132}
133
134fn parse_annotation_from_raw(
135 raw: &str,
136 is_extend: bool,
137 extra: Vec<AnnotationAppl>,
138) -> crate::error::ParserResult<AnnotationAppl> {
139 let source = raw.trim();
140 let source = source.strip_prefix("//@").unwrap_or(source);
141 let source = source.strip_prefix('@').unwrap_or(source).trim();
142 let (name, args) = match source.split_once('(') {
143 Some((name, rest)) => {
144 let args = rest.strip_suffix(')').unwrap_or(rest).trim();
145 (name.trim(), Some(args))
146 }
147 None => (source, None),
148 };
149 Ok(AnnotationAppl {
150 name: AnnotationName::Builtin(name.to_string()),
151 params: args.map(|value| AnnotationParams::Raw(value.to_string())),
152 builtin: None,
153 is_extend,
154 extra,
155 })
156}
157
158impl AnnotationAppl {
159 pub fn doc(text: String) -> Self {
160 let escaped = escape_doc_text(&text);
161 Self {
162 name: AnnotationName::Builtin("doc".to_string()),
163 params: Some(AnnotationParams::Raw(format!("\"{}\"", escaped))),
164 builtin: None,
165 is_extend: false,
166 extra: Vec::new(),
167 }
168 }
169}
170
171fn escape_doc_text(text: &str) -> String {
172 let mut out = String::with_capacity(text.len());
173 for ch in text.chars() {
174 match ch {
175 '\\' => out.push_str("\\\\"),
176 '"' => out.push_str("\\\""),
177 '\n' => out.push_str("\\n"),
178 '\r' => {}
179 _ => out.push(ch),
180 }
181 }
182 out
183}
184
185impl<'a> crate::parser::FromTreeSitter<'a> for AnnotationParams {
186 fn from_node(
187 node: tree_sitter::Node<'a>,
188 ctx: &mut crate::parser::ParseContext<'a>,
189 ) -> crate::error::ParserResult<Self> {
190 assert_eq!(
191 node.kind_id(),
192 xidl_parser_derive::node_id!("annotation_appl_params")
193 );
194
195 let mut const_expr = None;
196 let mut params = vec![];
197 for ch in node.children(&mut node.walk()) {
198 match ch.kind_id() {
199 xidl_parser_derive::node_id!("const_expr") => {
200 const_expr = Some(ConstExpr::from_node(ch, ctx)?);
201 }
202 xidl_parser_derive::node_id!("annotation_appl_param") => {
203 params.push(AnnotationApplParam::from_node(ch, ctx)?);
204 }
205 _ => {}
206 }
207 }
208
209 if let Some(const_expr) = const_expr {
210 return Ok(Self::ConstExpr(const_expr));
211 }
212 if !params.is_empty() {
213 return Ok(Self::Params(params));
214 }
215
216 Err(crate::error::ParseError::UnexpectedNode(
217 "annotation_appl_params missing content".to_string(),
218 ))
219 }
220}