// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #![allow( clippy::indexing_slicing, clippy::shadow_unrelated, clippy::option_if_let_else, clippy::semicolon_if_nothing_returned, clippy::pattern_type_mismatch, clippy::unused_trait_names, clippy::as_conversions )] // value helpers index paths directly for performance use crate::number::Number; use alloc::collections::{BTreeMap, BTreeSet}; use alloc::vec::Vec; use core::fmt; use core::ops; use core::convert::AsRef; use core::str::FromStr; use anyhow::{anyhow, bail, Result}; use serde::de::{self, Deserializer, Error as DeError, MapAccess, SeqAccess, Visitor}; use serde::ser::{SerializeMap, Serializer}; use serde::{Deserialize, Serialize}; use crate::*; /// A value in a Rego document. /// /// Value is similar to a [`serde_json::value::Value`], but has the following additional /// capabilities: /// - [`Value::Set`] variant to represent sets. /// - [`Value::Undefined`] variant to represent absence of value. // - [`Value::Object`] keys can be other values, not just strings. /// - [`Value::Number`] has at least 100 digits of precision for computations. /// /// Value can be efficiently cloned due to the use of reference counting. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum Value { /// JSON null. Null, /// JSON boolean. Bool(bool), /// JSON number. /// At least 100 digits of precision. Number(Number), /// JSON string. String(Rc), /// JSON array. Array(Rc>), /// A set of values. /// No JSON equivalent. /// Sets are serialized as arrays in JSON. Set(Rc>), /// An object. /// Unlike JSON, keys can be any value, not just string. Object(Rc>), /// Undefined value. /// Used to indicate the absence of a value. Undefined, } #[inline] fn enforce_limit_anyhow() -> Result<()> { crate::utils::limits::check_memory_limit_if_needed().map_err(|err| anyhow!(err)) } #[inline] fn enforce_limit_for() -> core::result::Result<(), E> { crate::utils::limits::check_memory_limit_if_needed().map_err(|err| E::custom(err.to_string())) } #[doc(hidden)] impl Serialize for Value { fn serialize(&self, serializer: S) -> Result where S: Serializer, { use serde::ser::Error; match self { Value::Null => serializer.serialize_unit(), Value::Bool(b) => serializer.serialize_bool(*b), Value::String(s) => serializer.serialize_str(s.as_ref()), Value::Number(n) => n.serialize(serializer), Value::Array(a) => a.serialize(serializer), Value::Object(fields) => { let mut map = serializer.serialize_map(Some(fields.len()))?; for (k, v) in fields.iter() { match k { Value::String(_) => map.serialize_entry(k, v)?, _ => { let key_str = serde_json::to_string(k).map_err(Error::custom)?; map.serialize_entry(&key_str, v)? } } } map.end() } // display set as an array Value::Set(s) => s.serialize(serializer), // display undefined as a special string Value::Undefined => serializer.serialize_str(""), } } } struct ValueVisitor; impl<'de> Visitor<'de> for ValueVisitor { type Value = Value; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a value") } fn visit_unit(self) -> Result where E: de::Error, { Ok(Value::Null) } fn visit_bool(self, v: bool) -> Result where E: de::Error, { Ok(Value::Bool(v)) } fn visit_none(self) -> Result where E: de::Error, { Ok(Value::Null) } fn visit_some(self, deserializer: D) -> Result where D: Deserializer<'de>, { Value::deserialize(deserializer) } fn visit_u64(self, v: u64) -> Result where E: de::Error, { Ok(Value::from(v)) } fn visit_i64(self, v: i64) -> Result where E: de::Error, { Ok(Value::from(v)) } fn visit_u128(self, v: u128) -> Result where E: de::Error, { Ok(Value::from(v)) } fn visit_i128(self, v: i128) -> Result where E: de::Error, { Ok(Value::from(v)) } fn visit_f64(self, v: f64) -> Result where E: de::Error, { Ok(Value::from(Number::from(v))) } fn visit_str(self, s: &str) -> Result where E: de::Error, { Ok(Value::String(s.to_string().into())) } fn visit_string(self, s: String) -> Result where E: de::Error, { Ok(Value::String(s.into())) } fn visit_seq(self, mut visitor: V) -> Result where V: SeqAccess<'de>, { let mut arr = vec![]; while let Some(v) = visitor.next_element()? { arr.push(v); // Enforce allocator limit while expanding a deserialized array. enforce_limit_for::()?; } Ok(Value::from(arr)) } fn visit_map(self, mut visitor: V) -> Result where V: MapAccess<'de>, { if let Some((key, value)) = visitor.next_entry()? { if let (Value::String(k), Value::String(v)) = (&key, &value) { if k.as_ref() == "$serde_json::private::Number" { match Number::from_str(v) { Ok(n) => return Ok(Value::from(n)), _ => return Err(de::Error::custom("failed to read big number")), } } } let mut map = BTreeMap::new(); map.insert(key, value); // Enforce allocator limit while expanding a deserialized object. enforce_limit_for::()?; while let Some((key, value)) = visitor.next_entry()? { map.insert(key, value); // Enforce allocator limit while expanding a deserialized object. enforce_limit_for::()?; } Ok(Value::from(map)) } else { Ok(Value::new_object()) } } } #[doc(hidden)] impl<'de> Deserialize<'de> for Value { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_any(ValueVisitor) } } impl fmt::Display for Value { /// Display a value. /// /// A value is displayed by serializing it to JSON using serde_json::to_string. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from("hello"); /// assert_eq!(format!("{v}"), "\"hello\""); /// # Ok(()) /// # } /// ``` fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match serde_json::to_string(self) { Ok(s) => write!(f, "{s}"), Err(_e) => Err(fmt::Error), } } } impl Value { /// Create an empty [`Value::Array`] /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let obj = Value::new_array(); /// assert_eq!(obj.as_array().expect("not an array").len(), 0); /// # Ok(()) /// # } /// ``` pub fn new_array() -> Value { Value::from(vec![]) } /// Create an empty [`Value::Object`] /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let obj = Value::new_object(); /// assert_eq!(obj.as_object().expect("not an object").len(), 0); /// # Ok(()) /// # } /// ``` pub fn new_object() -> Value { Value::from(BTreeMap::new()) } /// Create an empty [`Value::Set`] /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let obj = Value::new_set(); /// assert_eq!(obj.as_set().expect("not a set").len(), 0); /// # Ok(()) /// # } /// ``` pub fn new_set() -> Value { Value::from(BTreeSet::new()) } } impl Value { /// Deserialize a [`Value`] from JSON. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let json = r#" /// [ /// null, true, false, /// "hello", 12345, /// { "name" : "regorus" } /// ]"#; /// /// // Deserialize json. /// let value = Value::from_json_str(json)?; /// /// // Assert outer array. /// let array = value.as_array().expect("not an array"); /// /// // Assert elements. /// assert_eq!(array[0], Value::Null); /// assert_eq!(array[1], Value::from(true)); /// assert_eq!(array[2], Value::from(false)); /// assert_eq!(array[3], Value::from("hello")); /// assert_eq!(array[4], Value::from(12345u64)); /// let obj = array[5].as_object().expect("not an object"); /// assert_eq!(obj.len(), 1); /// assert_eq!(obj[&Value::from("name")], Value::from("regorus")); /// # Ok(()) /// # } /// ``` pub fn from_json_str(json: &str) -> Result { match serde_json::from_str::(json) { Ok(value) => Ok(value), Err(err) => { #[cfg(all(feature = "allocator-memory-limits", not(miri)))] { // Re-validate allocator limits when serde parsing fails to surface LimitError. match crate::utils::limits::check_global_memory_limit() { Err(limit_err) => Err(anyhow!(limit_err)), Ok(_) => Err(anyhow!(err)), } } #[cfg(any(miri, not(feature = "allocator-memory-limits")))] { Err(anyhow!(err)) } } } } /// Deserialize a [`Value`] from a file containing JSON. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let value = Value::from_json_file("tests/aci/input.json")?; /// /// // Convert the value back to json. /// let json_str = value.to_json_str()?; /// /// assert_eq!(json_str.trim(), /// std::fs::read_to_string("tests/aci/input.json")?.trim().replace("\r\n", "\n")); /// # Ok(()) /// # } /// ``` #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn from_json_file>(path: P) -> Result { match std::fs::read_to_string(&path) { Ok(c) => Self::from_json_str(c.as_str()), Err(e) => bail!("Failed to read {}. {e}", path.as_ref().display()), } } /// Serialize a value to JSON. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let value = Value::from_json_file("tests/aci/input.json")?; /// /// // Convert the value back to json. /// let json_str = value.to_json_str()?; /// /// assert_eq!(json_str.trim(), /// std::fs::read_to_string("tests/aci/input.json")?.trim().replace("\r\n", "\n")); /// # Ok(()) /// # } /// ``` /// /// Sets are serialized as arrays. /// ``` /// # use regorus::*; /// # use std::collections::BTreeSet; /// # fn main() -> anyhow::Result<()> { /// let mut set = BTreeSet::new(); /// set.insert(Value::from("Hello")); /// set.insert(Value::from(1u64)); /// /// let set_value = Value::from(set); /// /// assert_eq!( /// set_value.to_json_str()?, /// r#" ///[ /// 1, /// "Hello" ///]"#.trim()); /// # Ok(()) /// # } /// ``` /// /// Non string keys of objects are serialized to json first and the serialized string representation /// is emitted as the key. /// ``` /// # use regorus::*; /// # use std::collections::BTreeMap; /// # fn main() -> anyhow::Result<()> { /// let mut obj = BTreeMap::new(); /// obj.insert(Value::from("Hello"), Value::from("World")); /// obj.insert(Value::from([Value::from(1u64)].to_vec()), Value::Null); /// /// let obj_value = Value::from(obj); /// /// assert_eq!( /// obj_value.to_json_str()?, /// r#" ///{ /// "Hello": "World", /// "[1]": null ///}"#.trim()); /// # Ok(()) /// # } /// ``` pub fn to_json_str(&self) -> Result { serde_json::to_string_pretty(self).map_err(anyhow::Error::msg) } /// Deserialize a value from YAML. /// Note: Deserialization from YAML does not support arbitrary precision numbers. #[cfg(feature = "yaml")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn from_yaml_str(yaml: &str) -> Result { let value = serde_yaml::from_str(yaml) .map_err(|err| anyhow::anyhow!("Failed to parse YAML: {}", err))?; Ok(value) } /// Deserialize a value from a file containing YAML. /// Note: Deserialization from YAML does not support arbitrary precision numbers. #[cfg(feature = "std")] #[cfg(feature = "yaml")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[cfg_attr(docsrs, doc(cfg(feature = "yaml")))] pub fn from_yaml_file(path: &String) -> Result { match std::fs::read_to_string(path) { Ok(c) => Self::from_yaml_str(c.as_str()), Err(e) => bail!("Failed to read {path}. {e}"), } } } impl From for Value { /// Create a [`Value::Bool`] from `bool`. /// ``` /// # use regorus::*; /// # use std::collections::BTreeSet; /// # fn main() -> anyhow::Result<()> { /// assert_eq!(Value::from(true), Value::Bool(true)); /// # Ok(()) /// # } fn from(b: bool) -> Self { Value::Bool(b) } } impl From for Value { /// Create a [`Value::String`] from `string`. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// assert_eq!(Value::from("Hello".to_string()), Value::String("Hello".into())); /// # Ok(()) /// # } fn from(s: String) -> Self { Value::String(s.into()) } } impl From<&str> for Value { /// Create a [`Value::String`] from `&str`. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// assert_eq!(Value::from("Hello"), Value::String("Hello".into())); /// # Ok(()) /// # } fn from(s: &str) -> Self { Value::String(s.into()) } } impl From for Value { /// Create a [`Value::Number`] from `u128`. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// assert_eq!( /// Value::from(340_282_366_920_938_463_463_374_607_431_768_211_455u128).as_u128()?, /// 340_282_366_920_938_463_463_374_607_431_768_211_455u128); /// # Ok(()) /// # } fn from(n: u128) -> Self { Value::Number(Number::from(n)) } } impl From for Value { /// Create a [`Value::Number`] from `i128`. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// assert_eq!( /// Value::from(-170141183460469231731687303715884105728i128).as_i128()?, /// -170141183460469231731687303715884105728i128); /// # Ok(()) /// # } fn from(n: i128) -> Self { Value::Number(Number::from(n)) } } impl From for Value { /// Create a [`Value::Number`] from `u64`. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// assert_eq!( /// Value::from(0u64), /// Value::from_json_str("0")?); /// # Ok(()) /// # } fn from(n: u64) -> Self { Value::Number(Number::from(n)) } } impl From for Value { /// Create a [`Value::Number`] from `i64`. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// assert_eq!( /// Value::from(0i64), /// Value::from_json_str("0")?); /// # Ok(()) /// # } fn from(n: i64) -> Self { Value::Number(Number::from(n)) } } impl From for Value { /// Create a [`Value::Number`] from `u32`. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// assert_eq!( /// Value::from(0u32), /// Value::from_json_str("0")?); /// # Ok(()) /// # } fn from(n: u32) -> Self { Value::Number(Number::from(n as u64)) } } impl From for Value { /// Create a [`Value::Number`] from `i32`. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// assert_eq!( /// Value::from(0i32), /// Value::from_json_str("0")?); /// # Ok(()) /// # } fn from(n: i32) -> Self { Value::Number(Number::from(n as i64)) } } impl From for Value { /// Create a [`Value::Number`] from `f64`. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// assert_eq!( /// Value::from(3.5f64), /// Value::from_numeric_string("3.5")?); /// # Ok(()) /// # } /// ``` /// /// [`Value::Number`] stores floating-point values as `f64`, so it inherits the same /// ~15-digit precision limit. Adding additional digits to either the literal or a parsed /// numeric string causes both to round to the same `f64` value. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let from_float = Value::from(3.141592653589793238462f64); /// let from_string = Value::from_numeric_string("3.141592653589793238462")?; /// assert_eq!(from_float, from_string); /// /// // All representations round to approximately 15 digits. /// assert_eq!( /// from_float, /// Value::from_numeric_string("3.141592653589793")?); /// # Ok(()) /// # } /// ``` /// /// If additional precision is required, keep the raw data as strings or use an external /// arbitrary-precision numeric type before converting it into [`Value`]. fn from(n: f64) -> Self { Value::Number(Number::from(n)) } } impl From for Value { /// Create a [`Value`] from [`serde_json::Value`]. /// /// Returns [`Value::Undefined`] in case of error. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let json_v = serde_json::json!({ "x":10, "y": 20 }); /// let v = Value::from(json_v); /// /// assert_eq!(v["x"].as_u64()?, 10); /// assert_eq!(v["y"].as_u64()?, 20); /// # Ok(()) /// # } fn from(v: serde_json::Value) -> Self { match serde_json::from_value(v) { Ok(v) => v, _ => Value::Undefined, } } } #[cfg(feature = "yaml")] #[cfg_attr(docsrs, doc(cfg(feature = "yaml")))] impl From for Value { /// Create a [`Value`] from [`serde_yaml::Value`]. /// /// Returns [`Value::Undefined`] in case of error. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let yaml = " /// x: 10 /// y: 20 /// "; /// let yaml_v : serde_yaml::Value = serde_yaml::from_str(&yaml).unwrap(); /// let v = Value::from(yaml_v); /// /// assert_eq!(v["x"].as_u64()?, 10); /// assert_eq!(v["y"].as_u64()?, 20); /// # Ok(()) /// # } fn from(v: serde_yaml::Value) -> Self { match serde_yaml::from_value(v) { Ok(v) => v, _ => Value::Undefined, } } } impl Value { /// Create a [`Value::Number`] from a string containing numeric representation of a number. /// /// This is the preferred way for creating arbitrary precision numbers. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from_numeric_string("3.14159265358979323846264338327950288419716939937510")?; /// /// println!("{}", v.to_json_str()?); /// // Prints 3.1415926535897932384626433832795028841971693993751 if serde_json/arbitrary_precision feature is enabled. /// // Prints 3.141592653589793 if serde_json/arbitrary_precision is not enabled. /// # Ok(()) /// # } /// ``` pub fn from_numeric_string(s: &str) -> Result { Ok(Value::Number( Number::from_str(s).map_err(|_| anyhow!("not a valid numeric string"))?, )) } } impl From for Value { /// Create a [`Value::Number`] from `usize`. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// assert_eq!( /// Value::from(0usize), /// Value::from_json_str("0")?); /// # Ok(()) /// # } fn from(n: usize) -> Self { Value::Number(Number::from(n)) } } #[doc(hidden)] impl From for Value { fn from(n: Number) -> Self { Value::Number(n) } } impl From> for Value { /// Create a [`Value::Array`] from a [`Vec`]. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let strings = [ "Hello", "World" ]; /// /// let v = Value::from(strings.iter().map(|s| Value::from(*s)).collect::>()); /// assert_eq!(v[0], Value::from(strings[0])); /// assert_eq!(v[1], Value::from(strings[1])); /// # Ok(()) /// # } fn from(a: Vec) -> Self { Value::Array(Rc::new(a)) } } impl From> for Value { /// Create a [`Value::Set`] from a [`BTreeSet`]. /// ``` /// # use regorus::*; /// # use std::collections::BTreeSet; /// # fn main() -> anyhow::Result<()> { /// let strings = [ "Hello", "World" ]; /// let v = Value::from(strings /// .iter() /// .map(|s| Value::from(*s)) /// .collect::>()); /// /// let mut iter = v.as_set()?.iter(); /// assert_eq!(iter.next(), Some(&Value::from(strings[0]))); /// assert_eq!(iter.next(), Some(&Value::from(strings[1]))); /// # Ok(()) /// # } fn from(s: BTreeSet) -> Self { Value::Set(Rc::new(s)) } } impl From> for Value { /// Create a [`Value::Object`] from a [`BTreeMap`]. /// ``` /// # use regorus::*; /// # use std::collections::BTreeMap; /// # fn main() -> anyhow::Result<()> { /// let strings = [ ("Hello", "World") ]; /// let v = Value::from(strings /// .iter() /// .map(|(k,v)| (Value::from(*k), Value::from(*v))) /// .collect::>()); /// /// let mut iter = v.as_object()?.iter(); /// assert_eq!(iter.next(), Some((&Value::from(strings[0].0), &Value::from(strings[0].1)))); /// # Ok(()) /// # } fn from(s: BTreeMap) -> Self { Value::Object(Rc::new(s)) } } impl Value { pub(crate) fn from_array(a: Vec) -> Value { Value::from(a) } pub(crate) fn from_set(s: BTreeSet) -> Value { Value::from(s) } pub(crate) fn from_map(m: BTreeMap) -> Value { Value::from(m) } pub(crate) fn is_empty_object(&self) -> bool { self == &Value::new_object() } } impl Value { /// Cast value to [`& bool`] if [`Value::Bool`]. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from(true); /// assert_eq!(v.as_bool()?, &true); /// # Ok(()) /// # } pub fn as_bool(&self) -> Result<&bool> { match self { Value::Bool(b) => Ok(b), _ => Err(anyhow!("not a bool")), } } /// Cast value to [`&mut bool`] if [`Value::Bool`]. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let mut v = Value::from(true); /// *v.as_bool_mut()? = false; /// # Ok(()) /// # } pub fn as_bool_mut(&mut self) -> Result<&mut bool> { match self { Value::Bool(b) => Ok(b), _ => Err(anyhow!("not a bool")), } } /// Cast value to [`& u128`] if [`Value::Number`]. /// /// Error is raised if the value is not a number or if the numeric value /// does not fit in a u128. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from(10); /// assert_eq!(v.as_u128()?, 10u128); /// /// let v = Value::from(-10); /// assert!(v.as_u128().is_err()); /// # Ok(()) /// # } pub fn as_u128(&self) -> Result { match self { Value::Number(b) => { if let Some(n) = b.as_u128() { return Ok(n); } bail!("not a u128"); } _ => Err(anyhow!("not a u128")), } } /// Cast value to [`& i128`] if [`Value::Number`]. /// /// Error is raised if the value is not a number or if the numeric value /// does not fit in a i128. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from(-10); /// assert_eq!(v.as_i128()?, -10i128); /// /// let v = Value::from_numeric_string("11111111111111111111111111111111111111111111111111")?; /// assert!(v.as_i128().is_err()); /// # Ok(()) /// # } pub fn as_i128(&self) -> Result { match self { Value::Number(b) => { if let Some(n) = b.as_i128() { return Ok(n); } bail!("not a i128"); } _ => Err(anyhow!("not a i128")), } } /// Cast value to [`& u64`] if [`Value::Number`]. /// /// Error is raised if the value is not a number or if the numeric value /// does not fit in a u64. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from(10); /// assert_eq!(v.as_u64()?, 10u64); /// /// let v = Value::from(-10); /// assert!(v.as_u64().is_err()); /// # Ok(()) /// # } pub fn as_u64(&self) -> Result { match self { Value::Number(b) => { if let Some(n) = b.as_u64() { return Ok(n); } bail!("not a u64"); } _ => Err(anyhow!("not a u64")), } } /// Cast value to [`& i64`] if [`Value::Number`]. /// /// Error is raised if the value is not a number or if the numeric value /// does not fit in a i64. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from(-10); /// assert_eq!(v.as_i64()?, -10i64); /// /// let v = Value::from(340_282_366_920_938_463_463_374_607_431_768_211_455u128); /// assert!(v.as_i64().is_err()); /// # Ok(()) /// # } pub fn as_i64(&self) -> Result { match self { Value::Number(b) => { if let Some(n) = b.as_i64() { return Ok(n); } bail!("not an i64"); } _ => Err(anyhow!("not an i64")), } } /// Cast value to [`& u32`] if [`Value::Number`]. /// /// Error is raised if the value is not a number or if the numeric value /// does not fit in a u32. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from(10); /// assert_eq!(v.as_u32()?, 10u32); /// /// let v = Value::from(-10); /// assert!(v.as_u32().is_err()); /// # Ok(()) /// # } pub fn as_u32(&self) -> Result { match self { Value::Number(b) => { if let Some(n) = b.as_u64() { if let Ok(v) = u32::try_from(n) { return Ok(v); } } bail!("not a u32"); } _ => Err(anyhow!("not a u32")), } } /// Cast value to [`& i32`] if [`Value::Number`]. /// /// Error is raised if the value is not a number or if the numeric value /// does not fit in a i32. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from(-10); /// assert_eq!(v.as_i32()?, -10i32); /// /// let v = Value::from(2_147_483_648i64); /// assert!(v.as_i32().is_err()); /// # Ok(()) /// # } pub fn as_i32(&self) -> Result { match self { Value::Number(b) => { if let Some(n) = b.as_i64() { if let Ok(v) = i32::try_from(n) { return Ok(v); } } bail!("not an i32"); } _ => Err(anyhow!("not an i32")), } } /// Cast value to [`& u16`] if [`Value::Number`]. /// /// Error is raised if the value is not a number or if the numeric value /// does not fit in a u16. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from(10); /// assert_eq!(v.as_u16()?, 10u16); /// /// let v = Value::from(-10); /// assert!(v.as_u16().is_err()); /// # Ok(()) /// # } pub fn as_u16(&self) -> Result { match self { Value::Number(b) => { if let Some(n) = b.as_u64() { if let Ok(v) = u16::try_from(n) { return Ok(v); } } bail!("not a u16"); } _ => Err(anyhow!("not a u16")), } } /// Cast value to [`& i16`] if [`Value::Number`]. /// /// Error is raised if the value is not a number or if the numeric value /// does not fit in a i16. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from(-10); /// assert_eq!(v.as_i16()?, -10i16); /// /// let v = Value::from(32768i64); /// assert!(v.as_i16().is_err()); /// # Ok(()) /// # } pub fn as_i16(&self) -> Result { match self { Value::Number(b) => { if let Some(n) = b.as_i64() { if let Ok(v) = i16::try_from(n) { return Ok(v); } } bail!("not an i16"); } _ => Err(anyhow!("not an i16")), } } /// Cast value to [`& u8`] if [`Value::Number`]. /// /// Error is raised if the value is not a number or if the numeric value /// does not fit in a u8. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from(10); /// assert_eq!(v.as_u8()?, 10u8); /// /// let v = Value::from(-10); /// assert!(v.as_u8().is_err()); /// # Ok(()) /// # } pub fn as_u8(&self) -> Result { match self { Value::Number(b) => { if let Some(n) = b.as_u64() { if let Ok(v) = u8::try_from(n) { return Ok(v); } } bail!("not a u8"); } _ => Err(anyhow!("not a u8")), } } /// Cast value to [`& i8`] if [`Value::Number`]. /// /// Error is raised if the value is not a number or if the numeric value /// does not fit in a i8. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from(-10); /// assert_eq!(v.as_i8()?, -10i8); /// /// let v = Value::from(128); /// assert!(v.as_i8().is_err()); /// # Ok(()) /// # } pub fn as_i8(&self) -> Result { match self { Value::Number(b) => { if let Some(n) = b.as_i64() { if let Ok(v) = i8::try_from(n) { return Ok(v); } } bail!("not an i8"); } _ => Err(anyhow!("not an i8")), } } /// Cast value to [`& f64`] if [`Value::Number`]. /// Error is raised if the value is not a number or if the numeric value /// does not fit in a i64. /// /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from(-10); /// assert_eq!(v.as_f64()?, -10f64); /// /// let v = Value::from(340_282_366_920_938_463_463_374_607_431_768_211_455u128); /// assert!(v.as_i64().is_err()); /// # Ok(()) /// # } pub fn as_f64(&self) -> Result { match self { Value::Number(b) => { if let Some(n) = b.as_f64() { return Ok(n); } bail!("not a f64"); } _ => Err(anyhow!("not a f64")), } } /// Cast value to [`& Rc`] if [`Value::String`]. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from("Hello"); /// assert_eq!(v.as_string()?.as_ref(), "Hello"); /// # Ok(()) /// # } pub fn as_string(&self) -> Result<&Rc> { match self { Value::String(s) => Ok(s), _ => Err(anyhow!("not a string")), } } /// Cast value to [`&mut Rc`] if [`Value::String`]. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let mut v = Value::from("Hello"); /// *v.as_string_mut()? = "World".into(); /// # Ok(()) /// # } pub fn as_string_mut(&mut self) -> Result<&mut Rc> { match self { Value::String(s) => Ok(s), _ => Err(anyhow!("not a string")), } } #[doc(hidden)] pub fn as_number(&self) -> Result<&Number> { match self { Value::Number(n) => Ok(n), _ => Err(anyhow!("not a number")), } } #[doc(hidden)] pub fn as_number_mut(&mut self) -> Result<&mut Number> { match self { Value::Number(n) => Ok(n), _ => Err(anyhow!("not a number")), } } /// Cast value to [`& Vec`] if [`Value::Array`]. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from([Value::from("Hello")].to_vec()); /// assert_eq!(v.as_array()?[0], Value::from("Hello")); /// # Ok(()) /// # } pub fn as_array(&self) -> Result<&Vec> { match self { Value::Array(a) => Ok(a), _ => Err(anyhow!("not an array")), } } /// Cast value to [`&mut Vec`] if [`Value::Array`]. /// ``` /// # use regorus::*; /// # fn main() -> anyhow::Result<()> { /// let mut v = Value::from([Value::from("Hello")].to_vec()); /// v.as_array_mut()?.push(Value::from("World")); /// # Ok(()) /// # } pub fn as_array_mut(&mut self) -> Result<&mut Vec> { match self { Value::Array(a) => Ok(Rc::make_mut(a)), _ => Err(anyhow!("not an array")), } } /// Cast value to [`& BTreeSet`] if [`Value::Set`]. /// ``` /// # use regorus::*; /// # use std::collections::BTreeSet; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from( /// [Value::from("Hello")] /// .iter() /// .cloned() /// .collect::>(), /// ); /// assert_eq!(v.as_set()?.first(), Some(&Value::from("Hello"))); /// # Ok(()) /// # } pub fn as_set(&self) -> Result<&BTreeSet> { match self { Value::Set(s) => Ok(s), _ => Err(anyhow!("not a set")), } } /// Cast value to [`&mut BTreeSet`] if [`Value::Set`]. /// ``` /// # use regorus::*; /// # use std::collections::BTreeSet; /// # fn main() -> anyhow::Result<()> { /// let mut v = Value::from( /// [Value::from("Hello")] /// .iter() /// .cloned() /// .collect::>(), /// ); /// v.as_set_mut()?.insert(Value::from("World")); /// # Ok(()) /// # } pub fn as_set_mut(&mut self) -> Result<&mut BTreeSet> { match self { Value::Set(s) => Ok(Rc::make_mut(s)), _ => Err(anyhow!("not a set")), } } /// Cast value to [`& BTreeMap`] if [`Value::Object`]. /// ``` /// # use regorus::*; /// # use std::collections::BTreeMap; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from( /// [(Value::from("Hello"), Value::from("World"))] /// .iter() /// .cloned() /// .collect::>(), /// ); /// assert_eq!( /// v.as_object()?.iter().next(), /// Some((&Value::from("Hello"), &Value::from("World"))), /// ); /// # Ok(()) /// # } pub fn as_object(&self) -> Result<&BTreeMap> { match self { Value::Object(m) => Ok(m), _ => Err(anyhow!("not an object")), } } /// Cast value to [`&mut BTreeMap`] if [`Value::Object`]. /// ``` /// # use regorus::*; /// # use std::collections::BTreeMap; /// # fn main() -> anyhow::Result<()> { /// let mut v = Value::from( /// [(Value::from("Hello"), Value::from("World"))] /// .iter() /// .cloned() /// .collect::>(), /// ); /// v.as_object_mut()?.insert(Value::from("Good"), Value::from("Bye")); /// # Ok(()) /// # } pub fn as_object_mut(&mut self) -> Result<&mut BTreeMap> { match self { Value::Object(m) => Ok(Rc::make_mut(m)), _ => Err(anyhow!("not an object")), } } } impl Value { pub(crate) fn make_or_get_value_mut<'a>(&'a mut self, paths: &[&str]) -> Result<&'a mut Value> { if paths.is_empty() { return Ok(self); } let key = Value::String(paths[0].into()); if self == &Value::Undefined { *self = Value::new_object(); } if let Value::Object(map) = self { if map.get(&key).is_none() { Rc::make_mut(map).insert(key.clone(), Value::Undefined); // Enforce allocator limit while creating nested object entries. enforce_limit_anyhow()?; } } match self { Value::Object(map) => match Rc::make_mut(map).get_mut(&key) { Some(v) if paths.len() == 1 => Ok(v), Some(v) => Self::make_or_get_value_mut(v, &paths[1..]), _ => bail!("internal error: unexpected"), }, Value::Undefined if paths.len() > 1 => { *self = Value::new_object(); Self::make_or_get_value_mut(self, paths) } Value::Undefined => Ok(self), _ => bail!("internal error: make: not an selfect {self:?}"), } } pub(crate) fn merge(&mut self, mut new: Value) -> Result<()> { if self == &new { return Ok(()); } match (self, &mut new) { (v @ Value::Undefined, _) => *v = new, (Value::Set(ref mut set), Value::Set(new)) => { Rc::make_mut(set).append(Rc::make_mut(new)); // Enforce allocator limit after merging set entries. enforce_limit_anyhow()?; } (Value::Object(map), Value::Object(new)) => { for (k, v) in new.iter() { match map.get(k) { Some(pv) if *pv != *v => { bail!( "value for key `{}` generated multiple times: `{}` and `{}`", serde_json::to_string_pretty(&k).map_err(anyhow::Error::msg)?, serde_json::to_string_pretty(&pv).map_err(anyhow::Error::msg)?, serde_json::to_string_pretty(&v).map_err(anyhow::Error::msg)?, ) } _ => { Rc::make_mut(map).insert(k.clone(), v.clone()); // Enforce allocator limit after merging object entries. enforce_limit_anyhow()?; } }; } } _ => bail!("error: could not merge value"), }; Ok(()) } } impl ops::Index<&Value> for Value { type Output = Value; /// Index a [`Value`] using a [`Value`]. /// /// [`Value::Undefined`] is returned /// - If the index not valid for the collection. /// - If the value being indexed is not an array, set or object. /// /// Sets can be indexed only by elements within the set. /// /// ``` /// # use regorus::*; /// # use std::collections::BTreeMap; /// # fn main() -> anyhow::Result<()> { /// /// let arr = Value::from([Value::from("Hello")].to_vec()); /// // Index an array. /// assert_eq!(arr[&Value::from(0)].as_string()?.as_ref(), "Hello"); /// assert_eq!(arr[&Value::from(10)], Value::Undefined); /// /// let mut set = Value::new_set(); /// set.as_set_mut()?.insert(Value::from(100)); /// set.as_set_mut()?.insert(Value::from("Hello")); /// /// // Index a set. /// let item = Value::from("Hello"); /// assert_eq!(&set[&item], &item); /// assert_eq!(&set[&Value::from(10)], &Value::Undefined); /// /// let mut obj = Value::new_object(); /// obj.as_object_mut()?.insert(Value::from("Hello"), Value::from("World")); /// obj.as_object_mut()?.insert(Value::new_array(), Value::from("bye")); /// /// // Index an object. /// assert_eq!(&obj[Value::from("Hello")].as_string()?.as_ref(), &"World"); /// assert_eq!(&obj[Value::from("hllo")], &Value::Undefined); /// // Index using non-string key. /// assert_eq!(&obj[&Value::new_array()].as_string()?.as_ref(), &"bye"); /// /// // Index a non-collection. /// assert_eq!(&Value::Null[&Value::from(1)], &Value::Undefined); /// # Ok(()) /// # } /// ``` /// /// This is the preferred way of indexing a value. /// Since constructing a value may be a costly operation (e.g. Value::String), /// the caller can construct the index value once and use it many times. ///` fn index(&self, key: &Value) -> &Self::Output { match (self, key) { (Value::Object(o), _) => match &o.get(key) { Some(v) => v, _ => &Value::Undefined, }, (Value::Set(s), _) => match s.get(key) { Some(v) => v, _ => &Value::Undefined, }, (Value::Array(a), Value::Number(n)) => match n.as_u64() { Some(index) if (index as usize) < a.len() => &a[index as usize], _ => &Value::Undefined, }, _ => &Value::Undefined, } } } impl ops::Index for Value where Value: From, { type Output = Value; /// Index a [`Value`]. /// /// /// A [`Value`] is constructed from the index which is then used for indexing. /// /// ``` /// # use regorus::*; /// # use std::collections::BTreeMap; /// # fn main() -> anyhow::Result<()> { /// let v = Value::from( /// [(Value::from("Hello"), Value::from("World")), /// (Value::from(1), Value::from(2))] /// .iter() /// .cloned() /// .collect::>(), /// ); /// /// assert_eq!(&v["Hello"].as_string()?.as_ref(), &"World"); /// assert_eq!(&v[1].as_u64()?, &2u64); /// # Ok(()) /// # } fn index(&self, key: T) -> &Self::Output { &self[&Value::from(key)] } }