darklua_core/nodes/statements/
if_statement.rs

1use std::mem;
2
3use crate::nodes::{Block, Expression, Token};
4
5/// Tokens associated with an if branch.
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub struct IfBranchTokens {
8    pub elseif: Token,
9    pub then: Token,
10}
11
12impl IfBranchTokens {
13    super::impl_token_fns!(target = [elseif, then]);
14}
15
16/// Represents a conditional branch in an if statement.
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct IfBranch {
19    condition: Expression,
20    block: Block,
21    tokens: Option<IfBranchTokens>,
22}
23
24impl IfBranch {
25    /// Creates a new if branch with the given condition and block.
26    pub fn new<E: Into<Expression>, B: Into<Block>>(condition: E, block: B) -> Self {
27        Self {
28            condition: condition.into(),
29            block: block.into(),
30            tokens: None,
31        }
32    }
33
34    /// Creates a new if branch with the given condition and an empty block.
35    pub fn empty<E: Into<Expression>>(condition: E) -> Self {
36        Self {
37            condition: condition.into(),
38            block: Block::default(),
39            tokens: None,
40        }
41    }
42
43    /// Sets the tokens for this if branch.
44    pub fn with_tokens(mut self, tokens: IfBranchTokens) -> Self {
45        self.tokens = Some(tokens);
46        self
47    }
48
49    /// Sets the tokens for this if branch.
50    #[inline]
51    pub fn set_tokens(&mut self, tokens: IfBranchTokens) {
52        self.tokens = Some(tokens);
53    }
54
55    /// Returns the tokens for this if branch, if any.
56    #[inline]
57    pub fn get_tokens(&self) -> Option<&IfBranchTokens> {
58        self.tokens.as_ref()
59    }
60
61    /// Returns the block of code for this branch.
62    #[inline]
63    pub fn get_block(&self) -> &Block {
64        &self.block
65    }
66
67    /// Returns the condition for this branch.
68    #[inline]
69    pub fn get_condition(&self) -> &Expression {
70        &self.condition
71    }
72
73    /// Returns a mutable reference to the block.
74    #[inline]
75    pub fn mutate_block(&mut self) -> &mut Block {
76        &mut self.block
77    }
78
79    /// Takes ownership of the block, leaving an empty block in its place.
80    #[inline]
81    pub fn take_block(&mut self) -> Block {
82        mem::take(&mut self.block)
83    }
84
85    /// Returns a mutable reference to the condition.
86    #[inline]
87    pub fn mutate_condition(&mut self) -> &mut Expression {
88        &mut self.condition
89    }
90
91    super::impl_token_fns!(iter = [tokens]);
92}
93
94/// Tokens associated with an if statement.
95#[derive(Clone, Debug, PartialEq, Eq)]
96pub struct IfStatementTokens {
97    pub r#if: Token,
98    pub then: Token,
99    pub end: Token,
100    pub r#else: Option<Token>,
101}
102
103impl IfStatementTokens {
104    super::impl_token_fns!(
105        target = [r#if, then, end]
106        iter = [r#else]
107    );
108}
109
110/// Represents an if statement.
111#[derive(Clone, Debug, PartialEq, Eq)]
112pub struct IfStatement {
113    branches: Vec<IfBranch>,
114    else_block: Option<Block>,
115    tokens: Option<IfStatementTokens>,
116}
117
118impl IfStatement {
119    /// Creates a new if statement with the given branches and optional else block.
120    pub fn new(branches: Vec<IfBranch>, else_block: Option<Block>) -> Self {
121        Self {
122            branches,
123            else_block,
124            tokens: None,
125        }
126    }
127
128    /// Creates a new if statement with a single condition and block.
129    pub fn create(condition: impl Into<Expression>, block: impl Into<Block>) -> Self {
130        Self {
131            branches: vec![IfBranch::new(condition, block)],
132            else_block: None,
133            tokens: None,
134        }
135    }
136
137    /// Sets the tokens for this if statement.
138    pub fn with_tokens(mut self, tokens: IfStatementTokens) -> Self {
139        self.tokens = Some(tokens);
140        self
141    }
142
143    /// Sets the tokens for this if statement.
144    #[inline]
145    pub fn set_tokens(&mut self, tokens: IfStatementTokens) {
146        self.tokens = Some(tokens);
147    }
148
149    /// Returns the tokens for this if statement, if any.
150    #[inline]
151    pub fn get_tokens(&self) -> Option<&IfStatementTokens> {
152        self.tokens.as_ref()
153    }
154
155    /// Returns a mutable reference to the tokens, if any.
156    #[inline]
157    pub fn mutate_tokens(&mut self) -> Option<&mut IfStatementTokens> {
158        self.tokens.as_mut()
159    }
160
161    /// Adds a branch to this if statement.
162    pub fn with_branch(mut self, branch: IfBranch) -> Self {
163        self.branches.push(branch);
164        self
165    }
166
167    /// Adds a new branch with the given condition and block.
168    pub fn with_new_branch(
169        mut self,
170        condition: impl Into<Expression>,
171        block: impl Into<Block>,
172    ) -> Self {
173        self.branches.push(IfBranch::new(condition, block));
174        self
175    }
176
177    /// Adds an else block to this if statement.
178    pub fn with_else_block<B: Into<Block>>(mut self, block: B) -> Self {
179        self.else_block.replace(block.into());
180        self
181    }
182
183    /// Returns mutable references to all blocks in this if statement.
184    pub fn mutate_all_blocks(&mut self) -> Vec<&mut Block> {
185        let mut blocks: Vec<&mut Block> = self
186            .branches
187            .iter_mut()
188            .map(|branch| branch.mutate_block())
189            .collect();
190
191        if let Some(else_block) = &mut self.else_block {
192            blocks.push(else_block);
193        };
194
195        blocks
196    }
197
198    /// Returns the branches of this if statement.
199    #[inline]
200    pub fn get_branches(&self) -> &Vec<IfBranch> {
201        &self.branches
202    }
203
204    /// Returns an iterator over the branches.
205    #[inline]
206    pub fn iter_branches(&self) -> impl Iterator<Item = &IfBranch> {
207        self.branches.iter()
208    }
209
210    /// Returns the number of branches.
211    #[inline]
212    pub fn branch_count(&self) -> usize {
213        self.branches.len()
214    }
215
216    /// Returns a mutable reference to the branches.
217    #[inline]
218    pub fn mutate_branches(&mut self) -> &mut Vec<IfBranch> {
219        &mut self.branches
220    }
221
222    /// Adds a new branch with the given condition and block.
223    #[inline]
224    pub fn push_new_branch(&mut self, condition: impl Into<Expression>, block: impl Into<Block>) {
225        self.branches
226            .push(IfBranch::new(condition.into(), block.into()));
227    }
228
229    /// Adds a branch to this if statement.
230    #[inline]
231    pub fn push_branch(&mut self, branch: IfBranch) {
232        self.branches.push(branch);
233    }
234
235    /// Returns the else block, if any.
236    #[inline]
237    pub fn get_else_block(&self) -> Option<&Block> {
238        self.else_block.as_ref()
239    }
240
241    /// Returns a mutable reference to the else block option.
242    #[inline]
243    pub fn mutate_else_block(&mut self) -> &mut Option<Block> {
244        &mut self.else_block
245    }
246
247    /// Sets the else block.
248    #[inline]
249    pub fn set_else_block(&mut self, block: impl Into<Block>) {
250        self.else_block = Some(block.into());
251    }
252
253    /// Removes the else block, if any.
254    #[inline]
255    pub fn take_else_block(&mut self) -> Option<Block> {
256        self.else_block.take()
257    }
258
259    /// Filters branches in-place, ensuring at least one branch remains.
260    pub fn retain_branches_mut(&mut self, filter: impl FnMut(&mut IfBranch) -> bool) -> bool {
261        self.branches.retain_mut(filter);
262        if self.branches.is_empty() {
263            // an if statement requires at least one branch
264            self.branches.push(IfBranch::new(false, Block::default()));
265            true
266        } else {
267            false
268        }
269    }
270
271    /// Returns a mutable reference to the first token for this statement, creating it if missing.
272    pub fn mutate_first_token(&mut self) -> &mut Token {
273        self.set_default_tokens();
274        &mut self.tokens.as_mut().unwrap().r#if
275    }
276
277    /// Returns a mutable reference to the last token for this statement,
278    /// creating it if missing.
279    pub fn mutate_last_token(&mut self) -> &mut Token {
280        self.set_default_tokens();
281        &mut self.tokens.as_mut().unwrap().end
282    }
283
284    fn set_default_tokens(&mut self) {
285        if self.tokens.is_none() {
286            self.tokens = Some(IfStatementTokens {
287                r#if: Token::from_content("if"),
288                then: Token::from_content("then"),
289                end: Token::from_content("end"),
290                r#else: None,
291            });
292        }
293    }
294
295    super::impl_token_fns!(iter = [tokens, branches]);
296}