1use std::collections::HashMap;
2
3use super::{EvalContext, Module, StaticValue, Type, Value};
4
5#[derive(Debug)]
7pub struct String_;
8
9impl Module for String_ {
10 fn get_name(&self) -> &'static str {
11 "string"
12 }
13
14 fn get_static_values(&self) -> HashMap<&'static str, StaticValue> {
15 [
16 (
17 "to_int",
18 StaticValue::function(
19 Self::to_int,
20 vec![vec![Type::Bytes], vec![Type::Bytes, Type::Integer]],
21 Type::Integer,
22 ),
23 ),
24 (
25 "length",
26 StaticValue::function(Self::length, vec![vec![Type::Bytes]], Type::Integer),
27 ),
28 ]
29 .into()
30 }
31}
32
33impl String_ {
34 fn to_int(_ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
35 let mut args = args.into_iter();
36
37 let s: Vec<u8> = args.next()?.try_into().ok()?;
38
39 let mut s = s.as_slice();
44 while !s.is_empty() && s[0].is_ascii_whitespace() {
45 s = &s[1..];
46 }
47
48 let mut base: u32 = match args.next() {
49 Some(Value::Integer(i)) => {
50 let i = i.try_into().ok()?;
51 if i == 0 || (2..=36).contains(&i) {
52 i
53 } else {
54 return None;
55 }
56 }
57 Some(_) => return None,
58 None => 0,
59 };
60
61 let is_negative = if s.starts_with(b"-") {
62 s = &s[1..];
63 true
64 } else if s.starts_with(b"+") {
65 s = &s[1..];
66 false
67 } else {
68 false
69 };
70
71 if base == 0 {
72 if s.starts_with(b"0x") || s.starts_with(b"0X") {
73 base = 16;
74 s = &s[2..];
75 } else if s.starts_with(b"0") {
76 base = 8;
77 } else {
81 base = 10;
82 }
83 }
84
85 if s.is_empty() {
86 return None;
87 }
88
89 let mut res: i64 = 0;
90 for c in s {
91 match (*c as char).to_digit(base) {
92 Some(c) => {
93 res = res.checked_mul(i64::from(base))?;
94 if is_negative {
95 res = res.checked_sub(i64::from(c))?;
96 } else {
97 res = res.checked_add(i64::from(c))?;
98 }
99 }
100 None => return None,
101 }
102 }
103 Some(Value::Integer(res))
104 }
105
106 fn length(_ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
107 let mut args = args.into_iter();
108 let s: Vec<u8> = args.next()?.try_into().ok()?;
109
110 Some(Value::Integer(s.len().try_into().ok()?))
111 }
112}