Skip to main content

darklua_core/rules/
convert_luau_number.rs

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