darklua_core/rules/
convert_luau_number.rs

1use crate::{
2    nodes::{Block, HexNumber, NumberExpression, Token},
3    process::{DefaultVisitor, NodeProcessor, NodeVisitor},
4    rules::{Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties},
5};
6
7use super::verify_no_rule_properties;
8
9#[derive(Default)]
10struct Processor<'a> {
11    code: &'a str,
12}
13
14impl Processor<'_> {
15    fn trim_underscores(&self, token: &mut Token) {
16        let content = token.read(self.code);
17
18        if content.contains('_') {
19            token.replace_with_content(content.chars().filter(|c| *c != '_').collect::<String>());
20        }
21    }
22}
23
24impl NodeProcessor for Processor<'_> {
25    fn process_number_expression(&mut self, number: &mut NumberExpression) {
26        match number {
27            NumberExpression::Binary(binary) => {
28                let value = binary.get_raw_value();
29                *number = HexNumber::new(value, false).into();
30            }
31            NumberExpression::Hex(hex_number) => {
32                if let Some(token) = hex_number.mutate_token() {
33                    self.trim_underscores(token);
34                }
35            }
36            NumberExpression::Decimal(decimal_number) => {
37                if let Some(token) = decimal_number.mutate_token() {
38                    self.trim_underscores(token);
39                }
40            }
41        }
42    }
43}
44
45impl<'a> Processor<'a> {
46    fn new(code: &'a str) -> Self {
47        Self { code }
48    }
49}
50
51pub const CONVERT_LUAU_NUMBER_RULE_NAME: &str = "convert_luau_number";
52
53/// A rule that converts Luau number literals to regular Lua numbers.
54#[derive(Default, Debug, PartialEq, Eq)]
55pub struct ConvertLuauNumber {}
56
57impl FlawlessRule for ConvertLuauNumber {
58    fn flawless_process(&self, block: &mut Block, context: &Context) {
59        let mut processor = Processor::new(context.original_code());
60        DefaultVisitor::visit_block(block, &mut processor);
61    }
62}
63
64impl RuleConfiguration for ConvertLuauNumber {
65    fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
66        verify_no_rule_properties(&properties)?;
67
68        Ok(())
69    }
70
71    fn get_name(&self) -> &'static str {
72        CONVERT_LUAU_NUMBER_RULE_NAME
73    }
74
75    fn serialize_to_properties(&self) -> RuleProperties {
76        RuleProperties::new()
77    }
78}
79
80#[cfg(test)]
81mod test {
82    use super::*;
83    use crate::rules::Rule;
84
85    use insta::assert_json_snapshot;
86
87    fn new_rule() -> ConvertLuauNumber {
88        ConvertLuauNumber::default()
89    }
90
91    #[test]
92    fn serialize_default_rule() {
93        let rule: Box<dyn Rule> = Box::new(new_rule());
94
95        assert_json_snapshot!("default_convert_luau_number", rule);
96    }
97
98    #[test]
99    fn configure_with_extra_field_error() {
100        let result = json5::from_str::<Box<dyn Rule>>(
101            r#"{
102            rule: 'convert_luau_number',
103            prop: "something",
104        }"#,
105        );
106        pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'");
107    }
108}