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 {
388 let mut map = IndexMap::with_capacity(3);
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("__name__".to_owned(), Val::path(doc.path.clone()));
399 map.insert(
400 "id".to_owned(),
401 Val::str(doc.path.last().map_or("", String::as_str)),
402 );
403 Val::Map(Rc::new(map))
404}
405
406pub fn java_double(f: f64) -> String {
409 if f.is_nan() {
410 return "NaN".to_owned();
411 }
412 if f.is_infinite() {
413 return if f > 0.0 { "Infinity" } else { "-Infinity" }.to_owned();
414 }
415 if f == 0.0 {
416 return if f.is_sign_negative() { "-0.0" } else { "0.0" }.to_owned();
417 }
418 let sign = if f < 0.0 { "-" } else { "" };
419 let sci = format!("{:e}", f.abs());
421 let (mantissa, exponent) = sci.split_once('e').unwrap_or((&sci, "0"));
422 let exponent: i32 = exponent.parse().unwrap_or(0);
423 let digits: String = mantissa.chars().filter(char::is_ascii_digit).collect();
424 let abs = f.abs();
425 if (1e-3..1e7).contains(&abs) {
426 let point = exponent + 1;
427 let (int, frac) = if point <= 0 {
428 (
429 "0".to_owned(),
430 format!("{}{digits}", "0".repeat(point.unsigned_abs() as usize)),
431 )
432 } else {
433 let point = point as usize;
434 let padded = format!("{digits:0<point$}");
435 (padded[..point].to_owned(), padded[point..].to_owned())
436 };
437 let frac = if frac.is_empty() {
438 "0".to_owned()
439 } else {
440 frac
441 };
442 format!("{sign}{int}.{frac}")
443 } else {
444 let (first, rest) = digits.split_at(1);
445 let rest = if rest.is_empty() { "0" } else { rest };
446 format!("{sign}{first}.{rest}E{exponent}")
447 }
448}
449
450pub(crate) fn parse_java_double(s: &str) -> Option<f64> {
453 let t = s.trim_matches(|c: char| c <= ' ');
454 let (sign, body) = match t.as_bytes().first() {
455 Some(b'-') => (-1.0, &t[1..]),
456 Some(b'+') => (1.0, &t[1..]),
457 _ => (1.0, t),
458 };
459 match body {
460 "NaN" => return Some(f64::NAN),
461 "Infinity" => return Some(sign * f64::INFINITY),
462 _ => {}
463 }
464 let body = body.strip_suffix(['d', 'D', 'f', 'F']).unwrap_or(body);
465 let bytes = body.as_bytes();
466 let mut i = 0;
467 let digits = |i: &mut usize| {
468 let start = *i;
469 while *i < bytes.len() && bytes[*i].is_ascii_digit() {
470 *i += 1;
471 }
472 *i - start
473 };
474 let mut count = digits(&mut i);
475 if i < bytes.len() && bytes[i] == b'.' {
476 i += 1;
477 count += digits(&mut i);
478 }
479 if count == 0 {
480 return None;
481 }
482 if i < bytes.len() && matches!(bytes[i], b'e' | b'E') {
483 i += 1;
484 if i < bytes.len() && matches!(bytes[i], b'+' | b'-') {
485 i += 1;
486 }
487 if digits(&mut i) == 0 {
488 return None;
489 }
490 }
491 if i != bytes.len() {
492 return None;
493 }
494 body.parse::<f64>().ok().map(|f| sign * f)
495}
496
497pub(crate) fn parse_java_long(s: &str) -> Option<i64> {
499 let digits = s.strip_prefix(['+', '-']).unwrap_or(s);
500 if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) {
501 return None;
502 }
503 s.strip_prefix('+').unwrap_or(s).parse().ok()
504}
505
506pub(crate) fn utf16_len(s: &str) -> usize {
508 s.chars().map(char::len_utf16).sum()
509}
510
511pub(crate) fn utf16_slice(s: &str, from: usize, to: usize) -> String {
513 let units: Vec<u16> = s.encode_utf16().collect();
514 String::from_utf16_lossy(&units[from..to])
515}
516
517pub(crate) fn utf16_cmp(a: &str, b: &str) -> std::cmp::Ordering {
519 a.encode_utf16().cmp(b.encode_utf16())
520}
521
522pub(crate) fn has_unknown(segments: &[String]) -> bool {
524 segments
525 .iter()
526 .any(|s| s == crate::paths::UNKNOWN_ID || s == crate::paths::UNKNOWN_RUN)
527}
528
529pub(crate) fn read(v: Option<&Val>) -> Option<R> {
531 v.map(|v| match v {
532 Val::Unknown => Err(Error::undecided()),
533 v => Ok(v.clone()),
534 })
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540
541 #[test]
542 fn java_doubles() {
543 for (f, s) in [
544 (1.0, "1.0"),
545 (1.5, "1.5"),
546 (-2.25, "-2.25"),
547 (1_000_000.0, "1000000.0"),
548 (10_000_000.0, "1.0E7"),
549 (100_000_000.0, "1.0E8"),
550 (123_456_789.0, "1.23456789E8"),
551 (0.001, "0.001"),
552 (0.0001, "1.0E-4"),
553 (0.000_123, "1.23E-4"),
554 (-0.0, "-0.0"),
555 (0.1 + 0.2, "0.30000000000000004"),
556 (1e300, "1.0E300"),
557 (f64::NAN, "NaN"),
558 (f64::NEG_INFINITY, "-Infinity"),
559 (9_223_372_036_854_775_807.0, "9.223372036854776E18"),
560 ] {
561 assert_eq!(java_double(f), s, "{f}");
562 }
563 }
564
565 #[test]
566 fn java_parsing() {
567 assert_eq!(parse_java_double(" 1.5 "), Some(1.5));
568 assert_eq!(parse_java_double("1e3"), Some(1000.0));
569 assert_eq!(parse_java_double("-.5"), Some(-0.5));
570 assert_eq!(parse_java_double("2."), Some(2.0));
571 assert_eq!(parse_java_double("1.5d"), Some(1.5));
572 assert_eq!(parse_java_double("-Infinity"), Some(f64::NEG_INFINITY));
573 assert!(parse_java_double("NaN").unwrap().is_nan());
574 for bad in ["inf", "abc", "", ".", "1e", "1.5x", "0x10", "nan"] {
575 assert_eq!(parse_java_double(bad), None, "{bad}");
576 }
577 assert_eq!(parse_java_long("+12"), Some(12));
578 assert_eq!(parse_java_long("-12"), Some(-12));
579 for bad in [" 12", "1.5", "", "+", "9223372036854775808", "0x10"] {
580 assert_eq!(parse_java_long(bad), None, "{bad}");
581 }
582 }
583
584 #[test]
585 fn equalities() {
586 let list = |items: Vec<Val>| Val::list(items);
587 assert!(eq(&Val::Int(1), &Val::Float(1.0)));
588 assert!(!eq(&list(vec![Val::Int(1)]), &list(vec![Val::Float(1.0)])));
589 assert!(loose_eq(
590 &list(vec![Val::Int(1)]),
591 &list(vec![Val::Float(1.0)])
592 ));
593 assert!(!eq(&Val::str("1"), &Val::Int(1)));
594 assert!(!eq(&Val::Null, &Val::Bool(false)));
595 assert!(!eq(&Val::Float(f64::NAN), &Val::Float(f64::NAN)));
596 }
597
598 #[test]
599 fn service_errors_print_the_path_as_text() {
600 let path: Vec<String> = ["databases", "a\"b", "é"].map(String::from).to_vec();
601 assert_eq!(
602 Error::service("get", &path).message,
603 "Service call error. Function: [get], Argument: [path_value {\n segments {\n \
604 simple: \"databases\"\n }\n segments {\n simple: \"a\\\"b\"\n }\n segments {\n \
605 simple: \"\\303\\251\"\n }\n}\n]."
606 );
607 }
608}