1use std::rc::Rc;
4
5use indexmap::IndexMap;
6
7use super::{Document, Timestamp, Value};
8
9#[derive(Debug, Clone)]
12pub(crate) enum Val {
13 Null,
14 Bool(bool),
15 Int(i64),
16 Float(f64),
17 Str(Rc<str>),
18 Bytes(Rc<[u8]>),
19 List(Rc<[Val]>),
20 Map(Rc<IndexMap<String, Val>>),
21 Set(Rc<[Val]>),
23 MapDiff(Rc<MapDiff>),
24 Timestamp(i64, u32),
26 Duration(i128),
28 LatLng(f64, f64),
29 Path(Rc<[String]>),
30 Constraint(Rc<[super::constraint::Simple]>),
32 Unknown,
35}
36
37#[derive(Debug)]
41pub(crate) struct MapDiff {
42 pub added: Vec<String>,
43 pub removed: Vec<String>,
44 pub changed: Vec<String>,
45 pub unchanged: Vec<String>,
46 pub statuses: Vec<(String, super::trace::KeyStatus)>,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
52pub(crate) struct Error {
53 pub message: String,
54 pub at: Option<usize>,
55}
56
57pub(crate) type R = Result<Val, Error>;
58
59pub(crate) const NANOS: i128 = 1_000_000_000;
60pub(crate) const MIN_SECONDS: i64 = -62_135_596_800;
61pub(crate) const MAX_SECONDS: i64 = 253_402_300_799;
62const MAX_DURATION_SECONDS: i128 = 315_576_000_000;
63
64impl Error {
65 pub fn new(message: impl Into<String>) -> Self {
66 Self {
67 message: message.into(),
68 at: None,
69 }
70 }
71
72 pub fn at(mut self, at: usize) -> Self {
75 if self.at.is_none() && !self.message.is_empty() {
76 self.at = Some(at);
77 }
78 self
79 }
80
81 pub fn undecided() -> Self {
84 Self::new(String::new())
85 }
86
87 pub fn null() -> Self {
88 Self::new("Null value error.".to_owned())
89 }
90
91 pub fn unbound_template() -> Self {
93 Self::new("Variable is not bound in path template.".to_owned())
94 }
95
96 pub fn property(name: &str) -> Self {
97 Self::new(format!("Property {name} is undefined on object."))
98 }
99
100 pub fn type_error(received: &str, expected: &str) -> Self {
101 Self::new(format!(
102 "Type error. Received: [{received}] Expected: [{expected}]."
103 ))
104 }
105
106 pub fn unsupported(received: &str, expected: &[String]) -> Self {
107 Self::new(format!(
108 "Unsupported operation error. Received: {received}. Expected: {}.",
109 expected.join(", ")
110 ))
111 }
112
113 pub fn not_found(name: &str) -> Self {
114 Self::new(format!("Function not found error: Name: [{name}]."))
115 }
116
117 pub fn arity(received: usize, expected: &[String]) -> Self {
118 Self::new(format!(
119 "Incorrect number of arguments. Received: {received}. Expected: {}.",
120 expected.join(", ")
121 ))
122 }
123
124 pub fn index(index: i64, size: usize) -> Self {
125 Self::new(format!(
126 "Index out of bound error. Index: [{index}] , size: [{size}]."
127 ))
128 }
129
130 pub fn range(from: i64, to: i64) -> Self {
131 Self::new(format!(
132 "Illegal range error. From index: [{from}] , To index: [{to}]."
133 ))
134 }
135
136 pub fn overflow() -> Self {
137 Self::new(
138 "Integer value is out of the [-9223372036854775808, 9223372036854775807] range."
139 .to_owned(),
140 )
141 }
142
143 pub fn divide_by_zero() -> Self {
144 Self::new("Divide by zero error.".to_owned())
145 }
146
147 pub fn conversion(argument: &str, from: &str, to: &str) -> Self {
148 Self::new(format!(
149 "Type conversion error. Argument: [{argument}], From type: [{from}], To type: [{to}]."
150 ))
151 }
152
153 pub fn regex(pattern: &str) -> Self {
154 Self::new(format!(
155 "Invalid regular expression pattern. Pattern: {pattern}."
156 ))
157 }
158
159 pub fn timestamp() -> Self {
160 Self::new(
161 "Timestamp is not valid. Seconds must be in range [-62,135,596,800, \
162 +253,402,300,799]. Nanos must be in range [0, +999,999,999]."
163 .to_owned(),
164 )
165 }
166
167 pub fn duration() -> Self {
168 Self::new(
169 "Duration is not valid. Seconds must be in range [-315,576,000,000, \
170 +315,576,000,000]. Nanos must be in range [-999,999,999, +999,999,999]. Nanos must \
171 have the same sign as seconds."
172 .to_owned(),
173 )
174 }
175
176 pub fn service(function: &str, path: &[String]) -> Self {
179 let mut argument = "path_value {\n".to_owned();
180 for segment in path {
181 argument.push_str(" segments {\n simple: \"");
182 argument.push_str(&text_format_escape(segment));
183 argument.push_str("\"\n }\n");
184 }
185 argument.push_str("}\n");
186 Self::new(format!(
187 "Service call error. Function: [{function}], Argument: [{argument}]."
188 ))
189 }
190}
191
192fn text_format_escape(s: &str) -> String {
195 let mut out = String::with_capacity(s.len());
196 for &b in s.as_bytes() {
197 match b {
198 b'\x07' => out.push_str("\\a"),
199 b'\x08' => out.push_str("\\b"),
200 b'\x0c' => out.push_str("\\f"),
201 b'\n' => out.push_str("\\n"),
202 b'\r' => out.push_str("\\r"),
203 b'\t' => out.push_str("\\t"),
204 b'\x0b' => out.push_str("\\v"),
205 b'\\' => out.push_str("\\\\"),
206 b'\'' => out.push_str("\\'"),
207 b'"' => out.push_str("\\\""),
208 0x20..0x7f => out.push(char::from(b)),
209 _ => out.push_str(&format!("\\{b:03o}")),
210 }
211 }
212 out
213}
214
215impl Val {
216 pub fn str(s: &str) -> Self {
217 Self::Str(Rc::from(s))
218 }
219
220 pub fn string(s: String) -> Self {
221 Self::Str(Rc::from(s))
222 }
223
224 pub fn list(items: Vec<Val>) -> Self {
225 Self::List(Rc::from(items))
226 }
227
228 pub fn path(segments: Vec<String>) -> Self {
229 Self::Path(Rc::from(segments))
230 }
231
232 pub fn type_name(&self) -> &'static str {
234 match self {
235 Self::Null => "null",
236 Self::Bool(_) => "bool",
237 Self::Int(_) => "int",
238 Self::Float(_) => "float",
239 Self::Str(_) => "string",
240 Self::Bytes(_) => "bytes",
241 Self::List(_) => "list",
242 Self::Map(_) => "map",
243 Self::Set(_) => "set",
244 Self::MapDiff(_) => "map_diff",
245 Self::Timestamp(..) => "timestamp",
246 Self::Duration(_) => "duration",
247 Self::LatLng(..) => "latlng",
248 Self::Path(_) => "path",
249 Self::Constraint(_) => "constraint",
250 Self::Unknown => "unknown",
251 }
252 }
253
254 pub fn is(&self, type_name: &str) -> bool {
256 match type_name {
257 "number" => matches!(self, Self::Int(_) | Self::Float(_)),
258 "set" | "vector" => false,
259 name => self.type_name() == name,
260 }
261 }
262
263 pub fn timestamp(seconds: i64, nanos: u32) -> R {
265 if (MIN_SECONDS..=MAX_SECONDS).contains(&seconds) {
266 Ok(Self::Timestamp(seconds, nanos))
267 } else {
268 Err(Error::timestamp())
269 }
270 }
271
272 pub fn timestamp_nanos(nanos: i128) -> R {
274 let seconds = nanos.div_euclid(NANOS);
275 let seconds = i64::try_from(seconds).map_err(|_| Error::timestamp())?;
276 Self::timestamp(seconds, nanos.rem_euclid(NANOS) as u32)
277 }
278
279 pub fn duration(nanos: i128) -> R {
281 if (nanos / NANOS).abs() <= MAX_DURATION_SECONDS {
282 Ok(Self::Duration(nanos))
283 } else {
284 Err(Error::duration())
285 }
286 }
287}
288
289pub(crate) fn eq(a: &Val, b: &Val) -> bool {
292 match (a, b) {
293 (Val::Int(x), Val::Float(y)) | (Val::Float(y), Val::Int(x)) => (*x as f64) == *y,
294 _ => strict_eq(a, b),
295 }
296}
297
298pub(crate) fn strict_eq(a: &Val, b: &Val) -> bool {
300 same(a, b, strict_eq)
301}
302
303pub(crate) fn loose_eq(a: &Val, b: &Val) -> bool {
306 match (a, b) {
307 (Val::Int(x), Val::Float(y)) | (Val::Float(y), Val::Int(x)) => (*x as f64) == *y,
308 _ => same(a, b, loose_eq),
309 }
310}
311
312fn same(a: &Val, b: &Val, inner: fn(&Val, &Val) -> bool) -> bool {
313 match (a, b) {
314 (Val::Null, Val::Null) => true,
315 (Val::Bool(x), Val::Bool(y)) => x == y,
316 (Val::Int(x), Val::Int(y)) => x == y,
317 (Val::Float(x), Val::Float(y)) => x == y,
318 (Val::Str(x), Val::Str(y)) => x == y,
319 (Val::Bytes(x), Val::Bytes(y)) => x == y,
320 (Val::List(x), Val::List(y)) => {
321 x.len() == y.len() && x.iter().zip(y.iter()).all(|(a, b)| inner(a, b))
322 }
323 (Val::Map(x), Val::Map(y)) => {
324 x.len() == y.len() && x.iter().all(|(k, v)| y.get(k).is_some_and(|w| inner(v, w)))
325 }
326 (Val::Set(x), Val::Set(y)) => {
327 x.len() == y.len() && x.iter().all(|a| y.iter().any(|b| loose_eq(a, b)))
328 }
329 (Val::Timestamp(s, n), Val::Timestamp(t, m)) => s == t && n == m,
330 (Val::Duration(x), Val::Duration(y)) => x == y,
331 (Val::LatLng(a, b), Val::LatLng(c, d)) => a == c && b == d,
332 (Val::Path(x), Val::Path(y)) => x == y,
333 _ => false,
335 }
336}
337
338pub(crate) fn contains(items: &[Val], v: &Val, eq: fn(&Val, &Val) -> bool) -> bool {
340 items.iter().any(|x| eq(x, v))
341}
342
343pub(crate) fn to_set(items: impl IntoIterator<Item = Val>) -> Val {
345 let mut out: Vec<Val> = Vec::new();
346 for item in items {
347 if !contains(&out, &item, loose_eq) {
348 out.push(item);
349 }
350 }
351 Val::Set(Rc::from(out))
352}
353
354impl From<&Value> for Val {
355 fn from(v: &Value) -> Self {
356 match v {
357 Value::Null => Self::Null,
358 Value::Bool(b) => Self::Bool(*b),
359 Value::Int(i) => Self::Int(*i),
360 Value::Float(f) => Self::Float(*f),
361 Value::String(s) => Self::str(s),
362 Value::Bytes(b) => Self::Bytes(Rc::from(b.as_slice())),
363 Value::Timestamp(Timestamp { seconds, nanos }) => {
364 let total = i128::from(*seconds) * NANOS + i128::from(*nanos);
365 Self::Timestamp(
366 total.div_euclid(NANOS) as i64,
367 total.rem_euclid(NANOS) as u32,
368 )
369 }
370 Value::Duration(seconds, nanos) => {
371 Self::Duration(i128::from(*seconds) * NANOS + i128::from(*nanos))
372 }
373 Value::LatLng(lat, lng) => Self::LatLng(*lat, *lng),
374 Value::Path(segments) => Self::Path(Rc::from(segments.as_slice())),
375 Value::List(items) => Self::List(items.iter().map(Self::from).collect()),
376 Value::Map(map) => Self::Map(Rc::new(
377 map.iter()
378 .map(|(k, v)| (k.clone(), Self::from(v)))
379 .collect(),
380 )),
381 }
382 }
383}
384
385pub(crate) fn resource(doc: &Document) -> Val {
387 let mut map = IndexMap::with_capacity(3);
388 map.insert("__name__".to_owned(), Val::path(doc.path.clone()));
389 map.insert(
390 "data".to_owned(),
391 Val::Map(Rc::new(
392 doc.data
393 .iter()
394 .map(|(k, v)| (k.clone(), Val::from(v)))
395 .collect(),
396 )),
397 );
398 map.insert(
399 "id".to_owned(),
400 Val::str(doc.path.last().map_or("", String::as_str)),
401 );
402 Val::Map(Rc::new(map))
403}
404
405pub fn java_double(f: f64) -> String {
408 if f.is_nan() {
409 return "NaN".to_owned();
410 }
411 if f.is_infinite() {
412 return if f > 0.0 { "Infinity" } else { "-Infinity" }.to_owned();
413 }
414 if f == 0.0 {
415 return if f.is_sign_negative() { "-0.0" } else { "0.0" }.to_owned();
416 }
417 let sign = if f < 0.0 { "-" } else { "" };
418 let sci = format!("{:e}", f.abs());
420 let (mantissa, exponent) = sci.split_once('e').unwrap_or((&sci, "0"));
421 let exponent: i32 = exponent.parse().unwrap_or(0);
422 let digits: String = mantissa.chars().filter(char::is_ascii_digit).collect();
423 let abs = f.abs();
424 if (1e-3..1e7).contains(&abs) {
425 let point = exponent + 1;
426 let (int, frac) = if point <= 0 {
427 (
428 "0".to_owned(),
429 format!("{}{digits}", "0".repeat(point.unsigned_abs() as usize)),
430 )
431 } else {
432 let point = point as usize;
433 let padded = format!("{digits:0<point$}");
434 (padded[..point].to_owned(), padded[point..].to_owned())
435 };
436 let frac = if frac.is_empty() {
437 "0".to_owned()
438 } else {
439 frac
440 };
441 format!("{sign}{int}.{frac}")
442 } else {
443 let (first, rest) = digits.split_at(1);
444 let rest = if rest.is_empty() { "0" } else { rest };
445 format!("{sign}{first}.{rest}E{exponent}")
446 }
447}
448
449pub(crate) fn parse_java_double(s: &str) -> Option<f64> {
452 let t = s.trim_matches(|c: char| c <= ' ');
453 let (sign, body) = match t.as_bytes().first() {
454 Some(b'-') => (-1.0, &t[1..]),
455 Some(b'+') => (1.0, &t[1..]),
456 _ => (1.0, t),
457 };
458 match body {
459 "NaN" => return Some(f64::NAN),
460 "Infinity" => return Some(sign * f64::INFINITY),
461 _ => {}
462 }
463 let body = body.strip_suffix(['d', 'D', 'f', 'F']).unwrap_or(body);
464 let bytes = body.as_bytes();
465 let mut i = 0;
466 let digits = |i: &mut usize| {
467 let start = *i;
468 while *i < bytes.len() && bytes[*i].is_ascii_digit() {
469 *i += 1;
470 }
471 *i - start
472 };
473 let mut count = digits(&mut i);
474 if i < bytes.len() && bytes[i] == b'.' {
475 i += 1;
476 count += digits(&mut i);
477 }
478 if count == 0 {
479 return None;
480 }
481 if i < bytes.len() && matches!(bytes[i], b'e' | b'E') {
482 i += 1;
483 if i < bytes.len() && matches!(bytes[i], b'+' | b'-') {
484 i += 1;
485 }
486 if digits(&mut i) == 0 {
487 return None;
488 }
489 }
490 if i != bytes.len() {
491 return None;
492 }
493 body.parse::<f64>().ok().map(|f| sign * f)
494}
495
496pub(crate) fn parse_java_long(s: &str) -> Option<i64> {
498 let digits = s.strip_prefix(['+', '-']).unwrap_or(s);
499 if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) {
500 return None;
501 }
502 s.strip_prefix('+').unwrap_or(s).parse().ok()
503}
504
505pub(crate) fn utf16_len(s: &str) -> usize {
507 s.chars().map(char::len_utf16).sum()
508}
509
510pub(crate) fn utf16_slice(s: &str, from: usize, to: usize) -> String {
512 let units: Vec<u16> = s.encode_utf16().collect();
513 String::from_utf16_lossy(&units[from..to])
514}
515
516pub(crate) fn utf16_cmp(a: &str, b: &str) -> std::cmp::Ordering {
518 a.encode_utf16().cmp(b.encode_utf16())
519}
520
521pub(crate) fn has_unknown(segments: &[String]) -> bool {
523 segments
524 .iter()
525 .any(|s| s == crate::paths::UNKNOWN_ID || s == crate::paths::UNKNOWN_RUN)
526}
527
528pub(crate) fn read(v: Option<&Val>) -> Option<R> {
530 v.map(|v| match v {
531 Val::Unknown => Err(Error::undecided()),
532 v => Ok(v.clone()),
533 })
534}
535
536#[cfg(test)]
537mod tests {
538 use super::*;
539
540 #[test]
541 fn java_doubles() {
542 for (f, s) in [
543 (1.0, "1.0"),
544 (1.5, "1.5"),
545 (-2.25, "-2.25"),
546 (1_000_000.0, "1000000.0"),
547 (10_000_000.0, "1.0E7"),
548 (100_000_000.0, "1.0E8"),
549 (123_456_789.0, "1.23456789E8"),
550 (0.001, "0.001"),
551 (0.0001, "1.0E-4"),
552 (0.000_123, "1.23E-4"),
553 (-0.0, "-0.0"),
554 (0.1 + 0.2, "0.30000000000000004"),
555 (1e300, "1.0E300"),
556 (f64::NAN, "NaN"),
557 (f64::NEG_INFINITY, "-Infinity"),
558 (9_223_372_036_854_775_807.0, "9.223372036854776E18"),
559 ] {
560 assert_eq!(java_double(f), s, "{f}");
561 }
562 }
563
564 #[test]
565 fn java_parsing() {
566 assert_eq!(parse_java_double(" 1.5 "), Some(1.5));
567 assert_eq!(parse_java_double("1e3"), Some(1000.0));
568 assert_eq!(parse_java_double("-.5"), Some(-0.5));
569 assert_eq!(parse_java_double("2."), Some(2.0));
570 assert_eq!(parse_java_double("1.5d"), Some(1.5));
571 assert_eq!(parse_java_double("-Infinity"), Some(f64::NEG_INFINITY));
572 assert!(parse_java_double("NaN").unwrap().is_nan());
573 for bad in ["inf", "abc", "", ".", "1e", "1.5x", "0x10", "nan"] {
574 assert_eq!(parse_java_double(bad), None, "{bad}");
575 }
576 assert_eq!(parse_java_long("+12"), Some(12));
577 assert_eq!(parse_java_long("-12"), Some(-12));
578 for bad in [" 12", "1.5", "", "+", "9223372036854775808", "0x10"] {
579 assert_eq!(parse_java_long(bad), None, "{bad}");
580 }
581 }
582
583 #[test]
584 fn equalities() {
585 let list = |items: Vec<Val>| Val::list(items);
586 assert!(eq(&Val::Int(1), &Val::Float(1.0)));
587 assert!(!eq(&list(vec![Val::Int(1)]), &list(vec![Val::Float(1.0)])));
588 assert!(loose_eq(
589 &list(vec![Val::Int(1)]),
590 &list(vec![Val::Float(1.0)])
591 ));
592 assert!(!eq(&Val::str("1"), &Val::Int(1)));
593 assert!(!eq(&Val::Null, &Val::Bool(false)));
594 assert!(!eq(&Val::Float(f64::NAN), &Val::Float(f64::NAN)));
595 }
596
597 #[test]
598 fn service_errors_print_the_path_as_text() {
599 let path: Vec<String> = ["databases", "a\"b", "é"].map(String::from).to_vec();
600 assert_eq!(
601 Error::service("get", &path).message,
602 "Service call error. Function: [get], Argument: [path_value {\n segments {\n \
603 simple: \"databases\"\n }\n segments {\n simple: \"a\\\"b\"\n }\n segments {\n \
604 simple: \"\\303\\251\"\n }\n}\n]."
605 );
606 }
607}