datex_core/datex_values/
primitive.rs

1use core::fmt::Write;
2use std::fmt;
3
4use num_bigint::BigInt;
5use regex::Regex;
6use lazy_static::lazy_static;
7
8
9use crate::global::binary_codes::BinaryCode;
10
11use super::{Value, Error, ValueResult, Quantity, Endpoint, primitives::time::Time, Url};
12
13#[derive(Clone)]
14pub enum PrimitiveValue {
15	Int8(i8),
16	Uint8(u8),
17	Int16(i16),
18	Int32(i32),
19	UInt16(u16),
20	UInt32(u32),
21	Int64(i64),
22	Float64(f64),
23	BigInt(BigInt),
24	Text(String),
25	Buffer(Vec<u8>),
26	Boolean(bool),
27	Quantity(Quantity),
28	Time(Time),
29	Endpoint(Endpoint),
30	Url(Url),
31	Null,
32	Void
33}
34
35
36
37impl Default for PrimitiveValue {
38    fn default() -> Self { PrimitiveValue::Void }
39}
40
41impl fmt::Display for PrimitiveValue {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        write!(f, "{}", Value::to_string(self))
44    }
45}
46
47fn escape_string(value:&String) -> String {
48	// TODO: \n only if formatted?
49
50	// TODO:
51	// name = Regex::new(r"[\u0000-\u0008\u000B-\u001F\u007F-\u009F\u2000-\u200F\u2028-\u202F\u205F-\u206F\u3000\uFEFF\u{E0100}-\u{E01EF}]").
52	// 	unwrap().
53	// 	replace_all(&name, "").to_string();
54
55	value
56		.replace("\\", "\\\\")
57		.replace("\"", "\\\"")
58		.replace("\n", "\\n")
59		.replace("\r", "\\r")
60		.replace("\t", "\\t")
61		.replace("\u{0008}", "\\b")
62		.replace("\u{000c}", "\\f")
63		.replace("\u{001b}", "\\u001b")
64}
65
66impl Value for PrimitiveValue {
67	fn to_string(&self) -> String {
68		match &self {
69			PrimitiveValue::Int8(value) => value.to_string(),
70			PrimitiveValue::Uint8(value) => value.to_string(),
71			PrimitiveValue::Int16(value) => value.to_string(),
72			PrimitiveValue::UInt16(value) => value.to_string(),
73			PrimitiveValue::Int32(value) => value.to_string(),
74			PrimitiveValue::UInt32(value) => value.to_string(),
75			PrimitiveValue::Int64(value) => value.to_string(),
76			PrimitiveValue::BigInt(value) => value.to_string(),
77			PrimitiveValue::Float64(value) => {
78				if value.is_infinite() {
79					if value.is_sign_negative() {return "-infinity".to_string()}
80					else {return "infinity".to_string()}
81				}
82				else if value.is_nan() {return "nan".to_string()}
83				else {
84					let mut string = value.to_string();
85					if !string.contains('.') {string += ".0";}
86					return string;
87				}
88			},
89			PrimitiveValue::Text(value) => {
90				let string = escape_string(value);
91				return format!("\"{string}\"");
92			}
93			PrimitiveValue::Buffer(value) => {
94				let n = value.len();
95
96				let mut s = String::with_capacity(2 * n);
97				for byte in value {
98					write!(s, "{:02X}", byte).expect("could not parse buffer")
99				}
100				return format!("`{s}`");
101			},
102			PrimitiveValue::Boolean(value) => value.to_string(),
103			PrimitiveValue::Void => "void".to_string(),
104			PrimitiveValue::Null => "null".to_string(),
105			PrimitiveValue::Quantity(value) => value.to_string(false),
106			PrimitiveValue::Endpoint(value) => value.to_string(false),
107			PrimitiveValue::Time(value) => value.to_string(),
108			PrimitiveValue::Url(value) => value.to_string()
109		}
110    }
111
112	fn binary_operation(&self, code: BinaryCode, other: Box<dyn Value>) -> ValueResult {
113		if other.is::<PrimitiveValue>() {
114			let other_prim = other.downcast_ref::<PrimitiveValue>().expect("error casting stack value to primitive");
115
116			return match 
117				match code {
118					BinaryCode::ADD 		=> self.sum(other_prim),
119					BinaryCode::SUBTRACT 	=> self.difference(other_prim),
120					BinaryCode::MULTIPLY	=> self.product(other_prim),
121					BinaryCode::DIVIDE 		=> self.quotient(other_prim),
122					BinaryCode::MODULO 		=> self.modulo(other_prim),
123					BinaryCode::POWER 		=> self.power(other_prim),
124
125					_ => Err(Error {message:"invalid binary operation".to_string()})
126				} 
127			{
128				Ok(result) => Ok(Box::new(result)),
129				Err(err) => Err(err)
130			}
131	
132		}
133
134		return Err(Error {message:"invalid binary operation".to_string()})
135    }
136
137	fn cast(&self, dx_type: super::Type) -> ValueResult {
138
139		// TODO: type check
140		if dx_type.name == "text" { 
141			Ok(Box::new(PrimitiveValue::Text(Value::to_string(self))))
142		}
143		
144		else {Err(Error {message:format!("cannot cast to {dx_type}")})}
145    }
146
147}
148
149impl PrimitiveValue {
150
151	// special colorized form
152	pub fn to_string_colorized(&self) -> String {
153		match &self {
154			PrimitiveValue::Quantity(value) => value.to_string(true),
155			PrimitiveValue::Endpoint(value) => value.to_string(true),
156			_ => Value::to_string(self)
157		}
158	}
159	
160
161	fn sum(&self, other: &PrimitiveValue) -> Result<PrimitiveValue,Error> {
162		if self.is_number() && other.is_number() {
163			match self {
164				PrimitiveValue::Int8(val) 	=> Ok(PrimitiveValue::Int8   (val + other.get_as_integer() as i8)),
165				PrimitiveValue::Int16(val) 	=> Ok(PrimitiveValue::Int16  (val + other.get_as_integer() as i16)),
166				PrimitiveValue::Int32(val) 	=> Ok(PrimitiveValue::Int32  (val + other.get_as_integer() as i32)),
167				PrimitiveValue::Int64(val) 	=> Ok(PrimitiveValue::Int64  (val + other.get_as_integer() as i64)),
168				PrimitiveValue::Float64(val) => Ok(PrimitiveValue::Float64(val + other.get_as_float())),
169				_ => Err(Error {message:"cannot perform an add operation".to_string()})
170			}
171		}
172
173		else if self.is_text() && other.is_text() {
174			return Ok(PrimitiveValue::Text(self.get_as_text().to_owned() + other.get_as_text()));
175		}
176
177		else {return Err(Error {message:"cannot perform an add operation".to_string()})}
178	}
179
180	fn difference(&self, other: &PrimitiveValue) -> Result<PrimitiveValue,Error> {
181		if self.is_number() && other.is_number() {
182			match self {
183				PrimitiveValue::Int8(val) 	=> Ok(PrimitiveValue::Int8   (val - other.get_as_integer() as i8)),
184				PrimitiveValue::Int16(val) 	=> Ok(PrimitiveValue::Int16  (val - other.get_as_integer() as i16)),
185				PrimitiveValue::Int32(val) 	=> Ok(PrimitiveValue::Int32  (val - other.get_as_integer() as i32)),
186				PrimitiveValue::Int64(val) 	=> Ok(PrimitiveValue::Int64  (val - other.get_as_integer() as i64)),
187				PrimitiveValue::Float64(val) => Ok(PrimitiveValue::Float64(val - other.get_as_float())),
188				_ => Err(Error {message:"cannot perform a subtract operation".to_string()})
189			}
190		}
191		else {return Err(Error {message:"cannot perform a subtract operation".to_string()})}
192	}
193
194
195	fn product(&self, other: &PrimitiveValue) -> Result<PrimitiveValue,Error> {
196		if self.is_number() && other.is_number() {
197			match self {
198				PrimitiveValue::Int8(val) 	=> Ok(PrimitiveValue::Int8   (val * other.get_as_integer() as i8)),
199				PrimitiveValue::Int16(val) 	=> Ok(PrimitiveValue::Int16  (val * other.get_as_integer() as i16)),
200				PrimitiveValue::Int32(val) 	=> Ok(PrimitiveValue::Int32  (val * other.get_as_integer() as i32)),
201				PrimitiveValue::Int64(val) 	=> Ok(PrimitiveValue::Int64  (val * other.get_as_integer() as i64)),
202				PrimitiveValue::Float64(val) => Ok(PrimitiveValue::Float64(val * other.get_as_float())),
203				_ => Err(Error {message:"cannot perform a subtract operation".to_string()})
204			}
205		}
206		else {return Err(Error {message:"cannot perform a subtract operation".to_string()})}
207	}
208
209	fn quotient(&self, other: &PrimitiveValue) -> Result<PrimitiveValue,Error> {
210		if self.is_number() && other.is_number() {
211			match self {
212				PrimitiveValue::Int8(val) 	=> Ok(PrimitiveValue::Int8   (val / other.get_as_integer() as i8)),
213				PrimitiveValue::Int16(val) 	=> Ok(PrimitiveValue::Int16  (val / other.get_as_integer() as i16)),
214				PrimitiveValue::Int32(val) 	=> Ok(PrimitiveValue::Int32  (val / other.get_as_integer() as i32)),
215				PrimitiveValue::Int64(val) 	=> Ok(PrimitiveValue::Int64  (val / other.get_as_integer() as i64)),
216				PrimitiveValue::Float64(val) => Ok(PrimitiveValue::Float64(val / other.get_as_float())),
217				_ => Err(Error {message:"cannot perform a subtract operation".to_string()})
218			}
219		}
220		else {return Err(Error {message:"cannot perform a subtract operation".to_string()})}
221	}
222
223	fn modulo(&self, other: &PrimitiveValue) -> Result<PrimitiveValue,Error> {
224		if self.is_number() && other.is_number() {
225			match self {
226				PrimitiveValue::Int8(val) 	=> Ok(PrimitiveValue::Int8   (val % other.get_as_integer() as i8)),
227				PrimitiveValue::Int16(val) 	=> Ok(PrimitiveValue::Int16  (val % other.get_as_integer() as i16)),
228				PrimitiveValue::Int32(val) 	=> Ok(PrimitiveValue::Int32  (val % other.get_as_integer() as i32)),
229				PrimitiveValue::Int64(val) 	=> Ok(PrimitiveValue::Int64  (val % other.get_as_integer() as i64)),
230				PrimitiveValue::Float64(val) => Ok(PrimitiveValue::Float64(val % other.get_as_float())),
231				_ => Err(Error {message:"cannot perform a subtract operation".to_string()})
232			}
233		}
234		else {return Err(Error {message:"cannot perform a subtract operation".to_string()})}
235	}
236
237	fn power(&self, other: &PrimitiveValue) -> Result<PrimitiveValue,Error> {
238		if self.is_number() && other.is_number() {
239			match self {
240				PrimitiveValue::Int8(val) 	=> Ok(PrimitiveValue::Int8   (val.pow(other.get_as_integer() as u32))),
241				PrimitiveValue::Int16(val) 	=> Ok(PrimitiveValue::Int16  (val.pow(other.get_as_integer() as u32))),
242				PrimitiveValue::Int32(val) 	=> Ok(PrimitiveValue::Int32  (val.pow(other.get_as_integer() as u32))),
243				PrimitiveValue::Int64(val) 	=> Ok(PrimitiveValue::Int64  (val.pow(other.get_as_integer() as u32))),
244				PrimitiveValue::Float64(val) => Ok(PrimitiveValue::Float64(val.powf(other.get_as_integer() as f64))),
245				_ => Err(Error {message:"cannot perform a subtract operation".to_string()})
246			}
247		}
248		else {return Err(Error {message:"cannot perform a subtract operation".to_string()})}
249	}
250
251	pub fn is_number(&self) -> bool {
252		match &self {
253			PrimitiveValue::Int8(_) => true,
254			PrimitiveValue::Int16(_) => true,
255			PrimitiveValue::Int32(_) => true,
256			PrimitiveValue::Int64(_) => true,
257			PrimitiveValue::Float64(_) => true,
258			PrimitiveValue::BigInt(_) => true,
259			_ => false
260		}
261	}
262
263	pub fn is_text(&self) -> bool {
264		match &self {
265			PrimitiveValue::Text(_) => true,
266			_ => false
267		}
268	}
269
270	pub fn get_as_text(&self) -> &str {
271		match &self {
272			PrimitiveValue::Text(value) => value,
273			_ => ""
274		}
275	}
276
277	pub fn get_as_buffer(&self) -> Vec<u8> {
278		match &self {
279			PrimitiveValue::Buffer(value) => value.to_vec(),
280			_ => Vec::new()
281		}
282	}
283	
284	pub fn get_as_integer(&self) -> isize {
285		match &self {
286			PrimitiveValue::Int8(value) => *value as isize,
287			PrimitiveValue::Uint8(value) => *value as isize,
288			PrimitiveValue::UInt16(value) => *value as isize,
289			PrimitiveValue::Int16(value) => *value as isize,
290			PrimitiveValue::Int32(value) => *value as isize,
291			PrimitiveValue::UInt32(value) => *value as isize,
292			PrimitiveValue::Int64(value) => *value as isize,
293			PrimitiveValue::Float64(value) => *value as isize,
294			_ => 0
295		}
296	}
297
298	pub fn get_as_unsigned_integer(&self) -> usize {
299		match &self {
300			PrimitiveValue::Int8(value) => *value as usize,
301			PrimitiveValue::Uint8(value) => *value as usize,
302			PrimitiveValue::UInt16(value) => *value as usize,
303			PrimitiveValue::Int16(value) => *value as usize,
304			PrimitiveValue::Int32(value) => *value as usize,
305			PrimitiveValue::UInt32(value) => *value as usize,
306			PrimitiveValue::Int64(value) => *value as usize,
307			PrimitiveValue::Float64(value) => *value as usize,
308			_ => 0
309		}
310	}
311
312	pub fn get_as_float(&self) -> f64 {
313		match &self {
314			PrimitiveValue::Int8(value) => *value as f64,
315			PrimitiveValue::Uint8(value) => *value as f64,
316			PrimitiveValue::UInt16(value) => *value as f64,
317			PrimitiveValue::Int16(value) => *value as f64,
318			PrimitiveValue::Int32(value) => *value as f64,
319			PrimitiveValue::UInt32(value) => *value as f64,
320			PrimitiveValue::Int64(value) => *value as f64,
321			PrimitiveValue::Float64(value) => *value as f64,
322			_ => 0.0
323		}
324	}
325
326	
327	// returns a string, omits quotes if possible (for keys)
328	pub fn to_key_string(&self) -> String  {
329		match &self {
330			PrimitiveValue::Text(value) => {
331				let string = escape_string(value);
332				// key:
333				if KEY_CAN_OMIT_QUOTES.is_match(&string) {
334					return string;
335				}
336				// "key":
337				else {return format!("\"{string}\"");}
338			}
339			_ => Value::to_string(self)
340		}
341	}
342
343	// returns true if not a text, or if the text only contains A-Z,0-9...
344	pub fn can_omit_quotes(&self) -> bool  {
345		match &self {
346			PrimitiveValue::Text(value) =>  KEY_CAN_OMIT_QUOTES.is_match(&escape_string(value)),
347			_ => true
348		}
349	}
350}
351
352lazy_static! {
353	static ref KEY_CAN_OMIT_QUOTES:Regex = Regex::new(r"^[A-Za-z_][A-Za-z_0-9]*$").unwrap();
354}