1use super::*;
2use serde::Serialize;
3
4#[derive(Clone, Serialize)]
5pub struct AnnotationAppl {
7 #[serde(skip)]
9 pub span: Option<std::ops::Range<usize>>,
10 pub name: AnnotationName,
11 pub params: Option<AnnotationParams>,
12 pub builtin: Option<BuiltinAnnotation>,
13 pub is_extend: bool,
14 pub extra: Vec<AnnotationAppl>,
15}
16
17#[derive(Debug, Clone, Serialize)]
18pub enum AnnotationName {
19 ScopedName(ScopedName),
20 Builtin(String),
21}
22
23#[derive(Debug, Clone, Serialize)]
24pub enum AnnotationParams {
25 Params(Vec<AnnotationApplParam>),
26 Raw(String),
27}
28
29#[derive(Debug, Clone, Serialize)]
30pub enum AnnotationApplParam {
31 Positional(ConstExpr),
32 Named { ident: Identifier, value: ConstExpr },
33}
34
35impl<'a> crate::parser::FromTreeSitter<'a> for AnnotationAppl {
36 fn from_node(
37 node: tree_sitter::Node<'a>,
38 ctx: &mut crate::parser::ParseContext<'a>,
39 ) -> crate::error::ParserResult<Self> {
40 let kind_id = node.kind_id();
41 let is_extend = kind_id == xidl_parser_derive::node_id!("extend_annotation_appl");
42 if !is_extend {
43 assert_eq!(kind_id, xidl_parser_derive::node_id!("annotation_appl"));
44 }
45 let raw = ctx.node_text(&node)?.to_string();
46 let span = node
47 .children(&mut node.walk())
48 .find(|child| {
49 matches!(
50 child.kind(),
51 "annotation_appl_custom_body" | "annotation_appl_builtin_body"
52 )
53 })
54 .map(|body| node.start_byte()..body.end_byte());
55
56 let mut custom_body = None;
57 let mut builtin_body = None;
58 let mut extra = Vec::new();
59 for ch in node.children(&mut node.walk()) {
60 match ch.kind_id() {
61 xidl_parser_derive::node_id!("annotation_appl_custom_body") => {
62 custom_body = Some(ch);
63 }
64 xidl_parser_derive::node_id!("annotation_appl_builtin_body") => {
65 builtin_body = Some(ch);
66 }
67 xidl_parser_derive::node_id!("annotation_appl")
68 | xidl_parser_derive::node_id!("extend_annotation_appl") => {
69 extra.push(AnnotationAppl::from_node(ch, ctx)?);
70 }
71 _ => {}
72 }
73 }
74
75 let body_source = custom_body
76 .or(builtin_body)
77 .map(|body| ctx.node_text(&body))
78 .transpose()?;
79 if let Some(source) = body_source.filter(|source| requires_raw_annotation_parse(source)) {
80 let mut annotation = parse_annotation_from_raw(source, is_extend, extra)?;
81 annotation.span = span;
82 return Ok(annotation);
83 }
84
85 if let Some(custom_body) = custom_body {
86 let mut scoped_name = None;
87 let mut params = None;
88 for ch in custom_body.children(&mut custom_body.walk()) {
89 match ch.kind_id() {
90 xidl_parser_derive::node_id!("scoped_name") => {
91 scoped_name = Some(ScopedName::from_node(ch, ctx)?);
92 }
93 xidl_parser_derive::node_id!("annotation_appl_params") => {
94 params = Some(AnnotationParams::from_node(ch, ctx)?);
95 }
96 _ => {}
97 }
98 }
99 let scoped_name = scoped_name.ok_or_else(|| {
100 crate::error::ParseError::UnexpectedNode(
101 "annotation_appl_custom_body missing scoped_name".to_string(),
102 )
103 })?;
104 return Ok(Self {
105 span,
106 name: AnnotationName::ScopedName(scoped_name),
107 params,
108 builtin: None,
109 is_extend,
110 extra,
111 });
112 }
113
114 let builtin = builtin_body
115 .map(|node| parse_builtin_annotation(node, ctx))
116 .transpose()?;
117 let source = builtin_body
118 .map(|node| ctx.node_text(&node))
119 .transpose()?
120 .unwrap_or(raw.as_str());
121 let source = source.trim().strip_prefix("//@").unwrap_or(source);
122 let raw = source.trim().strip_prefix('@').unwrap_or(source).trim();
123 let (name, args) = match raw.split_once('(') {
124 Some((name, rest)) => {
125 let args = rest.strip_suffix(')').unwrap_or(rest).trim();
126 (name.trim(), Some(args))
127 }
128 None => (raw, None),
129 };
130
131 Ok(Self {
132 span,
133 name: AnnotationName::Builtin(name.to_string()),
134 params: args.map(|value| AnnotationParams::Raw(value.to_string())),
135 builtin,
136 is_extend,
137 extra,
138 })
139 }
140}
141
142fn requires_raw_annotation_parse(raw: &str) -> bool {
143 let raw = crate::parser::raw::RawArguments::without_comments(raw);
144 let source = raw.trim();
145 let source = source.strip_prefix("//@").unwrap_or(source);
146 let source = source.strip_prefix('@').unwrap_or(source);
147 let name = source
148 .split_once('(')
149 .map(|(name, _)| name)
150 .unwrap_or(source);
151 name.contains('-') || crate::parser::raw::RawArguments::contains_array(source)
152}
153
154fn parse_annotation_from_raw(
155 raw: &str,
156 is_extend: bool,
157 extra: Vec<AnnotationAppl>,
158) -> crate::error::ParserResult<AnnotationAppl> {
159 let source = raw.trim();
160 let source = source.strip_prefix("//@").unwrap_or(source);
161 let source = source.strip_prefix('@').unwrap_or(source).trim();
162 let (name, args) = match source.split_once('(') {
163 Some((name, rest)) => {
164 let args = rest.strip_suffix(')').unwrap_or(rest).trim();
165 (name.trim(), Some(args))
166 }
167 None => (source, None),
168 };
169 Ok(AnnotationAppl {
170 span: None,
171 name: AnnotationName::Builtin(name.to_string()),
172 params: args.map(|value| AnnotationParams::Raw(value.to_string())),
173 builtin: None,
174 is_extend,
175 extra,
176 })
177}
178
179impl AnnotationAppl {
180 pub fn doc(text: String) -> Self {
181 let escaped = escape_doc_text(&text);
182 Self {
183 span: None,
184 name: AnnotationName::Builtin("doc".to_string()),
185 params: Some(AnnotationParams::Raw(format!("\"{}\"", escaped))),
186 builtin: None,
187 is_extend: false,
188 extra: Vec::new(),
189 }
190 }
191}
192
193fn escape_doc_text(text: &str) -> String {
194 let mut out = String::with_capacity(text.len());
195 for ch in text.chars() {
196 match ch {
197 '\\' => out.push_str("\\\\"),
198 '"' => out.push_str("\\\""),
199 '\n' => out.push_str("\\n"),
200 '\r' => {}
201 _ => out.push(ch),
202 }
203 }
204 out
205}
206
207impl<'a> crate::parser::FromTreeSitter<'a> for AnnotationParams {
208 fn from_node(
209 node: tree_sitter::Node<'a>,
210 ctx: &mut crate::parser::ParseContext<'a>,
211 ) -> crate::error::ParserResult<Self> {
212 assert_eq!(
213 node.kind_id(),
214 xidl_parser_derive::node_id!("annotation_appl_params")
215 );
216
217 let mut params = vec![];
218 for ch in node.children(&mut node.walk()) {
219 match ch.kind_id() {
220 xidl_parser_derive::node_id!("const_expr") => {
221 return Ok(Self::Params(vec![AnnotationApplParam::Positional(
222 ConstExpr::from_node(ch, ctx)?,
223 )]));
224 }
225 xidl_parser_derive::node_id!("annotation_appl_param") => {
226 params.push(AnnotationApplParam::from_node(ch, ctx)?);
227 }
228 _ => {}
229 }
230 }
231
232 if !params.is_empty() {
233 return Ok(Self::Params(params));
234 }
235
236 Err(crate::error::ParseError::UnexpectedNode(
237 "annotation_appl_params missing content".to_string(),
238 ))
239 }
240}
241
242impl<'a> crate::parser::FromTreeSitter<'a> for AnnotationApplParam {
243 fn from_node(
244 node: tree_sitter::Node<'a>,
245 ctx: &mut crate::parser::ParseContext<'a>,
246 ) -> crate::error::ParserResult<Self> {
247 assert_eq!(
248 node.kind_id(),
249 xidl_parser_derive::node_id!("annotation_appl_param")
250 );
251
252 let mut ident = None;
253 let mut value = None;
254 for ch in node.children(&mut node.walk()) {
255 match ch.kind_id() {
256 xidl_parser_derive::node_id!("identifier") => {
257 ident = Some(Identifier::from_node(ch, ctx)?);
258 }
259 xidl_parser_derive::node_id!("const_expr") => {
260 value = Some(ConstExpr::from_node(ch, ctx)?);
261 }
262 _ => {}
263 }
264 }
265
266 match (ident, value) {
267 (None, Some(value)) => Ok(Self::Positional(value)),
268 (Some(ident), Some(value)) => Ok(Self::Named { ident, value }),
269 _ => Err(crate::error::ParseError::UnexpectedNode(
270 "annotation_appl_param missing const_expr".to_string(),
271 )),
272 }
273 }
274}
275
276impl std::fmt::Debug for AnnotationAppl {
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 f.debug_struct("AnnotationAppl")
279 .field("name", &self.name)
280 .field("params", &self.params)
281 .field("builtin", &self.builtin)
282 .field("is_extend", &self.is_extend)
283 .field("extra", &self.extra)
284 .finish()
285 }
286}