darklua_core/rules/
convert_luau_number.rs1use 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#[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}