1use get_field_by_type::GetFieldByType;
2use iterator_endiate::EndiateIteratorExt;
3use source_map::Span;
4use tokenizer_lib::{
5 sized_tokens::{TokenReaderWithTokenEnds, TokenStart},
6 Token, TokenReader,
7};
8use visitable_derive::Visitable;
9
10use crate::{
11 derive_ASTNode, tokens::token_as_identifier, ASTNode, Expression, ParseOptions, ParseResult,
12 TSXToken, Visitable,
13};
14
15#[derive(Debug, PartialEq, Clone, Visitable)]
16#[apply(derive_ASTNode)]
17pub struct Decorator {
18 pub name: Vec<String>,
19 pub arguments: Option<Vec<Expression>>,
20 pub position: Span,
21}
22
23impl ASTNode for Decorator {
24 fn get_position(&self) -> Span {
25 self.position
26 }
27
28 fn from_reader(
29 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
30 state: &mut crate::ParsingState,
31 options: &ParseOptions,
32 ) -> ParseResult<Self> {
33 let at_pos = reader.expect_next(TSXToken::At)?;
34 Self::from_reader_sub_at_symbol(reader, state, options, at_pos)
35 }
36
37 fn to_string_from_buffer<T: source_map::ToString>(
38 &self,
39 buf: &mut T,
40 options: &crate::ToStringOptions,
41 local: crate::LocalToStringInformation,
42 ) {
43 if options.include_decorators {
44 buf.push('@');
45 for (not_at_end, value) in self.name.iter().nendiate() {
46 buf.push_str(value);
47 if not_at_end {
48 buf.push('.');
49 }
50 }
51 if let Some(arguments) = &self.arguments {
52 buf.push('(');
53 for (at_end, argument) in arguments.iter().endiate() {
54 argument.to_string_from_buffer(buf, options, local);
55 if !at_end {
56 buf.push(',');
57 options.push_gap_optionally(buf);
58 }
59 }
60 buf.push(')');
61 }
62 }
63 }
64}
65
66impl Decorator {
67 pub(crate) fn from_reader_sub_at_symbol(
68 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
69 state: &mut crate::ParsingState,
70 options: &ParseOptions,
71 at_pos: TokenStart,
72 ) -> ParseResult<Self> {
73 let (name, mut last_position) =
74 token_as_identifier(reader.next().unwrap(), "Decorator name")?;
75
76 let mut names = vec![name];
77 while matches!(reader.peek(), Some(Token(TSXToken::Dot, _))) {
78 let (name, pos) = token_as_identifier(reader.next().unwrap(), "Nested decorator name")?;
79 last_position = pos;
80 names.push(name);
81 }
82
83 let (arguments, position) = if reader
84 .conditional_next(|token| matches!(token, TSXToken::OpenParentheses))
85 .is_some()
86 {
87 let mut arguments = Vec::<_>::new();
88 loop {
89 if let Some(Token(TSXToken::CloseParentheses, _)) = reader.peek() {
90 break;
91 }
92 arguments.push(Expression::from_reader(reader, state, options)?);
93 match reader.peek() {
94 Some(Token(TSXToken::Comma, _)) => {
95 reader.next();
96 }
97 _ => break,
98 }
99 }
100 let end = reader.expect_next_get_end(TSXToken::CloseParentheses)?;
101 (Some(arguments), at_pos.union(end))
102 } else {
103 (None, at_pos.union(last_position))
104 };
105 Ok(Self { name: names, arguments, position })
106 }
107}
108
109#[apply(derive_ASTNode)]
111#[derive(Debug, PartialEq, Clone, get_field_by_type::GetFieldByType)]
112#[get_field_by_type_target(Span)]
113pub struct Decorated<T> {
114 pub decorators: Vec<Decorator>,
115 pub on: T,
116 pub position: Span,
118}
119
120impl<N: ASTNode> ASTNode for Decorated<N> {
121 fn get_position(&self) -> Span {
122 *self.get()
123 }
124
125 fn from_reader(
126 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
127 state: &mut crate::ParsingState,
128 options: &ParseOptions,
129 ) -> ParseResult<Self> {
130 let decorators = decorators_from_reader(reader, state, options)?;
131 N::from_reader(reader, state, options).map(|on| Self::new(decorators, on))
132 }
133
134 fn to_string_from_buffer<T: source_map::ToString>(
135 &self,
136 buf: &mut T,
137 options: &crate::ToStringOptions,
138 local: crate::LocalToStringInformation,
139 ) {
140 self.to_string_from_buffer_just_decorators(buf, options, local);
141 self.on.to_string_from_buffer(buf, options, local);
142 }
143}
144
145impl<U: ASTNode> Decorated<U> {
146 pub fn new_empty(on: U) -> Self {
147 Self::new(Default::default(), on)
148 }
149
150 pub fn new(decorators: Vec<Decorator>, on: U) -> Self {
151 let position =
152 decorators.first().map_or(on.get_position(), |d| d.position).union(on.get_position());
153 Self { decorators, on, position }
154 }
155
156 pub(crate) fn to_string_from_buffer_just_decorators<T: source_map::ToString>(
157 &self,
158 buf: &mut T,
159 options: &crate::ToStringOptions,
160 local: crate::LocalToStringInformation,
161 ) {
162 if options.include_decorators {
163 for decorator in &self.decorators {
164 decorator.to_string_from_buffer(buf, options, local);
165 if options.pretty {
166 buf.push_new_line();
167 } else {
168 buf.push(' ');
169 }
170 }
171 }
172 }
173}
174
175pub(crate) fn decorators_from_reader(
176 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
177 state: &mut crate::ParsingState,
178 options: &ParseOptions,
179) -> ParseResult<Vec<Decorator>> {
180 let mut decorators = Vec::new();
181 while let Some(Token(TSXToken::At, _)) = reader.peek() {
182 decorators.push(Decorator::from_reader(reader, state, options)?);
183 }
184 Ok(decorators)
185}
186
187impl<T: Visitable> Visitable for Decorated<T> {
188 fn visit<TData>(
189 &self,
190 visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
191 data: &mut TData,
192 options: &crate::VisitOptions,
193
194 chain: &mut temporary_annex::Annex<crate::Chain>,
195 ) {
196 self.on.visit(visitors, data, options, chain);
197 }
198
199 fn visit_mut<TData>(
200 &mut self,
201 visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
202 data: &mut TData,
203 options: &crate::VisitOptions,
204
205 chain: &mut temporary_annex::Annex<crate::Chain>,
206 ) {
207 self.on.visit_mut(visitors, data, options, chain);
208 }
209}