darklua_core/nodes/statements/
local_assign.rs1use crate::nodes::{Expression, Token, TypedIdentifier};
2
3#[deprecated(since = "0.19.0", note = "Renamed to `VariableAssignmentTokens`")]
4pub type LocalAssignTokens = VariableAssignmentTokens;
5
6#[deprecated(since = "0.19.0", note = "Renamed to `VariableAssignment`")]
7pub type LocalAssignStatement = VariableAssignment;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
10pub enum AssignmentKind {
11 #[default]
12 Local,
13 Const,
14}
15
16impl AssignmentKind {
17 pub fn as_keyword(&self) -> &'static str {
18 match self {
19 AssignmentKind::Local => "local",
20 AssignmentKind::Const => "const",
21 }
22 }
23}
24
25#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct VariableAssignmentTokens {
28 pub keyword: Token,
29 pub equal: Option<Token>,
31 pub variable_commas: Vec<Token>,
33 pub value_commas: Vec<Token>,
35}
36
37impl VariableAssignmentTokens {
38 super::impl_token_fns!(
39 target = [keyword]
40 iter = [variable_commas, value_commas, equal]
41 );
42}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
46pub struct VariableAssignment {
47 keyword: AssignmentKind,
48 variables: Vec<TypedIdentifier>,
49 values: Vec<Expression>,
50 tokens: Option<VariableAssignmentTokens>,
51}
52
53impl VariableAssignment {
54 pub fn new(variables: Vec<TypedIdentifier>, values: Vec<Expression>) -> Self {
56 Self {
57 keyword: AssignmentKind::Local,
58 variables,
59 values,
60 tokens: None,
61 }
62 }
63
64 pub fn from_variable<S: Into<TypedIdentifier>>(variable: S) -> Self {
66 Self {
67 keyword: AssignmentKind::Local,
68 variables: vec![variable.into()],
69 values: Vec::new(),
70 tokens: None,
71 }
72 }
73
74 pub fn with_tokens(mut self, tokens: VariableAssignmentTokens) -> Self {
76 self.tokens = Some(tokens);
77 self
78 }
79
80 #[inline]
82 pub fn set_tokens(&mut self, tokens: VariableAssignmentTokens) {
83 self.tokens = Some(tokens);
84 }
85
86 #[inline]
88 pub fn get_tokens(&self) -> Option<&VariableAssignmentTokens> {
89 self.tokens.as_ref()
90 }
91
92 #[inline]
94 pub fn mutate_tokens(&mut self) -> Option<&mut VariableAssignmentTokens> {
95 self.tokens.as_mut()
96 }
97
98 pub fn with_variable<S: Into<TypedIdentifier>>(mut self, variable: S) -> Self {
100 self.variables.push(variable.into());
101 self
102 }
103
104 pub fn with_value<E: Into<Expression>>(mut self, value: E) -> Self {
106 self.values.push(value.into());
107 self
108 }
109
110 pub fn into_assignments(self) -> (Vec<TypedIdentifier>, Vec<Expression>) {
112 (self.variables, self.values)
113 }
114
115 pub fn append_assignment<S: Into<TypedIdentifier>>(&mut self, variable: S, value: Expression) {
117 self.variables.push(variable.into());
118 self.values.push(value);
119 }
120
121 pub fn for_each_assignment<F>(&mut self, mut callback: F)
123 where
124 F: FnMut(&mut TypedIdentifier, Option<&mut Expression>),
125 {
126 let mut values = self.values.iter_mut();
127 self.variables
128 .iter_mut()
129 .for_each(|variable| callback(variable, values.next()));
130 }
131
132 pub fn with_assignment_kind(mut self, kind: AssignmentKind) -> Self {
134 self.set_assignment_kind(kind);
135 self
136 }
137
138 pub fn set_assignment_kind(&mut self, kind: AssignmentKind) {
140 if self.keyword == kind {
141 return;
142 }
143 if let Some(tokens) = &mut self.tokens {
144 tokens.keyword.replace_with_content(kind.as_keyword());
145 }
146 self.keyword = kind;
147 }
148
149 pub fn get_assignment_kind(&self) -> AssignmentKind {
151 self.keyword
152 }
153
154 #[inline]
156 pub fn get_variables(&self) -> &Vec<TypedIdentifier> {
157 &self.variables
158 }
159
160 #[inline]
162 pub fn iter_variables(&self) -> impl Iterator<Item = &TypedIdentifier> {
163 self.variables.iter()
164 }
165
166 #[inline]
168 pub fn iter_mut_variables(&mut self) -> impl Iterator<Item = &mut TypedIdentifier> {
169 self.variables.iter_mut()
170 }
171
172 #[inline]
174 pub fn append_variables(&mut self, variables: &mut Vec<TypedIdentifier>) {
175 self.variables.append(variables);
176 }
177
178 #[inline]
180 pub fn extend_values<T: IntoIterator<Item = Expression>>(&mut self, iter: T) {
181 self.values.extend(iter);
182 }
183
184 #[inline]
186 pub fn iter_mut_values(&mut self) -> impl Iterator<Item = &mut Expression> {
187 self.values.iter_mut()
188 }
189
190 #[inline]
192 pub fn iter_values(&self) -> impl Iterator<Item = &Expression> {
193 self.values.iter()
194 }
195
196 #[inline]
198 pub fn push_variable(&mut self, variable: impl Into<TypedIdentifier>) {
199 self.variables.push(variable.into());
200 }
201
202 #[inline]
204 pub fn push_value(&mut self, value: impl Into<Expression>) {
205 self.values.push(value.into());
206 }
207
208 #[inline]
210 pub fn append_values(&mut self, values: &mut Vec<Expression>) {
211 self.values.append(values);
212 }
213
214 #[inline]
216 pub fn last_value(&self) -> Option<&Expression> {
217 self.values.last()
218 }
219
220 pub fn pop_value(&mut self) -> Option<Expression> {
222 let value = self.values.pop();
223 if let Some(tokens) = &mut self.tokens {
224 let length = self.values.len();
225 if length == 0 {
226 if !tokens.value_commas.is_empty() {
227 tokens.value_commas.clear();
228 }
229 if tokens.equal.is_some() {
230 tokens.equal = None;
231 }
232 } else {
233 tokens.value_commas.truncate(length.saturating_sub(1));
234 }
235 }
236 value
237 }
238
239 pub fn remove_value(&mut self, index: usize) -> Option<Expression> {
241 if index < self.values.len() {
242 let value = self.values.remove(index);
243
244 if let Some(tokens) = &mut self.tokens {
245 if index < tokens.value_commas.len() {
246 tokens.value_commas.remove(index);
247 }
248 if self.values.is_empty() && tokens.equal.is_some() {
249 tokens.equal = None;
250 }
251 }
252
253 Some(value)
254 } else {
255 None
256 }
257 }
258
259 pub fn remove_variable(&mut self, index: usize) -> Option<TypedIdentifier> {
263 let len = self.variables.len();
264
265 if len > 1 && index < len {
266 let variable = self.variables.remove(index);
267
268 if let Some(tokens) = &mut self.tokens {
269 if index < tokens.variable_commas.len() {
270 tokens.variable_commas.remove(index);
271 }
272 }
273
274 Some(variable)
275 } else {
276 None
277 }
278 }
279
280 #[inline]
282 pub fn values_len(&self) -> usize {
283 self.values.len()
284 }
285
286 #[inline]
288 pub fn variables_len(&self) -> usize {
289 self.variables.len()
290 }
291
292 #[inline]
294 pub fn has_values(&self) -> bool {
295 !self.values.is_empty()
296 }
297
298 pub fn required_nil_values(&self) -> usize {
301 match self.keyword {
302 AssignmentKind::Local => 0,
303 AssignmentKind::Const => {
304 let length = self.variables.len();
305
306 if length <= self.values.len()
307 || self
308 .values
309 .last()
310 .map(|last| {
311 matches!(last, Expression::Call(_) | Expression::VariableArguments(_))
312 })
313 .unwrap_or_default()
314 {
315 0
316 } else {
317 length - self.values.len()
318 }
319 }
320 }
321 }
322
323 pub fn required_new_variables(&self) -> usize {
327 match self.keyword {
328 AssignmentKind::Local if self.variables.is_empty() => 1,
329 AssignmentKind::Local => 0,
330 AssignmentKind::Const => self.values.len().saturating_sub(self.variables.len()),
331 }
332 }
333
334 pub fn clear_types(&mut self) {
336 for variable in &mut self.variables {
337 variable.remove_type();
338 }
339 }
340
341 pub fn mutate_first_token(&mut self) -> &mut Token {
343 if self.tokens.is_none() {
344 self.tokens = Some(VariableAssignmentTokens {
345 keyword: Token::from_content(self.keyword.as_keyword()),
346 equal: (!self.values.is_empty()).then(|| Token::from_content("=")),
347 variable_commas: Vec::new(),
348 value_commas: Vec::new(),
349 });
350 }
351 &mut self.tokens.as_mut().unwrap().keyword
352 }
353
354 pub fn mutate_last_token(&mut self) -> &mut Token {
357 if let Some(last_value) = self.values.last_mut() {
358 return last_value.mutate_last_token();
359 }
360 self.variables
361 .last_mut()
362 .expect("local assign must have at least one variable")
363 .mutate_or_insert_token()
364 }
365
366 super::impl_token_fns!(iter = [variables, tokens]);
367}
368
369#[cfg(test)]
370mod test {
371 use super::*;
372
373 mod pop_value {
374 use super::*;
375
376 #[test]
377 fn removes_the_equal_sign() {
378 let mut assign = VariableAssignment::from_variable("var")
379 .with_value(true)
380 .with_tokens(VariableAssignmentTokens {
381 keyword: Token::from_content("local"),
382 equal: Some(Token::from_content("=")),
383 variable_commas: Vec::new(),
384 value_commas: Vec::new(),
385 });
386
387 assign.pop_value();
388
389 pretty_assertions::assert_eq!(
390 assign,
391 VariableAssignment::from_variable("var").with_tokens(VariableAssignmentTokens {
392 keyword: Token::from_content("local"),
393 equal: None,
394 variable_commas: Vec::new(),
395 value_commas: Vec::new(),
396 })
397 );
398 }
399
400 #[test]
401 fn removes_the_last_comma_token() {
402 let mut assign = VariableAssignment::from_variable("var")
403 .with_value(true)
404 .with_value(false)
405 .with_tokens(VariableAssignmentTokens {
406 keyword: Token::from_content("local"),
407 equal: Some(Token::from_content("=")),
408 variable_commas: Vec::new(),
409 value_commas: vec![Token::from_content(",")],
410 });
411
412 assign.pop_value();
413
414 pretty_assertions::assert_eq!(
415 assign,
416 VariableAssignment::from_variable("var")
417 .with_value(true)
418 .with_tokens(VariableAssignmentTokens {
419 keyword: Token::from_content("local"),
420 equal: Some(Token::from_content("=")),
421 variable_commas: Vec::new(),
422 value_commas: Vec::new(),
423 })
424 );
425 }
426
427 #[test]
428 fn removes_one_comma_token() {
429 let mut assign = VariableAssignment::from_variable("var")
430 .with_value(true)
431 .with_value(false)
432 .with_value(true)
433 .with_tokens(VariableAssignmentTokens {
434 keyword: Token::from_content("local"),
435 equal: Some(Token::from_content("=")),
436 variable_commas: Vec::new(),
437 value_commas: vec![Token::from_content(","), Token::from_content(",")],
438 });
439
440 assign.pop_value();
441
442 pretty_assertions::assert_eq!(
443 assign,
444 VariableAssignment::from_variable("var")
445 .with_value(true)
446 .with_value(false)
447 .with_tokens(VariableAssignmentTokens {
448 keyword: Token::from_content("local"),
449 equal: Some(Token::from_content("=")),
450 variable_commas: Vec::new(),
451 value_commas: vec![Token::from_content(",")],
452 })
453 );
454 }
455 }
456
457 mod remove_variable {
458 use super::*;
459
460 #[test]
461 fn single_variable_returns_none_without_mutating() {
462 let mut assign = VariableAssignment::from_variable("var").with_value(true);
463 let copy = assign.clone();
464
465 assert_eq!(assign.remove_variable(0), None);
466
467 pretty_assertions::assert_eq!(assign, copy);
468 }
469
470 #[test]
471 fn single_variable_remove_outside_of_bounds() {
472 let mut assign = VariableAssignment::from_variable("var");
473 let copy = assign.clone();
474
475 assert_eq!(assign.remove_variable(1), None);
476 pretty_assertions::assert_eq!(assign, copy);
477
478 assert_eq!(assign.remove_variable(3), None);
479 pretty_assertions::assert_eq!(assign, copy);
480 }
481
482 #[test]
483 fn two_variables_remove_first() {
484 let mut assign = VariableAssignment::from_variable("var")
485 .with_variable("var2")
486 .with_value(true)
487 .with_value(false);
488
489 assert_eq!(assign.remove_variable(0), Some(TypedIdentifier::new("var")));
490
491 pretty_assertions::assert_eq!(
492 assign,
493 VariableAssignment::from_variable("var2")
494 .with_value(true)
495 .with_value(false)
496 );
497 }
498
499 #[test]
500 fn two_variables_remove_second() {
501 let mut assign = VariableAssignment::from_variable("var")
502 .with_variable("var2")
503 .with_value(true)
504 .with_value(false);
505
506 assert_eq!(
507 assign.remove_variable(1),
508 Some(TypedIdentifier::new("var2"))
509 );
510
511 pretty_assertions::assert_eq!(
512 assign,
513 VariableAssignment::from_variable("var")
514 .with_value(true)
515 .with_value(false)
516 );
517 }
518 }
519}