1use steel::rvals::SteelVal;
3use rust_decimal::prelude::*;
4use std::str::FromStr;
5use thiserror::Error;
6
7#[derive(Debug, Error)]
8pub enum ConversionError {
9 #[error("Invalid decimal format: {0}")]
10 InvalidDecimal(String),
11 #[error("Unsupported SteelVal type: {0:?}")]
12 UnsupportedType(SteelVal),
13 #[error("Type conversion failed: {0}")]
14 ConversionFailed(String),
15}
16
17pub struct TypeConverter;
19
20impl TypeConverter {
21 pub fn steel_val_to_decimal(val: &SteelVal) -> Result<Decimal, ConversionError> {
23 match val {
24 SteelVal::StringV(s) => {
25 Decimal::from_str(&s.to_string())
26 .map_err(|e| ConversionError::InvalidDecimal(format!("{}: {}", s, e)))
27 }
28 SteelVal::NumV(n) => {
29 Decimal::try_from(*n)
30 .map_err(|e| ConversionError::InvalidDecimal(format!("{}: {}", n, e)))
31 }
32 SteelVal::IntV(i) => {
33 Ok(Decimal::from(*i))
34 }
35 _ => Err(ConversionError::UnsupportedType(val.clone()))
36 }
37 }
38
39 pub fn decimal_to_steel_val(decimal: Decimal) -> SteelVal {
41 SteelVal::StringV(decimal.to_string().into())
42 }
43
44 pub fn steel_vals_to_strings(vals: Vec<SteelVal>) -> Result<Vec<String>, ConversionError> {
46 vals.into_iter()
47 .map(|val| Self::steel_val_to_string(val))
48 .collect()
49 }
50
51 pub fn steel_val_to_string(val: SteelVal) -> Result<String, ConversionError> {
53 match val {
54 SteelVal::StringV(s) => Ok(s.to_string()),
55 SteelVal::NumV(n) => Ok(n.to_string()),
56 SteelVal::IntV(i) => Ok(i.to_string()),
57 SteelVal::BoolV(b) => Ok(b.to_string()),
58 SteelVal::VectorV(v) => {
59 let string_values: Result<Vec<String>, _> = v.iter()
60 .map(|item| Self::steel_val_to_string(item.clone()))
61 .collect();
62
63 match string_values {
64 Ok(strings) => Ok(strings.join(",")),
65 Err(e) => Err(e),
66 }
67 }
68 _ => Err(ConversionError::UnsupportedType(val))
69 }
70 }
71
72 pub fn validate_decimal_string(s: &str) -> Result<String, ConversionError> {
74 Decimal::from_str(s)
75 .map(|d| d.to_string())
76 .map_err(|e| ConversionError::InvalidDecimal(format!("{}: {}", s, e)))
77 }
78
79 pub fn f64_to_decimal_string(f: f64) -> Result<String, ConversionError> {
81 Decimal::try_from(f)
82 .map(|d| d.to_string())
83 .map_err(|e| ConversionError::ConversionFailed(format!("f64 to decimal: {}", e)))
84 }
85
86 pub fn i64_to_decimal_string(i: i64) -> String {
87 Decimal::from(i).to_string()
88 }
89
90 pub fn u64_to_decimal_string(u: u64) -> String {
91 Decimal::from(u).to_string()
92 }
93}
94
95pub struct ScriptAnalyzer;
97
98impl ScriptAnalyzer {
99 pub fn is_decimal_like(s: &str) -> bool {
101 Decimal::from_str(s.trim()).is_ok()
102 }
103
104 pub fn extract_string_literals(script: &str) -> Vec<String> {
106 let re = regex::Regex::new(r#""((?:\\.|[^"])*)""#).unwrap();
107 re.captures_iter(script)
108 .map(|cap| cap[1].to_string())
109 .collect()
110 }
111
112 pub fn count_function_calls(script: &str, function_name: &str) -> usize {
114 let pattern = format!(r"\({}\s+", regex::escape(function_name));
115 let re = regex::Regex::new(&pattern).unwrap();
116 re.find_iter(script).count()
117 }
118
119 pub fn contains_decimal_functions(script: &str) -> bool {
121 let decimal_functions = [
122 "decimal-add", "decimal-sub", "decimal-mul", "decimal-div",
123 "decimal-pow", "decimal-sqrt", "decimal-ln", "decimal-log10", "decimal-exp",
124 "decimal-sin", "decimal-cos", "decimal-tan",
125 "decimal-gt", "decimal-gte", "decimal-lt", "decimal-lte", "decimal-eq",
126 "decimal-abs", "decimal-round", "decimal-min", "decimal-max",
127 ];
128
129 decimal_functions.iter().any(|func| script.contains(func))
130 }
131}
132
133pub struct DecimalPrecision;
135
136impl DecimalPrecision {
137 pub fn set_precision(decimal_str: &str, precision: u32) -> Result<String, ConversionError> {
139 let decimal = Decimal::from_str(decimal_str)
140 .map_err(|e| ConversionError::InvalidDecimal(format!("{}: {}", decimal_str, e)))?;
141
142 Ok(decimal.round_dp(precision).to_string())
143 }
144
145 pub fn get_decimal_places(decimal_str: &str) -> Result<u32, ConversionError> {
147 let decimal = Decimal::from_str(decimal_str)
148 .map_err(|e| ConversionError::InvalidDecimal(format!("{}: {}", decimal_str, e)))?;
149
150 Ok(decimal.scale())
151 }
152
153 pub fn normalize(decimal_str: &str) -> Result<String, ConversionError> {
155 let decimal = Decimal::from_str(decimal_str)
156 .map_err(|e| ConversionError::InvalidDecimal(format!("{}: {}", decimal_str, e)))?;
157
158 Ok(decimal.normalize().to_string())
159 }
160}