#![cfg_attr(
feature = "deserialization",
doc = r"
```
# use minijinja::value::Value;
use serde::Deserialize;
let value = Value::from(vec![1, 2, 3]);
let vec = Vec::<i32>::deserialize(value).unwrap();
```
"
)]
use core::str;
use std::cell::{Cell, RefCell};
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex};
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
use crate::error::{Error, ErrorKind};
use crate::functions;
use crate::value::ops::as_f64;
use crate::value::serialize::transform;
use crate::vm::State;
pub use crate::value::argtypes::{from_args, ArgType, FunctionArgs, FunctionResult, Kwargs, Rest};
pub use crate::value::merge_object::merge_maps;
pub use crate::value::object::{DynObject, Enumerator, Object, ObjectExt, ObjectRepr};
#[macro_use]
mod type_erase;
mod argtypes;
#[cfg(feature = "deserialization")]
mod deserialize;
pub(crate) mod merge_object;
pub(crate) mod namespace_object;
mod object;
pub(crate) mod ops;
mod serialize;
#[cfg(feature = "deserialization")]
pub use self::deserialize::ViaDeserialize;
const VALUE_HANDLE_MARKER: &str = "\x01__minijinja_ValueHandle";
#[cfg(feature = "preserve_order")]
pub(crate) type ValueMap = indexmap::IndexMap<Value, Value>;
#[cfg(not(feature = "preserve_order"))]
pub(crate) type ValueMap = std::collections::BTreeMap<Value, Value>;
#[inline(always)]
pub(crate) fn value_map_with_capacity(capacity: usize) -> ValueMap {
#[cfg(not(feature = "preserve_order"))]
{
let _ = capacity;
ValueMap::new()
}
#[cfg(feature = "preserve_order")]
{
ValueMap::with_capacity(crate::utils::untrusted_size_hint(capacity))
}
}
pub(crate) struct ValueHandleRegistry {
single: Option<(u32, Value)>,
overflow: BTreeMap<u32, Value>,
}
impl ValueHandleRegistry {
const fn new() -> Self {
Self {
single: None,
overflow: BTreeMap::new(),
}
}
#[inline(always)]
pub(crate) fn insert(&mut self, handle: u32, value: Value) {
if self.single.is_none() && self.overflow.is_empty() {
self.single = Some((handle, value));
return;
}
if let Some((other_handle, other_value)) = self.single.take() {
self.overflow.insert(other_handle, other_value);
}
self.overflow.insert(handle, value);
}
#[inline(always)]
pub(crate) fn remove(&mut self, handle: u32) -> Option<Value> {
if let Some((single_handle, _)) = self.single {
if single_handle == handle {
return self.single.take().map(|(_, value)| value);
}
}
self.overflow.remove(&handle)
}
}
thread_local! {
static INTERNAL_SERIALIZATION: Cell<bool> = const { Cell::new(false) };
static LAST_VALUE_HANDLE: Cell<u32> = const { Cell::new(0) };
static VALUE_HANDLES: RefCell<ValueHandleRegistry> = const { RefCell::new(ValueHandleRegistry::new()) };
}
pub fn serializing_for_value() -> bool {
INTERNAL_SERIALIZATION.with(|flag| flag.get())
}
struct InternalSerializationGuard<'a> {
flag: &'a Cell<bool>,
reset_on_drop: bool,
}
impl Drop for InternalSerializationGuard<'_> {
fn drop(&mut self) {
if self.reset_on_drop {
self.flag.set(false);
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[non_exhaustive]
pub enum ValueKind {
Undefined,
None,
Bool,
Number,
String,
Bytes,
Seq,
Map,
Iterable,
Plain,
Invalid,
}
impl fmt::Display for ValueKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match *self {
ValueKind::Undefined => "undefined",
ValueKind::None => "none",
ValueKind::Bool => "bool",
ValueKind::Number => "number",
ValueKind::String => "string",
ValueKind::Bytes => "bytes",
ValueKind::Seq => "sequence",
ValueKind::Map => "map",
ValueKind::Iterable => "iterator",
ValueKind::Plain => "plain object",
ValueKind::Invalid => "invalid value",
})
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum StringType {
Normal,
Safe,
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum UndefinedType {
Default,
Silent,
}
#[derive(Copy, Debug)]
#[cfg_attr(feature = "unstable_machinery_serde", derive(serde::Serialize))]
#[repr(packed)]
pub(crate) struct Packed<T: Copy>(pub T);
impl<T: Copy> Clone for Packed<T> {
fn clone(&self) -> Self {
*self
}
}
const SMALL_STR_CAP: usize = 22;
#[derive(Clone)]
pub(crate) struct SmallStr {
len: u8,
buf: [u8; SMALL_STR_CAP],
}
impl SmallStr {
pub fn try_new(s: &str) -> Option<SmallStr> {
let len = s.len();
if len <= SMALL_STR_CAP {
let mut buf = [0u8; SMALL_STR_CAP];
buf[..len].copy_from_slice(s.as_bytes());
Some(SmallStr {
len: len as u8,
buf,
})
} else {
None
}
}
pub fn as_str(&self) -> &str {
unsafe { std::str::from_utf8_unchecked(&self.buf[..self.len as usize]) }
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
#[derive(Clone)]
pub(crate) enum ValueRepr {
None,
Undefined(UndefinedType),
Bool(bool),
U64(u64),
I64(i64),
F64(f64),
Invalid(Arc<Error>),
U128(Packed<u128>),
I128(Packed<i128>),
String(Arc<str>, StringType),
SmallStr(SmallStr),
Bytes(Arc<Vec<u8>>),
Object(DynObject),
}
impl fmt::Debug for ValueRepr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ValueRepr::Undefined(_) => f.write_str("undefined"),
ValueRepr::Bool(ref val) => fmt::Debug::fmt(val, f),
ValueRepr::U64(ref val) => fmt::Debug::fmt(val, f),
ValueRepr::I64(ref val) => fmt::Debug::fmt(val, f),
ValueRepr::F64(ref val) => fmt::Debug::fmt(val, f),
ValueRepr::None => f.write_str("none"),
ValueRepr::Invalid(ref val) => write!(f, "<invalid value: {val}>"),
ValueRepr::U128(val) => fmt::Debug::fmt(&{ val.0 }, f),
ValueRepr::I128(val) => fmt::Debug::fmt(&{ val.0 }, f),
ValueRepr::String(ref val, _) => fmt::Debug::fmt(val, f),
ValueRepr::SmallStr(ref val) => fmt::Debug::fmt(val.as_str(), f),
ValueRepr::Bytes(ref val) => {
write!(f, "b'")?;
for &b in val.iter() {
if b == b'"' {
write!(f, "\"")?
} else {
write!(f, "{}", b.escape_ascii())?;
}
}
write!(f, "'")
}
ValueRepr::Object(ref val) => val.render(f),
}
}
}
impl Hash for Value {
fn hash<H: Hasher>(&self, state: &mut H) {
match self.0 {
ValueRepr::None | ValueRepr::Undefined(_) => 0u8.hash(state),
ValueRepr::String(ref s, _) => s.hash(state),
ValueRepr::SmallStr(ref s) => s.as_str().hash(state),
ValueRepr::Bool(b) => b.hash(state),
ValueRepr::Invalid(ref e) => (e.kind(), e.detail()).hash(state),
ValueRepr::Bytes(ref b) => b.hash(state),
ValueRepr::Object(ref d) => d.hash(state),
ValueRepr::U64(_)
| ValueRepr::I64(_)
| ValueRepr::F64(_)
| ValueRepr::U128(_)
| ValueRepr::I128(_) => {
if let Ok(val) = i64::try_from(self.clone()) {
val.hash(state)
} else {
as_f64(self, true).map(|x| x.to_bits()).hash(state)
}
}
}
}
}
#[derive(Clone)]
pub struct Value(pub(crate) ValueRepr);
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (&self.0, &other.0) {
(&ValueRepr::None, &ValueRepr::None) => true,
(&ValueRepr::Undefined(_), &ValueRepr::Undefined(_)) => true,
(&ValueRepr::String(ref a, _), &ValueRepr::String(ref b, _)) => a == b,
(&ValueRepr::SmallStr(ref a), &ValueRepr::SmallStr(ref b)) => a.as_str() == b.as_str(),
(&ValueRepr::Bytes(ref a), &ValueRepr::Bytes(ref b)) => a == b,
_ => match ops::coerce(self, other, false) {
Some(ops::CoerceResult::F64(a, b)) => a == b,
Some(ops::CoerceResult::I128(a, b)) => a == b,
Some(ops::CoerceResult::Str(a, b)) => a == b,
None => {
if let (Some(a), Some(b)) = (self.as_object(), other.as_object()) {
if a.is_same_object(b) {
return true;
} else if a.is_same_object_type(b) {
if let Some(rv) = a.custom_cmp(b) {
return rv == Ordering::Equal;
}
}
match (a.repr(), b.repr()) {
(ObjectRepr::Map, ObjectRepr::Map) => {
let mut need_length_fallback = true;
if let (Some(a_len), Some(b_len)) =
(a.enumerator_len(), b.enumerator_len())
{
if a_len != b_len {
return false;
}
need_length_fallback = false;
}
let mut a_count = 0;
if !a.try_iter_pairs().is_some_and(|mut ak| {
ak.all(|(k, v1)| {
a_count += 1;
b.get_value(&k) == Some(v1)
})
}) {
return false;
}
if !need_length_fallback {
true
} else {
a_count == b.try_iter().map_or(0, |x| x.count())
}
}
(
ObjectRepr::Seq | ObjectRepr::Iterable,
ObjectRepr::Seq | ObjectRepr::Iterable,
) => {
if let (Some(ak), Some(bk)) = (a.try_iter(), b.try_iter()) {
ak.eq(bk)
} else {
false
}
}
(ObjectRepr::Plain, ObjectRepr::Plain) => {
a.to_string() == b.to_string()
}
(_, _) => false,
}
} else {
false
}
}
},
}
}
}
impl Eq for Value {}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
fn f64_total_cmp(left: f64, right: f64) -> Ordering {
let mut left = left.to_bits() as i64;
let mut right = right.to_bits() as i64;
left ^= (((left >> 63) as u64) >> 1) as i64;
right ^= (((right >> 63) as u64) >> 1) as i64;
left.cmp(&right)
}
impl Ord for Value {
fn cmp(&self, other: &Self) -> Ordering {
let kind_ordering = self.kind().cmp(&other.kind());
if matches!(kind_ordering, Ordering::Less | Ordering::Greater) {
return kind_ordering;
}
match (&self.0, &other.0) {
(&ValueRepr::None, &ValueRepr::None) => Ordering::Equal,
(&ValueRepr::Undefined(_), &ValueRepr::Undefined(_)) => Ordering::Equal,
(&ValueRepr::String(ref a, _), &ValueRepr::String(ref b, _)) => a.cmp(b),
(&ValueRepr::SmallStr(ref a), &ValueRepr::SmallStr(ref b)) => {
a.as_str().cmp(b.as_str())
}
(&ValueRepr::Bytes(ref a), &ValueRepr::Bytes(ref b)) => a.cmp(b),
_ => match ops::coerce(self, other, false) {
Some(ops::CoerceResult::F64(a, b)) => f64_total_cmp(a, b),
Some(ops::CoerceResult::I128(a, b)) => a.cmp(&b),
Some(ops::CoerceResult::Str(a, b)) => a.cmp(b),
None => {
let a = self.as_object().unwrap();
let b = other.as_object().unwrap();
if a.is_same_object(b) {
Ordering::Equal
} else {
if a.is_same_object_type(b) {
if let Some(rv) = a.custom_cmp(b) {
return rv;
}
}
match (a.repr(), b.repr()) {
(ObjectRepr::Map, ObjectRepr::Map) => {
match (a.try_iter_pairs(), b.try_iter_pairs()) {
(Some(a), Some(b)) => a.cmp(b),
_ => unreachable!(),
}
}
(
ObjectRepr::Seq | ObjectRepr::Iterable,
ObjectRepr::Seq | ObjectRepr::Iterable,
) => match (a.try_iter(), b.try_iter()) {
(Some(a), Some(b)) => a.cmp(b),
_ => unreachable!(),
},
(ObjectRepr::Plain, ObjectRepr::Plain) => {
a.to_string().cmp(&b.to_string())
}
(_, _) => unreachable!(),
}
}
}
},
}
}
}
impl fmt::Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
fmt::Debug::fmt(&self.0, f)
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
ValueRepr::Undefined(_) => Ok(()),
ValueRepr::Bool(val) => val.fmt(f),
ValueRepr::U64(val) => val.fmt(f),
ValueRepr::I64(val) => val.fmt(f),
ValueRepr::F64(val) => {
if val.is_nan() {
f.write_str("NaN")
} else if val.is_infinite() {
write!(f, "{}inf", if val.is_sign_negative() { "-" } else { "" })
} else {
let mut num = val.to_string();
if !num.contains('.') {
num.push_str(".0");
}
write!(f, "{num}")
}
}
ValueRepr::None => f.write_str("none"),
ValueRepr::Invalid(ref val) => write!(f, "<invalid value: {val}>"),
ValueRepr::I128(val) => write!(f, "{}", { val.0 }),
ValueRepr::String(ref val, _) => write!(f, "{val}"),
ValueRepr::SmallStr(ref val) => write!(f, "{}", val.as_str()),
ValueRepr::Bytes(ref val) => write!(f, "{}", String::from_utf8_lossy(val)),
ValueRepr::U128(val) => write!(f, "{}", { val.0 }),
ValueRepr::Object(ref x) => write!(f, "{x}"),
}
}
}
impl Default for Value {
fn default() -> Value {
ValueRepr::Undefined(UndefinedType::Default).into()
}
}
#[doc(hidden)]
#[deprecated = "This function no longer has an effect. Use Arc::from directly."]
pub fn intern(s: &str) -> Arc<str> {
Arc::from(s.to_string())
}
#[allow(clippy::len_without_is_empty)]
impl Value {
pub const UNDEFINED: Value = Value(ValueRepr::Undefined(UndefinedType::Default));
pub fn from_serialize<T: Serialize>(value: T) -> Value {
INTERNAL_SERIALIZATION.with(|flag| {
let old = flag.replace(true);
let _serialization_guard = InternalSerializationGuard {
flag,
reset_on_drop: !old,
};
transform(value)
})
}
pub(crate) fn validate(self) -> Result<Value, Error> {
if let ValueRepr::Invalid(err) = self.0 {
Err(Arc::try_unwrap(err).unwrap_or_else(|arc| (*arc).internal_clone()))
} else {
Ok(self)
}
}
pub fn from_safe_string(value: String) -> Value {
ValueRepr::String(Arc::from(value), StringType::Safe).into()
}
pub fn from_bytes(value: Vec<u8>) -> Value {
ValueRepr::Bytes(value.into()).into()
}
pub fn from_object<T: Object + Send + Sync + 'static>(value: T) -> Value {
Value::from(ValueRepr::Object(DynObject::new(Arc::new(value))))
}
pub fn from_dyn_object<T: Into<DynObject>>(value: T) -> Value {
Value::from(ValueRepr::Object(value.into()))
}
pub fn make_iterable<I, T, F>(maker: F) -> Value
where
I: Iterator<Item = T> + Send + Sync + 'static,
T: Into<Value> + Send + Sync + 'static,
F: Fn() -> I + Send + Sync + 'static,
{
Value::make_object_iterable((), move |_| Box::new(maker().map(Into::into)))
}
pub fn make_object_iterable<T, F>(object: T, maker: F) -> Value
where
T: Send + Sync + 'static,
F: for<'a> Fn(&'a T) -> Box<dyn Iterator<Item = Value> + Send + Sync + 'a>
+ Send
+ Sync
+ 'static,
{
struct Iterable<T, F> {
maker: F,
object: T,
}
impl<T, F> fmt::Debug for Iterable<T, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("<iterator>").finish()
}
}
impl<T, F> Object for Iterable<T, F>
where
T: Send + Sync + 'static,
F: for<'a> Fn(&'a T) -> Box<dyn Iterator<Item = Value> + Send + Sync + 'a>
+ Send
+ Sync
+ 'static,
{
fn repr(self: &Arc<Self>) -> ObjectRepr {
ObjectRepr::Iterable
}
fn enumerate(self: &Arc<Self>) -> Enumerator {
mapped_enumerator(self, |this| (this.maker)(&this.object))
}
}
Value::from_object(Iterable { maker, object })
}
pub fn make_object_map<T, E, A>(object: T, enumerate_fn: E, attr_fn: A) -> Value
where
T: Send + Sync + 'static,
E: for<'a> Fn(&'a T) -> Box<dyn Iterator<Item = Value> + Send + Sync + 'a>
+ Send
+ Sync
+ 'static,
A: Fn(&T, &Value) -> Option<Value> + Send + Sync + 'static,
{
struct ProxyMapObject<T, E, A> {
enumerate_fn: E,
attr_fn: A,
object: T,
}
impl<T, E, A> fmt::Debug for ProxyMapObject<T, E, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("<map-object>").finish()
}
}
impl<T, E, A> Object for ProxyMapObject<T, E, A>
where
T: Send + Sync + 'static,
E: for<'a> Fn(&'a T) -> Box<dyn Iterator<Item = Value> + Send + Sync + 'a>
+ Send
+ Sync
+ 'static,
A: Fn(&T, &Value) -> Option<Value> + Send + Sync + 'static,
{
#[inline]
fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value> {
(self.attr_fn)(&self.object, key)
}
#[inline]
fn enumerate(self: &Arc<Self>) -> Enumerator {
mapped_enumerator(self, |this| (this.enumerate_fn)(&this.object))
}
}
Value::from_object(ProxyMapObject {
enumerate_fn,
attr_fn,
object,
})
}
pub fn make_one_shot_iterator<I, T>(iter: I) -> Value
where
I: Iterator<Item = T> + Send + Sync + 'static,
T: Into<Value> + Send + Sync + 'static,
{
let iter = Arc::new(Mutex::new(iter.fuse()));
Value::make_iterable(move || {
let iter = iter.clone();
std::iter::from_fn(move || iter.lock().unwrap().next())
})
}
pub fn from_function<F, Rv, Args>(f: F) -> Value
where
F: functions::Function<Rv, Args>,
Rv: FunctionResult,
Args: for<'a> FunctionArgs<'a>,
{
functions::BoxedFunction::new(f).to_value()
}
pub fn kind(&self) -> ValueKind {
match self.0 {
ValueRepr::Undefined(_) => ValueKind::Undefined,
ValueRepr::Bool(_) => ValueKind::Bool,
ValueRepr::U64(_) | ValueRepr::I64(_) | ValueRepr::F64(_) => ValueKind::Number,
ValueRepr::None => ValueKind::None,
ValueRepr::I128(_) => ValueKind::Number,
ValueRepr::String(..) | ValueRepr::SmallStr(_) => ValueKind::String,
ValueRepr::Bytes(_) => ValueKind::Bytes,
ValueRepr::U128(_) => ValueKind::Number,
ValueRepr::Invalid(_) => ValueKind::Invalid,
ValueRepr::Object(ref obj) => match obj.repr() {
ObjectRepr::Map => ValueKind::Map,
ObjectRepr::Seq => ValueKind::Seq,
ObjectRepr::Iterable => ValueKind::Iterable,
ObjectRepr::Plain => ValueKind::Plain,
},
}
}
pub fn is_number(&self) -> bool {
matches!(
self.0,
ValueRepr::U64(_)
| ValueRepr::I64(_)
| ValueRepr::F64(_)
| ValueRepr::I128(_)
| ValueRepr::U128(_)
)
}
pub fn is_integer(&self) -> bool {
matches!(
self.0,
ValueRepr::U64(_) | ValueRepr::I64(_) | ValueRepr::I128(_) | ValueRepr::U128(_)
)
}
pub fn is_kwargs(&self) -> bool {
Kwargs::extract(self).is_some()
}
pub fn is_true(&self) -> bool {
match self.0 {
ValueRepr::Bool(val) => val,
ValueRepr::U64(x) => x != 0,
ValueRepr::U128(x) => x.0 != 0,
ValueRepr::I64(x) => x != 0,
ValueRepr::I128(x) => x.0 != 0,
ValueRepr::F64(x) => x != 0.0,
ValueRepr::String(ref x, _) => !x.is_empty(),
ValueRepr::SmallStr(ref x) => !x.is_empty(),
ValueRepr::Bytes(ref x) => !x.is_empty(),
ValueRepr::None | ValueRepr::Undefined(_) | ValueRepr::Invalid(_) => false,
ValueRepr::Object(ref x) => x.is_true(),
}
}
pub fn is_safe(&self) -> bool {
matches!(&self.0, ValueRepr::String(_, StringType::Safe))
}
pub fn is_undefined(&self) -> bool {
matches!(&self.0, ValueRepr::Undefined(_))
}
pub fn is_none(&self) -> bool {
matches!(&self.0, ValueRepr::None)
}
pub fn to_str(&self) -> Option<Arc<str>> {
match self.0 {
ValueRepr::String(ref s, _) => Some(s.clone()),
ValueRepr::SmallStr(ref s) => Some(Arc::from(s.as_str())),
ValueRepr::Bytes(ref b) => Some(Arc::from(String::from_utf8_lossy(b))),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self.0 {
ValueRepr::String(ref s, _) => Some(s as &str),
ValueRepr::SmallStr(ref s) => Some(s.as_str()),
ValueRepr::Bytes(ref b) => str::from_utf8(b).ok(),
_ => None,
}
}
#[inline]
pub fn as_usize(&self) -> Option<usize> {
match self.0 {
ValueRepr::I64(val) => TryFrom::try_from(val).ok(),
ValueRepr::U64(val) => TryFrom::try_from(val).ok(),
_ => self.clone().try_into().ok(),
}
}
pub fn as_i64(&self) -> Option<i64> {
i64::try_from(self.clone()).ok()
}
pub fn as_bytes(&self) -> Option<&[u8]> {
match self.0 {
ValueRepr::String(ref s, _) => Some(s.as_bytes()),
ValueRepr::SmallStr(ref s) => Some(s.as_str().as_bytes()),
ValueRepr::Bytes(ref b) => Some(&b[..]),
_ => None,
}
}
pub fn as_object(&self) -> Option<&DynObject> {
match self.0 {
ValueRepr::Object(ref dy) => Some(dy),
_ => None,
}
}
pub fn len(&self) -> Option<usize> {
match self.0 {
ValueRepr::String(ref s, _) => Some(s.chars().count()),
ValueRepr::SmallStr(ref s) => Some(s.as_str().chars().count()),
ValueRepr::Bytes(ref b) => Some(b.len()),
ValueRepr::Object(ref dy) => dy.enumerator_len(),
_ => None,
}
}
pub fn get_attr(&self, key: &str) -> Result<Value, Error> {
let value = match self.0 {
ValueRepr::Undefined(_) => return Err(Error::from(ErrorKind::UndefinedError)),
ValueRepr::Object(ref dy) => dy.get_value_by_str(key),
_ => None,
};
Ok(value.unwrap_or(Value::UNDEFINED))
}
pub(crate) fn get_attr_fast(&self, key: &str) -> Option<Value> {
match self.0 {
ValueRepr::Object(ref dy) => dy.get_value_by_str(key),
_ => None,
}
}
pub fn get_item_by_index(&self, idx: usize) -> Result<Value, Error> {
self.get_item(&Value(ValueRepr::U64(idx as _)))
}
pub fn get_item(&self, key: &Value) -> Result<Value, Error> {
if let ValueRepr::Undefined(_) = self.0 {
Err(Error::from(ErrorKind::UndefinedError))
} else {
Ok(self.get_item_opt(key).unwrap_or(Value::UNDEFINED))
}
}
pub fn try_iter(&self) -> Result<ValueIter, Error> {
match self.0 {
ValueRepr::None | ValueRepr::Undefined(_) => Some(ValueIterImpl::Empty),
ValueRepr::String(ref s, _) => {
Some(ValueIterImpl::Chars(0, s.chars().count(), Arc::clone(s)))
}
ValueRepr::SmallStr(ref s) => Some(ValueIterImpl::Chars(
0,
s.as_str().chars().count(),
Arc::from(s.as_str()),
)),
ValueRepr::Object(ref obj) => obj.try_iter().map(ValueIterImpl::Dyn),
_ => None,
}
.map(|imp| ValueIter { imp })
.ok_or_else(|| {
Error::new(
ErrorKind::InvalidOperation,
format!("{} is not iterable", self.kind()),
)
})
}
pub fn reverse(&self) -> Result<Value, Error> {
match self.0 {
ValueRepr::Undefined(_) | ValueRepr::None => Some(self.clone()),
ValueRepr::String(ref s, _) => Some(Value::from(s.chars().rev().collect::<String>())),
ValueRepr::SmallStr(ref s) => {
Some(Value::from(s.as_str().chars().rev().collect::<String>()))
}
ValueRepr::Bytes(ref b) => Some(Value::from_bytes(
b.iter().rev().copied().collect::<Vec<_>>(),
)),
ValueRepr::Object(ref o) => match o.enumerate() {
Enumerator::NonEnumerable => None,
Enumerator::Empty => Some(Value::make_iterable(|| None::<Value>.into_iter())),
Enumerator::Seq(l) => {
let self_clone = o.clone();
Some(Value::make_iterable(move || {
let self_clone = self_clone.clone();
(0..l).rev().map(move |idx| {
self_clone.get_value(&Value::from(idx)).unwrap_or_default()
})
}))
}
Enumerator::Iter(iter) => {
let mut v = iter.collect::<Vec<_>>();
v.reverse();
Some(Value::make_object_iterable(v, move |v| {
Box::new(v.iter().cloned())
}))
}
Enumerator::RevIter(rev_iter) => {
let for_restart = self.clone();
let iter = Mutex::new(Some(rev_iter));
Some(Value::make_iterable(move || {
if let Some(iter) = iter.lock().unwrap().take() {
Box::new(iter) as Box<dyn Iterator<Item = Value> + Send + Sync>
} else {
match for_restart.reverse().and_then(|x| x.try_iter()) {
Ok(iterable) => Box::new(iterable)
as Box<dyn Iterator<Item = Value> + Send + Sync>,
Err(err) => Box::new(Some(Value::from(err)).into_iter())
as Box<dyn Iterator<Item = Value> + Send + Sync>,
}
}
}))
}
Enumerator::Str(s) => Some(Value::make_iterable(move || s.iter().rev().copied())),
Enumerator::Values(mut v) => {
v.reverse();
Some(Value::make_object_iterable(v, move |v| {
Box::new(v.iter().cloned())
}))
}
},
_ => None,
}
.ok_or_else(|| {
Error::new(
ErrorKind::InvalidOperation,
format!("cannot reverse values of type {}", self.kind()),
)
})
}
pub fn downcast_object_ref<T: 'static>(&self) -> Option<&T> {
match self.0 {
ValueRepr::Object(ref o) => o.downcast_ref(),
_ => None,
}
}
pub fn downcast_object<T: 'static>(&self) -> Option<Arc<T>> {
match self.0 {
ValueRepr::Object(ref o) => o.downcast(),
_ => None,
}
}
pub(crate) fn get_item_opt(&self, key: &Value) -> Option<Value> {
fn index(value: &Value, len: impl Fn() -> Option<usize>) -> Option<usize> {
match value.as_i64().and_then(|v| isize::try_from(v).ok()) {
Some(i) if i < 0 => some!(len()).checked_sub(i.unsigned_abs()),
Some(i) => Some(i as usize),
None => None,
}
}
match self.0 {
ValueRepr::Object(ref dy) => match dy.repr() {
ObjectRepr::Map | ObjectRepr::Plain => dy.get_value(key),
ObjectRepr::Iterable => {
if let Some(rv) = dy.get_value(key) {
return Some(rv);
}
if let Some(idx) = index(key, || dy.enumerator_len()) {
if let Some(mut iter) = dy.try_iter() {
if let Some(rv) = iter.nth(idx) {
return Some(rv);
}
}
}
None
}
ObjectRepr::Seq => {
let idx = index(key, || dy.enumerator_len()).map(Value::from);
dy.get_value(idx.as_ref().unwrap_or(key))
}
},
ValueRepr::String(ref s, _) => {
let idx = some!(index(key, || Some(s.chars().count())));
s.chars().nth(idx).map(Value::from)
}
ValueRepr::SmallStr(ref s) => {
let idx = some!(index(key, || Some(s.as_str().chars().count())));
s.as_str().chars().nth(idx).map(Value::from)
}
ValueRepr::Bytes(ref b) => {
let idx = some!(index(key, || Some(b.len())));
b.get(idx).copied().map(Value::from)
}
_ => None,
}
}
pub fn call(&self, state: &State, args: &[Value]) -> Result<Value, Error> {
if let ValueRepr::Object(ref dy) = self.0 {
dy.call(state, args)
} else {
Err(Error::new(
ErrorKind::InvalidOperation,
format!("value of type {} is not callable", self.kind()),
))
}
}
pub fn call_method(&self, state: &State, name: &str, args: &[Value]) -> Result<Value, Error> {
match self._call_method(state, name, args) {
Ok(rv) => Ok(rv),
Err(mut err) => {
if err.kind() == ErrorKind::UnknownMethod {
if let Some(ref callback) = state.env().unknown_method_callback {
match callback(state, self, name, args) {
Ok(result) => return Ok(result),
Err(callback_err) => {
if callback_err.kind() == ErrorKind::UnknownMethod {
err = callback_err;
} else {
return Err(callback_err);
}
}
}
}
if err.detail().is_none() {
err.set_detail(format!("{} has no method named {}", self.kind(), name));
}
}
Err(err)
}
}
}
fn _call_method(&self, state: &State, name: &str, args: &[Value]) -> Result<Value, Error> {
if let Some(object) = self.as_object() {
object.call_method(state, name, args)
} else {
Err(Error::from(ErrorKind::UnknownMethod))
}
}
#[cfg(feature = "builtins")]
pub(crate) fn get_path(&self, path: &str) -> Result<Value, Error> {
let mut rv = self.clone();
for part in path.split('.') {
if let Ok(num) = part.parse::<usize>() {
rv = ok!(rv.get_item_by_index(num));
} else {
rv = ok!(rv.get_attr(part));
}
}
Ok(rv)
}
#[cfg(feature = "builtins")]
pub(crate) fn get_path_or_default(&self, path: &str, default: &Value) -> Value {
match self.get_path(path) {
Err(_) => default.clone(),
Ok(val) if val.is_undefined() => default.clone(),
Ok(val) => val,
}
}
}
impl Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializing_for_value() {
let handle = LAST_VALUE_HANDLE.with(|x| {
let rv = x.get().wrapping_add(1);
x.set(rv);
rv
});
VALUE_HANDLES.with(|handles| handles.borrow_mut().insert(handle, self.clone()));
let mut s = ok!(serializer.serialize_tuple_struct(VALUE_HANDLE_MARKER, 1));
ok!(s.serialize_field(&handle));
return s.end();
}
match self.0 {
ValueRepr::Bool(b) => serializer.serialize_bool(b),
ValueRepr::U64(u) => serializer.serialize_u64(u),
ValueRepr::I64(i) => serializer.serialize_i64(i),
ValueRepr::F64(f) => serializer.serialize_f64(f),
ValueRepr::None | ValueRepr::Undefined(_) | ValueRepr::Invalid(_) => {
serializer.serialize_unit()
}
ValueRepr::U128(u) => serializer.serialize_u128(u.0),
ValueRepr::I128(i) => serializer.serialize_i128(i.0),
ValueRepr::String(ref s, _) => serializer.serialize_str(s),
ValueRepr::SmallStr(ref s) => serializer.serialize_str(s.as_str()),
ValueRepr::Bytes(ref b) => serializer.serialize_bytes(b),
ValueRepr::Object(ref o) => match o.repr() {
ObjectRepr::Plain => serializer.serialize_str(&o.to_string()),
ObjectRepr::Seq | ObjectRepr::Iterable => {
use serde::ser::SerializeSeq;
let mut seq = ok!(serializer.serialize_seq(o.enumerator_len()));
if let Some(iter) = o.try_iter() {
for item in iter {
ok!(seq.serialize_element(&item));
}
}
seq.end()
}
ObjectRepr::Map => {
use serde::ser::SerializeMap;
let mut map = ok!(serializer.serialize_map(None));
if let Some(iter) = o.try_iter_pairs() {
for (key, value) in iter {
ok!(map.serialize_entry(&key, &value));
}
}
map.end()
}
},
}
}
}
pub(crate) fn mapped_enumerator<F, T>(obj: &Arc<T>, maker: F) -> Enumerator
where
T: Object + 'static,
F: for<'a> FnOnce(&'a T) -> Box<dyn Iterator<Item = Value> + Send + Sync + 'a>,
{
struct Iter {
iter: Box<dyn Iterator<Item = Value> + Send + Sync + 'static>,
_object: DynObject,
}
impl Iterator for Iter {
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
let iter = unsafe {
std::mem::transmute::<Box<dyn Iterator<Item = _>>, Box<dyn Iterator<Item = _> + Send + Sync>>(
maker(obj),
)
};
let _object = DynObject::new(obj.clone());
Enumerator::Iter(Box::new(Iter { iter, _object }))
}
pub struct ValueIter {
imp: ValueIterImpl,
}
impl Iterator for ValueIter {
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
match self.imp {
ValueIterImpl::Empty => None,
ValueIterImpl::Chars(ref mut offset, ref mut len, ref s) => {
(s as &str)[*offset..].chars().next().map(|c| {
*offset += c.len_utf8();
*len -= 1;
Value::from(c)
})
}
ValueIterImpl::Dyn(ref mut iter) => iter.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.imp {
ValueIterImpl::Empty => (0, Some(0)),
ValueIterImpl::Chars(_, len, _) => (0, Some(len)),
ValueIterImpl::Dyn(ref iter) => iter.size_hint(),
}
}
}
impl fmt::Debug for ValueIter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ValueIterator").finish()
}
}
enum ValueIterImpl {
Empty,
Chars(usize, usize, Arc<str>),
Dyn(Box<dyn Iterator<Item = Value> + Send + Sync>),
}
impl From<Error> for Value {
fn from(value: Error) -> Self {
Value(ValueRepr::Invalid(Arc::new(value)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use similar_asserts::assert_eq;
#[test]
fn test_dynamic_object_roundtrip() {
use std::sync::atomic::{self, AtomicUsize};
#[derive(Debug, Clone)]
struct X(Arc<AtomicUsize>);
impl Object for X {
fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value> {
match key.as_str()? {
"value" => Some(Value::from(self.0.load(atomic::Ordering::Relaxed))),
_ => None,
}
}
fn enumerate(self: &Arc<Self>) -> Enumerator {
Enumerator::Str(&["value"])
}
fn render(self: &Arc<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.load(atomic::Ordering::Relaxed))
}
}
let x = Arc::new(X(Default::default()));
let x_value = Value::from_dyn_object(x.clone());
x.0.fetch_add(42, atomic::Ordering::Relaxed);
let x_clone = Value::from_serialize(&x_value);
x.0.fetch_add(23, atomic::Ordering::Relaxed);
assert_eq!(x_value.to_string(), "65");
assert_eq!(x_clone.to_string(), "65");
}
#[test]
fn test_string_char() {
let val = Value::from('a');
assert_eq!(char::try_from(val).unwrap(), 'a');
let val = Value::from("a");
assert_eq!(char::try_from(val).unwrap(), 'a');
let val = Value::from("wat");
assert!(char::try_from(val).is_err());
}
#[test]
#[cfg(target_pointer_width = "64")]
fn test_sizes() {
assert_eq!(std::mem::size_of::<Value>(), 24);
}
}