Skip to main content

xidl_parser/typed_ast/
annotation.rs

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