Files
regorus/src/value/mod.rs
Anand Krishnamoorthi ed6ae465b0 refactor(value): migrate Value::Object to Object storage abstraction (#736)
Builds on #57. Swap Value::Object's payload from Rc<BTreeMap<Value, Value>>
to Rc<Object> and migrate all call sites to the Object API.

as_object / as_object_mut keep their names but return &Object / &mut Object.
The mutable accessor handles Rc::make_mut internally, so callers no longer
do it themselves. Object grows into_value() and From<Object> for Value.
Value's serializer now delegates to Object::serialize, dropping a duplicate
non-string-key stringification path.

RVM IterationState::Object is rewritten around ObjectCursor: O(log n)
steps over a shared Rc<Object>, no eager pair snapshot. Snapshot
independence is preserved by Rc copy-on-write; setup_next_iteration
advances the cursor inline and advance() becomes a no-op for this variant.
A new iteration_state_object_is_snapshot_independent_of_source test
covers CoW against a mutated alias.

Value::Set still wraps Rc<BTreeSet<Value>>; the matching Set abstraction
and its swap ship in follow-up PRs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 18:04:37 -05:00

1498 lines
43 KiB
Rust

// 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
mod object;
#[cfg(test)]
mod tests;
#[allow(unused_imports)] // surface for downstream PRs
pub use object::{IntoIter, Iter, IterMut, Object};
#[cfg(feature = "rvm")]
#[allow(unused_imports)] // surface for downstream PRs
pub use object::ObjectCursor;
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::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<str>),
/// JSON array.
Array(Rc<Vec<Value>>),
/// A set of values.
/// No JSON equivalent.
/// Sets are serialized as arrays in JSON.
Set(Rc<BTreeSet<Value>>),
/// An object.
/// Unlike JSON, keys can be any value, not just string.
Object(Rc<Object>),
/// 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<E: DeError>() -> 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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
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),
// Delegate to the Object/Set serializers — single canonical path,
// handles non-string-key stringification internally.
Value::Object(fields) => fields.serialize(serializer),
// display set as an array
Value::Set(s) => s.serialize(serializer),
// display undefined as a special string
Value::Undefined => serializer.serialize_str("<undefined>"),
}
}
}
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<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::Null)
}
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::Bool(v))
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::Null)
}
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
Value::deserialize(deserializer)
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::from(v))
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::from(v))
}
fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::from(v))
}
fn visit_i128<E>(self, v: i128) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::from(v))
}
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::from(Number::from(v)))
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::String(s.to_string().into()))
}
fn visit_string<E>(self, s: String) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Value::String(s.into()))
}
fn visit_seq<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
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::<V::Error>()?;
}
Ok(Value::from(arr))
}
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
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::<V::Error>()?;
while let Some((key, value)) = visitor.next_entry()? {
map.insert(key, value);
// Enforce allocator limit while expanding a deserialized object.
enforce_limit_for::<V::Error>()?;
}
Ok(Value::from(map))
} else {
Ok(Value::new_object())
}
}
}
#[doc(hidden)]
impl<'de> Deserialize<'de> for Value {
fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
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.get(&Value::from("name")).expect("missing name"), &Value::from("regorus"));
/// # Ok(())
/// # }
/// ```
pub fn from_json_str(json: &str) -> Result<Value> {
match serde_json::from_str::<Value>(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<P: AsRef<std::path::Path>>(path: P) -> Result<Value> {
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<String> {
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<Value> {
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<Value> {
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<bool> 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<String> 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<u128> 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<i128> 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<u64> 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<i64> 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<u32> 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<i32> 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<f64> 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<serde_json::Value> 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<serde_yaml::Value> 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<Value> {
Ok(Value::Number(
Number::from_str(s).map_err(|_| anyhow!("not a valid numeric string"))?,
))
}
}
impl From<usize> 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<Number> for Value {
fn from(n: Number) -> Self {
Value::Number(n)
}
}
impl From<Vec<Value>> for Value {
/// Create a [`Value::Array`] from a [`Vec<Value>`].
/// ```
/// # use regorus::*;
/// # fn main() -> anyhow::Result<()> {
/// let strings = [ "Hello", "World" ];
///
/// let v = Value::from(strings.iter().map(|s| Value::from(*s)).collect::<Vec<Value>>());
/// assert_eq!(v[0], Value::from(strings[0]));
/// assert_eq!(v[1], Value::from(strings[1]));
/// # Ok(())
/// # }
fn from(a: Vec<Value>) -> Self {
Value::Array(Rc::new(a))
}
}
impl From<BTreeSet<Value>> for Value {
/// Create a [`Value::Set`] from a [`BTreeSet<Value>`].
/// ```
/// # 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::<BTreeSet<Value>>());
///
/// 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<Value>) -> Self {
Value::Set(Rc::new(s))
}
}
impl From<BTreeMap<Value, Value>> for Value {
/// Create a [`Value::Object`] from a [`BTreeMap<Value>`].
/// ```
/// # 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::<BTreeMap<Value, Value>>());
///
/// 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<Value, Value>) -> Self {
Value::Object(Rc::new(Object::from(s)))
}
}
impl Value {
pub(crate) fn from_array(a: Vec<Value>) -> Value {
Value::from(a)
}
pub(crate) fn from_set(s: BTreeSet<Value>) -> Value {
Value::from(s)
}
pub(crate) fn from_map(m: BTreeMap<Value, Value>) -> 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<u128> {
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<i128> {
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<u64> {
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<i64> {
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<u32> {
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<i32> {
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<u16> {
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<i16> {
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<u8> {
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<i8> {
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<f64> {
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<str>`] 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<str>> {
match self {
Value::String(s) => Ok(s),
_ => Err(anyhow!("not a string")),
}
}
/// Cast value to [`&mut Rc<str>`] 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<str>> {
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<Value>`] 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<Value>> {
match self {
Value::Array(a) => Ok(a),
_ => Err(anyhow!("not an array")),
}
}
/// Cast value to [`&mut Vec<Value>`] 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<Value>> {
match self {
Value::Array(a) => Ok(Rc::make_mut(a)),
_ => Err(anyhow!("not an array")),
}
}
/// Cast value to [`& BTreeSet<Value>`] if [`Value::Set`].
/// ```
/// # use regorus::*;
/// # use std::collections::BTreeSet;
/// # fn main() -> anyhow::Result<()> {
/// let v = Value::from(
/// [Value::from("Hello")]
/// .iter()
/// .cloned()
/// .collect::<BTreeSet<Value>>(),
/// );
/// assert_eq!(v.as_set()?.first(), Some(&Value::from("Hello")));
/// # Ok(())
/// # }
pub fn as_set(&self) -> Result<&BTreeSet<Value>> {
match self {
Value::Set(s) => Ok(s),
_ => Err(anyhow!("not a set")),
}
}
/// Cast value to [`&mut BTreeSet<Value>`] if [`Value::Set`].
/// ```
/// # use regorus::*;
/// # use std::collections::BTreeSet;
/// # fn main() -> anyhow::Result<()> {
/// let mut v = Value::from(
/// [Value::from("Hello")]
/// .iter()
/// .cloned()
/// .collect::<BTreeSet<Value>>(),
/// );
/// v.as_set_mut()?.insert(Value::from("World"));
/// # Ok(())
/// # }
pub fn as_set_mut(&mut self) -> Result<&mut BTreeSet<Value>> {
match self {
Value::Set(s) => Ok(Rc::make_mut(s)),
_ => Err(anyhow!("not a set")),
}
}
/// Cast value to [`&Object`] if [`Value::Object`].
/// ```
/// # use regorus::*;
/// # use regorus::value::Object;
/// # fn main() -> anyhow::Result<()> {
/// let v = Value::from(
/// [(Value::from("Hello"), Value::from("World"))]
/// .iter()
/// .cloned()
/// .collect::<Object>(),
/// );
/// assert_eq!(
/// v.as_object()?.iter().next(),
/// Some((&Value::from("Hello"), &Value::from("World"))),
/// );
/// # Ok(())
/// # }
pub fn as_object(&self) -> Result<&Object> {
match self {
Value::Object(m) => Ok(m),
_ => Err(anyhow!("not an object")),
}
}
/// Cast value to [`&mut Object`] if [`Value::Object`].
/// ```
/// # use regorus::*;
/// # use regorus::value::Object;
/// # fn main() -> anyhow::Result<()> {
/// let mut v = Value::from(
/// [(Value::from("Hello"), Value::from("World"))]
/// .iter()
/// .cloned()
/// .collect::<Object>(),
/// );
/// v.as_object_mut()?.insert(Value::from("Good"), Value::from("Bye"));
/// # Ok(())
/// # }
pub fn as_object_mut(&mut self) -> Result<&mut Object> {
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<T> ops::Index<T> for Value
where
Value: From<T>,
{
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::<BTreeMap<Value, Value>>(),
/// );
///
/// 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)]
}
}