#![allow(clippy::result_large_err)]
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use facet_core::{
Def, Facet, Field, NumericType, PrimitiveType, Shape, StructKind, TextualType, Type, UserType,
Variant,
};
use facet_reflect::{AllocError, Partial, ReflectError, ShapeMismatchError, TypePlan};
use crate::{VNumber, Value, ValueType};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PathSegment {
Field(String),
Variant(String),
Index(usize),
}
impl core::fmt::Display for PathSegment {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
PathSegment::Field(name) => write!(f, ".{name}"),
PathSegment::Variant(name) => write!(f, "::{name}"),
PathSegment::Index(i) => write!(f, "[{i}]"),
}
}
}
#[derive(Debug)]
pub struct ValueError {
pub kind: ValueErrorKind,
pub source_path: Vec<PathSegment>,
pub dest_path: Vec<PathSegment>,
pub target_shape: Option<&'static Shape>,
pub source_value: Option<Value>,
}
impl core::fmt::Display for ValueError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if self.source_path.is_empty() {
write!(f, "{}", self.kind)
} else {
write!(f, "at {}: {}", self.source_path_string(), self.kind)
}
}
}
impl ValueError {
pub const fn new(kind: ValueErrorKind) -> Self {
Self {
kind,
source_path: Vec::new(),
dest_path: Vec::new(),
target_shape: None,
source_value: None,
}
}
pub const fn with_shape(mut self, shape: &'static Shape) -> Self {
self.target_shape = Some(shape);
self
}
pub fn with_value(mut self, value: Value) -> Self {
self.source_value = Some(value);
self
}
pub fn with_path(mut self, segment: PathSegment) -> Self {
self.source_path.insert(0, segment.clone());
self.dest_path.insert(0, segment);
self
}
pub fn source_path_string(&self) -> String {
if self.source_path.is_empty() {
"<root>".into()
} else {
use core::fmt::Write;
let mut s = String::new();
for seg in &self.source_path {
let _ = write!(s, "{seg}");
}
s
}
}
pub fn dest_path_string(&self) -> String {
if self.dest_path.is_empty() {
"<root>".into()
} else {
use core::fmt::Write;
let mut s = String::new();
for seg in &self.dest_path {
let _ = write!(s, "{seg}");
}
s
}
}
}
#[cfg(feature = "std")]
impl core::error::Error for ValueError {}
#[derive(Debug)]
pub enum ValueErrorKind {
TypeMismatch {
expected: &'static str,
got: ValueType,
},
MissingField {
field: &'static str,
},
UnknownField {
field: String,
},
NumberOutOfRange {
message: String,
},
Reflect(ReflectError),
Unsupported {
message: String,
},
}
impl core::fmt::Display for ValueErrorKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ValueErrorKind::TypeMismatch { expected, got } => {
write!(f, "type mismatch: expected {expected}, got {got:?}")
}
ValueErrorKind::MissingField { field } => {
write!(f, "missing required field `{field}`")
}
ValueErrorKind::UnknownField { field } => {
write!(f, "unknown field `{field}`")
}
ValueErrorKind::NumberOutOfRange { message } => {
write!(f, "number out of range: {message}")
}
ValueErrorKind::Reflect(e) => write!(f, "reflection error: {e}"),
ValueErrorKind::Unsupported { message } => {
write!(f, "unsupported: {message}")
}
}
}
}
impl From<ReflectError> for ValueError {
fn from(err: ReflectError) -> Self {
ValueError::new(ValueErrorKind::Reflect(err))
}
}
impl From<ShapeMismatchError> for ValueError {
fn from(err: ShapeMismatchError) -> Self {
ValueError::new(ValueErrorKind::Unsupported {
message: format!(
"shape mismatch: expected {}, got {}",
err.expected, err.actual
),
})
}
}
impl From<AllocError> for ValueError {
fn from(err: AllocError) -> Self {
ValueError::new(ValueErrorKind::Unsupported {
message: format!("allocation failed for {}: {}", err.shape, err.operation),
})
}
}
pub type Result<T> = core::result::Result<T, ValueError>;
pub fn from_value<T: Facet<'static>>(value: Value) -> Result<T> {
let plan = TypePlan::<T>::build().map_err(|e| {
ValueError::from(e)
.with_shape(T::SHAPE)
.with_value(value.clone())
})?;
let partial = plan.partial_owned().map_err(|e| {
ValueError::from(e)
.with_shape(T::SHAPE)
.with_value(value.clone())
})?;
let partial = deserialize_value_into(&value, partial)
.map_err(|e| e.with_shape(T::SHAPE).with_value(value.clone()))?;
let heap_value = partial.build().map_err(|e| {
ValueError::from(e)
.with_shape(T::SHAPE)
.with_value(value.clone())
})?;
heap_value.materialize().map_err(|e| {
ValueError::from(e)
.with_shape(T::SHAPE)
.with_value(value.clone())
})
}
fn deserialize_value_into<'facet>(
value: &Value,
partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
let mut partial = partial;
let shape = partial.shape();
if matches!(&shape.def, Def::Option(_)) {
return deserialize_option(value, partial);
}
if matches!(&shape.def, Def::Pointer(_)) {
return deserialize_pointer(value, partial);
}
#[cfg(feature = "alloc")]
if shape.proxy.is_some() {
let (partial_returned, has_proxy) = partial.begin_custom_deserialization_from_shape()?;
partial = partial_returned;
if has_proxy {
partial = deserialize_value_into(value, partial)?;
partial = partial.end()?;
return Ok(partial);
}
}
if shape.builder_shape.is_some() {
partial = partial.begin_inner()?;
partial = deserialize_value_into(value, partial)?;
partial = partial.end()?;
return Ok(partial);
}
if shape.inner.is_some()
&& !matches!(
&shape.def,
Def::List(_) | Def::Map(_) | Def::Set(_) | Def::Array(_)
)
{
partial = partial.begin_inner()?;
partial = deserialize_value_into(value, partial)?;
partial = partial.end()?;
return Ok(partial);
}
match &shape.ty {
Type::User(UserType::Struct(struct_def)) => {
if struct_def.kind == StructKind::Tuple {
return deserialize_tuple(value, partial);
}
return deserialize_struct(value, partial);
}
Type::User(UserType::Enum(_)) => return deserialize_enum(value, partial),
_ => {}
}
match &shape.def {
Def::Scalar => deserialize_scalar(value, partial),
Def::List(_) => deserialize_list(value, partial),
Def::Map(_) => deserialize_map(value, partial),
Def::Array(_) => deserialize_array(value, partial),
Def::Set(_) => deserialize_set(value, partial),
Def::DynamicValue(_) => {
partial = partial.set(value.clone())?;
Ok(partial)
}
_ => Err(ValueError::new(ValueErrorKind::Unsupported {
message: format!("unsupported shape def: {:?}", shape.def),
})),
}
}
fn deserialize_scalar<'facet>(
value: &Value,
partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
let mut partial = partial;
let shape = partial.shape();
match value.value_type() {
ValueType::Null => {
partial = partial.set_default()?;
Ok(partial)
}
ValueType::Bool => {
let b = value.as_bool().unwrap();
partial = partial.set(b)?;
Ok(partial)
}
ValueType::Number => {
let num = value.as_number().unwrap();
if *shape == *String::SHAPE {
let s = if let Some(i) = num.to_i64() {
format!("{i}")
} else if let Some(u) = num.to_u64() {
format!("{u}")
} else if let Some(f) = num.to_f64() {
format!("{f}")
} else {
return Err(ValueError::new(ValueErrorKind::TypeMismatch {
expected: "String",
got: ValueType::Number,
}));
};
partial = partial.set(s)?;
Ok(partial)
} else {
set_number(num, partial, shape)
}
}
ValueType::String => {
let s = value.as_string().unwrap();
if shape.vtable.has_parse() {
partial = partial.parse_from_str(s.as_str())?;
} else {
partial = partial.set(s.as_str().to_string())?;
}
Ok(partial)
}
ValueType::Bytes => {
let bytes = value.as_bytes().unwrap();
partial = partial.set(bytes.as_slice().to_vec())?;
Ok(partial)
}
other => Err(ValueError::new(ValueErrorKind::TypeMismatch {
expected: shape.type_identifier,
got: other,
})),
}
}
fn set_number<'facet>(
num: &VNumber,
partial: Partial<'facet, false>,
shape: &Shape,
) -> Result<Partial<'facet, false>> {
use facet_core::{NumericType, PrimitiveType, ShapeLayout};
let mut partial = partial;
let size = match shape.layout {
ShapeLayout::Sized(layout) => layout.size(),
_ => {
return Err(ValueError::new(ValueErrorKind::Unsupported {
message: "unsized numeric type".into(),
}));
}
};
match &shape.ty {
Type::Primitive(PrimitiveType::Numeric(NumericType::Integer { signed: true })) => {
let val = num.to_i64().ok_or_else(|| {
ValueError::new(ValueErrorKind::NumberOutOfRange {
message: "value cannot be represented as i64".into(),
})
})?;
if *shape == *isize::SHAPE {
let v = isize::try_from(val).map_err(|_| {
ValueError::new(ValueErrorKind::NumberOutOfRange {
message: format!("{val} out of range for isize"),
})
})?;
partial = partial.set(v)?;
} else {
match size {
1 => {
let v = i8::try_from(val).map_err(|_| {
ValueError::new(ValueErrorKind::NumberOutOfRange {
message: format!("{val} out of range for i8"),
})
})?;
partial = partial.set(v)?;
}
2 => {
let v = i16::try_from(val).map_err(|_| {
ValueError::new(ValueErrorKind::NumberOutOfRange {
message: format!("{val} out of range for i16"),
})
})?;
partial = partial.set(v)?;
}
4 => {
let v = i32::try_from(val).map_err(|_| {
ValueError::new(ValueErrorKind::NumberOutOfRange {
message: format!("{val} out of range for i32"),
})
})?;
partial = partial.set(v)?;
}
8 => {
partial = partial.set(val)?;
}
16 => {
partial = partial.set(val as i128)?;
}
_ => {
return Err(ValueError::new(ValueErrorKind::Unsupported {
message: format!("unexpected integer size: {size}"),
}));
}
}
}
}
Type::Primitive(PrimitiveType::Numeric(NumericType::Integer { signed: false })) => {
let val = num.to_u64().ok_or_else(|| {
ValueError::new(ValueErrorKind::NumberOutOfRange {
message: "value cannot be represented as u64".into(),
})
})?;
if *shape == *usize::SHAPE {
let v = usize::try_from(val).map_err(|_| {
ValueError::new(ValueErrorKind::NumberOutOfRange {
message: format!("{val} out of range for usize"),
})
})?;
partial = partial.set(v)?;
} else {
match size {
1 => {
let v = u8::try_from(val).map_err(|_| {
ValueError::new(ValueErrorKind::NumberOutOfRange {
message: format!("{val} out of range for u8"),
})
})?;
partial = partial.set(v)?;
}
2 => {
let v = u16::try_from(val).map_err(|_| {
ValueError::new(ValueErrorKind::NumberOutOfRange {
message: format!("{val} out of range for u16"),
})
})?;
partial = partial.set(v)?;
}
4 => {
let v = u32::try_from(val).map_err(|_| {
ValueError::new(ValueErrorKind::NumberOutOfRange {
message: format!("{val} out of range for u32"),
})
})?;
partial = partial.set(v)?;
}
8 => {
partial = partial.set(val)?;
}
16 => {
partial = partial.set(val as u128)?;
}
_ => {
return Err(ValueError::new(ValueErrorKind::Unsupported {
message: format!("unexpected integer size: {size}"),
}));
}
}
}
}
Type::Primitive(PrimitiveType::Numeric(NumericType::Float)) => {
let val = num.to_f64_lossy();
match size {
4 => {
partial = partial.set(val as f32)?;
}
8 => {
partial = partial.set(val)?;
}
_ => {
return Err(ValueError::new(ValueErrorKind::Unsupported {
message: format!("unexpected float size: {size}"),
}));
}
}
}
_ => {
return Err(ValueError::new(ValueErrorKind::TypeMismatch {
expected: shape.type_identifier,
got: ValueType::Number,
}));
}
}
Ok(partial)
}
fn deserialize_struct<'facet>(
value: &Value,
partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
let mut partial = partial;
let obj = value.as_object().ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "object",
got: value.value_type(),
})
})?;
let struct_def = match &partial.shape().ty {
Type::User(UserType::Struct(s)) => s,
_ => {
return Err(ValueError::new(ValueErrorKind::Unsupported {
message: "expected struct type".into(),
}));
}
};
let deny_unknown_fields = partial.struct_plan().unwrap().deny_unknown_fields;
let has_flattened = struct_def.fields.iter().any(|f| f.is_flattened());
if has_flattened {
return deserialize_struct_with_flatten(obj, partial, struct_def, deny_unknown_fields);
}
let num_fields = struct_def.fields.len();
let mut fields_set = alloc::vec![false; num_fields];
for (key, val) in obj.iter() {
let key_str = key.as_str();
let field_info = struct_def
.fields
.iter()
.enumerate()
.find(|(_, f)| f.effective_name() == key_str || f.alias == Some(key_str));
if let Some((idx, field)) = field_info {
partial = partial.begin_field(field.name)?;
#[cfg(feature = "alloc")]
if field.proxy_convert_in_fn().is_some() {
partial = partial.begin_custom_deserialization()?;
partial = deserialize_value_into(val, partial)?;
partial = partial.end()?;
} else {
partial = deserialize_value_into(val, partial)?;
}
#[cfg(not(feature = "alloc"))]
{
partial = deserialize_value_into(val, partial)?;
}
partial = partial.end()?;
fields_set[idx] = true;
} else if deny_unknown_fields {
return Err(ValueError::new(ValueErrorKind::UnknownField {
field: key_str.to_string(),
}));
}
}
for (idx, field) in struct_def.fields.iter().enumerate() {
if fields_set[idx] {
continue;
}
partial = partial
.set_nth_field_to_default(idx)
.map_err(|_| ValueError::new(ValueErrorKind::MissingField { field: field.name }))?;
}
Ok(partial)
}
fn deserialize_struct_with_flatten<'facet>(
obj: &crate::VObject,
mut partial: Partial<'facet, false>,
struct_def: &'static facet_core::StructType,
deny_unknown_fields: bool,
) -> Result<Partial<'facet, false>> {
use alloc::collections::BTreeMap;
let num_fields = struct_def.fields.len();
let mut fields_set = alloc::vec![false; num_fields];
let mut flatten_keys: BTreeMap<&str, (usize, &str)> = BTreeMap::new();
for (idx, field) in struct_def.fields.iter().enumerate() {
if !field.is_flattened() {
continue;
}
let inner_shape = field.shape.get();
if let Type::User(UserType::Struct(inner_struct)) = &inner_shape.ty {
for inner_field in inner_struct.fields.iter() {
let key_name = inner_field.rename.unwrap_or(inner_field.name);
flatten_keys.insert(key_name, (idx, inner_field.name));
}
}
}
let mut flatten_values: Vec<BTreeMap<String, Value>> =
(0..num_fields).map(|_| BTreeMap::new()).collect();
for (key, val) in obj.iter() {
let key_str = key.as_str();
let direct_field = struct_def.fields.iter().enumerate().find(|(_, f)| {
!f.is_flattened() && (f.effective_name() == key_str || f.alias == Some(key_str))
});
if let Some((idx, field)) = direct_field {
partial = partial.begin_field(field.name)?;
#[cfg(feature = "alloc")]
if field.proxy_convert_in_fn().is_some() {
partial = partial.begin_custom_deserialization()?;
partial = deserialize_value_into(val, partial)?;
partial = partial.end()?;
} else {
partial = deserialize_value_into(val, partial)?;
}
#[cfg(not(feature = "alloc"))]
{
partial = deserialize_value_into(val, partial)?;
}
partial = partial.end()?;
fields_set[idx] = true;
continue;
}
if let Some(&(flatten_idx, inner_name)) = flatten_keys.get(key_str) {
flatten_values[flatten_idx].insert(inner_name.to_string(), val.clone());
fields_set[flatten_idx] = true;
continue;
}
if deny_unknown_fields {
return Err(ValueError::new(ValueErrorKind::UnknownField {
field: key_str.to_string(),
}));
}
}
for (idx, field) in struct_def.fields.iter().enumerate() {
if !field.is_flattened() {
continue;
}
if !flatten_values[idx].is_empty() {
let mut synthetic_obj = crate::VObject::new();
let values = core::mem::take(&mut flatten_values[idx]);
for (k, v) in values {
synthetic_obj.insert(k, v);
}
let synthetic_value = Value::from(synthetic_obj);
partial = partial.begin_field(field.name)?;
partial = deserialize_value_into(&synthetic_value, partial)?;
partial = partial.end()?;
fields_set[idx] = true;
}
}
for (idx, field) in struct_def.fields.iter().enumerate() {
if fields_set[idx] {
continue;
}
partial = partial
.set_nth_field_to_default(idx)
.map_err(|_| ValueError::new(ValueErrorKind::MissingField { field: field.name }))?;
}
Ok(partial)
}
fn deserialize_tuple<'facet>(
value: &Value,
partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
let mut partial = partial;
let arr = value.as_array().ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "array",
got: value.value_type(),
})
})?;
let tuple_len = match &partial.shape().ty {
Type::User(UserType::Struct(struct_def)) => struct_def.fields.len(),
_ => {
return Err(ValueError::new(ValueErrorKind::Unsupported {
message: "expected tuple type".into(),
}));
}
};
if arr.len() != tuple_len {
return Err(ValueError::new(ValueErrorKind::Unsupported {
message: format!("tuple has {} elements but got {}", tuple_len, arr.len()),
}));
}
for (i, item) in arr.iter().enumerate() {
partial = partial.begin_nth_field(i)?;
partial = deserialize_value_into(item, partial)?;
partial = partial.end()?;
}
Ok(partial)
}
fn deserialize_enum<'facet>(
value: &Value,
partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
let shape = partial.shape();
let tag_key = shape.get_tag_attr();
let content_key = shape.get_content_attr();
if shape.is_numeric() && tag_key.is_none() {
return deserialize_numeric_enum(value, partial);
}
if shape.is_untagged() {
return deserialize_untagged_enum(value, partial);
}
match (tag_key, content_key) {
(Some(tag_key), None) => deserialize_internally_tagged_enum(value, partial, tag_key),
(Some(tag_key), Some(content_key)) => {
deserialize_adjacently_tagged_enum(value, partial, tag_key, content_key)
}
(None, None) => deserialize_externally_tagged_enum(value, partial),
(None, Some(_)) => Err(ValueError::new(ValueErrorKind::Unsupported {
message: "content key without tag key is invalid".into(),
})),
}
}
fn deserialize_numeric_enum<'facet>(
value: &Value,
mut partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
let discriminant = match value.value_type() {
ValueType::Number => {
let num = value.as_number().unwrap();
if let Some(i) = num.to_i64() {
i
} else {
return Err(ValueError::new(ValueErrorKind::TypeMismatch {
expected: "Could not parse discriminant into i64", got: ValueType::Number,
}));
}
}
ValueType::String => {
let s = value.as_string().unwrap().as_str();
s.parse().map_err(|_| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "Failed to parse string into i64",
got: ValueType::String,
})
})?
}
other => {
return Err(ValueError::new(ValueErrorKind::TypeMismatch {
expected: "Expected number or string for numeric enum",
got: other,
}));
}
};
partial = partial.select_variant(discriminant)?;
Ok(partial)
}
fn deserialize_externally_tagged_enum<'facet>(
value: &Value,
mut partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
match value.value_type() {
ValueType::String => {
let variant_name = value.as_string().unwrap().as_str();
partial = partial.select_variant_named(variant_name)?;
Ok(partial)
}
ValueType::Object => {
let obj = value.as_object().unwrap();
if obj.len() != 1 {
return Err(ValueError::new(ValueErrorKind::Unsupported {
message: format!("enum object must have exactly 1 key, got {}", obj.len()),
}));
}
let (key, val) = obj.iter().next().unwrap();
let variant_name = key.as_str();
partial = partial.select_variant_named(variant_name)?;
let variant = partial.selected_variant().ok_or_else(|| {
ValueError::new(ValueErrorKind::Unsupported {
message: "failed to get selected variant".into(),
})
})?;
populate_variant_from_value(val, partial, &variant)
}
other => Err(ValueError::new(ValueErrorKind::TypeMismatch {
expected: "string or object for enum",
got: other,
})),
}
}
fn deserialize_internally_tagged_enum<'facet>(
value: &Value,
mut partial: Partial<'facet, false>,
tag_key: &str,
) -> Result<Partial<'facet, false>> {
let obj = value.as_object().ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "object for internally tagged enum",
got: value.value_type(),
})
})?;
let tag_value = obj.get(tag_key).ok_or_else(|| {
ValueError::new(ValueErrorKind::Unsupported {
message: format!("internally tagged enum missing tag key '{tag_key}'"),
})
})?;
if partial.shape().is_numeric() {
let discriminant = tag_value
.as_number()
.and_then(VNumber::to_i64)
.ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "integer for enum discriminant",
got: tag_value.value_type(),
})
})?;
partial = partial.select_variant(discriminant)?;
} else {
let variant_name = tag_value.as_string().ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "string for enum tag",
got: tag_value.value_type(),
})
})?;
partial = partial.select_variant_named(variant_name.as_str())?;
}
let variant = partial.selected_variant().ok_or_else(|| {
ValueError::new(ValueErrorKind::Unsupported {
message: "failed to get selected variant".into(),
})
})?;
match variant.data.kind {
StructKind::Unit => {
Ok(partial)
}
StructKind::Struct => {
for field in variant.data.fields.iter() {
if let Some(field_value) = obj
.get(field.effective_name())
.or_else(|| field.alias.and_then(|alias| obj.get(alias)))
{
partial = partial.begin_field(field.name)?;
partial = deserialize_enum_field_value(field_value, field, partial)?;
partial = partial.end()?;
}
}
Ok(partial)
}
StructKind::TupleStruct | StructKind::Tuple => {
Err(ValueError::new(ValueErrorKind::Unsupported {
message: "internally tagged tuple variants are not supported".into(),
}))
}
}
}
fn deserialize_adjacently_tagged_enum<'facet>(
value: &Value,
mut partial: Partial<'facet, false>,
tag_key: &str,
content_key: &str,
) -> Result<Partial<'facet, false>> {
let obj = value.as_object().ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "object for adjacently tagged enum",
got: value.value_type(),
})
})?;
let tag_value = obj.get(tag_key).ok_or_else(|| {
ValueError::new(ValueErrorKind::Unsupported {
message: format!("adjacently tagged enum missing tag key '{tag_key}'"),
})
})?;
if partial.shape().is_numeric() {
let discriminant = tag_value
.as_number()
.and_then(VNumber::to_i64)
.ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "integer for enum discriminant",
got: tag_value.value_type(),
})
})?;
partial = partial.select_variant(discriminant)?;
} else {
let variant_name = tag_value.as_string().ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "string for enum tag",
got: tag_value.value_type(),
})
})?;
partial = partial.select_variant_named(variant_name.as_str())?;
}
let variant = partial.selected_variant().ok_or_else(|| {
ValueError::new(ValueErrorKind::Unsupported {
message: "failed to get selected variant".into(),
})
})?;
match variant.data.kind {
StructKind::Unit => {
Ok(partial)
}
_ => {
let content_value = obj.get(content_key).ok_or_else(|| {
ValueError::new(ValueErrorKind::Unsupported {
message: format!("adjacently tagged enum missing content key '{content_key}'"),
})
})?;
populate_variant_from_value(content_value, partial, &variant)
}
}
}
fn deserialize_untagged_enum<'facet>(
value: &Value,
partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
let mut partial = partial;
let shape = partial.shape();
let enum_type = match &shape.ty {
Type::User(UserType::Enum(enum_def)) => enum_def,
_ => {
return Err(ValueError::new(ValueErrorKind::Unsupported {
message: "expected enum type".into(),
}));
}
};
for variant in enum_type.variants.iter() {
if value_matches_variant(value, variant) {
partial = partial.select_variant_named(variant.effective_name())?;
return populate_variant_from_value(value, partial, variant);
}
}
Err(ValueError::new(ValueErrorKind::TypeMismatch {
expected: shape.type_identifier,
got: value.value_type(),
}))
}
fn populate_variant_from_value<'facet>(
value: &Value,
mut partial: Partial<'facet, false>,
variant: &Variant,
) -> Result<Partial<'facet, false>> {
match variant.data.kind {
StructKind::Unit => {
if !matches!(value.value_type(), ValueType::Null) {
return Err(ValueError::new(ValueErrorKind::TypeMismatch {
expected: "null for unit variant",
got: value.value_type(),
}));
}
}
StructKind::TupleStruct | StructKind::Tuple => {
let num_fields = variant.data.fields.len();
if num_fields == 0 {
} else if num_fields == 1 {
let field = variant.data.fields[0];
partial = partial.begin_nth_field(0)?;
partial = deserialize_enum_field_value(value, &field, partial)?;
partial = partial.end()?;
} else {
let arr = value.as_array().ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "array for tuple variant",
got: value.value_type(),
})
})?;
if arr.len() != num_fields {
return Err(ValueError::new(ValueErrorKind::Unsupported {
message: format!(
"tuple variant has {} fields but got {}",
num_fields,
arr.len()
),
}));
}
for (i, (field, item)) in variant.data.fields.iter().zip(arr.iter()).enumerate() {
partial = partial.begin_nth_field(i)?;
partial = deserialize_enum_field_value(item, field, partial)?;
partial = partial.end()?;
}
}
}
StructKind::Struct => {
let inner_obj = value.as_object().ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "object for struct variant",
got: value.value_type(),
})
})?;
for (field_key, field_val) in inner_obj.iter() {
let key = field_key.as_str();
let field = variant
.data
.fields
.iter()
.find(|f| f.effective_name() == key || f.alias == Some(key))
.ok_or_else(|| {
ValueError::new(ValueErrorKind::UnknownField {
field: key.to_string(),
})
})?;
partial = partial.begin_field(field.name)?;
partial = deserialize_enum_field_value(field_val, field, partial)?;
partial = partial.end()?;
}
}
}
Ok(partial)
}
fn deserialize_enum_field_value<'facet>(
value: &Value,
field: &Field,
mut partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
#[cfg(feature = "alloc")]
if field.proxy_convert_in_fn().is_some() {
partial = partial.begin_custom_deserialization()?;
partial = deserialize_value_into(value, partial)?;
partial = partial.end()?;
} else {
partial = deserialize_value_into(value, partial)?;
}
#[cfg(not(feature = "alloc"))]
{
partial = deserialize_value_into(value, partial)?;
}
Ok(partial)
}
fn value_matches_variant(value: &Value, variant: &Variant) -> bool {
match variant.data.kind {
StructKind::Unit => matches!(value.value_type(), ValueType::Null),
StructKind::TupleStruct | StructKind::Tuple => {
let fields = variant.data.fields;
if fields.is_empty() {
matches!(value.value_type(), ValueType::Null)
} else if fields.len() == 1 {
value_matches_shape(value, fields[0].shape.get())
} else {
value
.as_array()
.map(|arr| arr.len() == fields.len())
.unwrap_or(false)
}
}
StructKind::Struct => matches!(value.value_type(), ValueType::Object),
}
}
fn value_matches_shape(value: &Value, shape: &'static Shape) -> bool {
match &shape.ty {
Type::Primitive(PrimitiveType::Boolean) => {
matches!(value.value_type(), ValueType::Bool)
}
Type::Primitive(PrimitiveType::Numeric(num)) => match num {
NumericType::Integer { signed } => {
if *signed {
value.as_number().and_then(|n| n.to_i64()).is_some()
} else {
value.as_number().and_then(|n| n.to_u64()).is_some()
}
}
NumericType::Float => value.as_number().and_then(|n| n.to_f64()).is_some(),
},
_ => true,
}
}
fn deserialize_list<'facet>(
value: &Value,
partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
let mut partial = partial;
let arr = value.as_array().ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "array",
got: value.value_type(),
})
})?;
partial = partial.init_list()?;
for item in arr.iter() {
partial = partial.begin_list_item()?;
partial = deserialize_value_into(item, partial)?;
partial = partial.end()?;
}
Ok(partial)
}
fn deserialize_array<'facet>(
value: &Value,
partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
let mut partial = partial;
let arr = value.as_array().ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "array",
got: value.value_type(),
})
})?;
let array_len = match &partial.shape().def {
Def::Array(arr_def) => arr_def.n,
_ => {
return Err(ValueError::new(ValueErrorKind::Unsupported {
message: "expected array type".into(),
}));
}
};
if arr.len() != array_len {
return Err(ValueError::new(ValueErrorKind::Unsupported {
message: format!(
"fixed array has {} elements but got {}",
array_len,
arr.len()
),
}));
}
for (i, item) in arr.iter().enumerate() {
partial = partial.begin_nth_field(i)?;
partial = deserialize_value_into(item, partial)?;
partial = partial.end()?;
}
Ok(partial)
}
fn deserialize_set<'facet>(
value: &Value,
partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
let mut partial = partial;
let arr = value.as_array().ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "array",
got: value.value_type(),
})
})?;
partial = partial.init_set()?;
for item in arr.iter() {
partial = partial.begin_set_item()?;
partial = deserialize_value_into(item, partial)?;
partial = partial.end()?;
}
Ok(partial)
}
fn deserialize_map<'facet>(
value: &Value,
partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
let mut partial = partial;
let obj = value.as_object().ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "object",
got: value.value_type(),
})
})?;
partial = partial.init_map()?;
for (key, val) in obj.iter() {
partial = partial.begin_key()?;
if partial.shape().inner.is_some() {
partial = partial.begin_inner()?;
partial = partial.set(key.as_str().to_string())?;
partial = partial.end()?;
} else {
partial = partial.set(key.as_str().to_string())?;
}
partial = partial.end()?;
partial = partial.begin_value()?;
partial = deserialize_value_into(val, partial)?;
partial = partial.end()?;
}
Ok(partial)
}
fn deserialize_option<'facet>(
value: &Value,
partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
let mut partial = partial;
if value.is_null() {
partial = partial.set_default()?; } else {
partial = partial.begin_some()?;
partial = deserialize_value_into(value, partial)?;
partial = partial.end()?;
}
Ok(partial)
}
fn deserialize_pointer<'facet>(
value: &Value,
partial: Partial<'facet, false>,
) -> Result<Partial<'facet, false>> {
use facet_core::{KnownPointer, SequenceType};
let mut partial = partial;
let (is_slice_pointer, is_reference, is_cow) =
if let Def::Pointer(ptr_def) = partial.shape().def {
let is_slice = if let Some(pointee) = ptr_def.pointee() {
matches!(pointee.ty, Type::Sequence(SequenceType::Slice(_)))
} else {
false
};
let is_ref = matches!(
ptr_def.known,
Some(KnownPointer::SharedReference | KnownPointer::ExclusiveReference)
);
let is_cow = matches!(ptr_def.known, Some(KnownPointer::Cow));
(is_slice, is_ref, is_cow)
} else {
(false, false, false)
};
if is_reference {
return Err(ValueError::new(ValueErrorKind::Unsupported {
message: format!(
"cannot deserialize into reference type '{}'",
partial.shape().type_identifier
),
}));
}
if is_cow {
if let Def::Pointer(ptr_def) = partial.shape().def
&& let Some(pointee) = ptr_def.pointee()
&& matches!(
pointee.ty,
Type::Primitive(PrimitiveType::Textual(TextualType::Str))
)
{
if let Some(s) = value.as_string() {
partial = partial.set(alloc::borrow::Cow::<'static, str>::Owned(
s.as_str().to_string(),
))?;
return Ok(partial);
} else {
return Err(ValueError::new(ValueErrorKind::TypeMismatch {
expected: "string for Cow<str>",
got: value.value_type(),
}));
}
}
partial = partial.begin_inner()?;
partial = deserialize_value_into(value, partial)?;
partial = partial.end()?;
return Ok(partial);
}
partial = partial.begin_smart_ptr()?;
if is_slice_pointer {
let arr = value.as_array().ok_or_else(|| {
ValueError::new(ValueErrorKind::TypeMismatch {
expected: "array",
got: value.value_type(),
})
})?;
for item in arr.iter() {
partial = partial.begin_list_item()?;
partial = deserialize_value_into(item, partial)?;
partial = partial.end()?;
}
} else {
partial = deserialize_value_into(value, partial)?;
}
partial = partial.end()?;
Ok(partial)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{VArray, VObject, VString};
#[test]
fn test_deserialize_primitives() {
let v = Value::TRUE;
let b: bool = from_value(v).unwrap();
assert!(b);
let v = Value::from(42i64);
let n: i32 = from_value(v).unwrap();
assert_eq!(n, 42);
let v: Value = VString::new("hello").into();
let s: String = from_value(v).unwrap();
assert_eq!(s, "hello");
}
#[test]
fn test_deserialize_option() {
let v = Value::from(42i64);
let opt: Option<i32> = from_value(v).unwrap();
assert_eq!(opt, Some(42));
let v = Value::NULL;
let opt: Option<i32> = from_value(v).unwrap();
assert_eq!(opt, None);
}
#[test]
fn test_deserialize_vec() {
let mut arr = VArray::new();
arr.push(Value::from(1i64));
arr.push(Value::from(2i64));
arr.push(Value::from(3i64));
let v: Value = arr.into();
let vec: alloc::vec::Vec<i32> = from_value(v).unwrap();
assert_eq!(vec, alloc::vec![1, 2, 3]);
}
#[test]
fn test_deserialize_nested() {
let mut arr = VArray::new();
arr.push(Value::from(1i64));
arr.push(Value::NULL);
arr.push(Value::from(3i64));
let v: Value = arr.into();
let vec: alloc::vec::Vec<Option<i32>> = from_value(v).unwrap();
assert_eq!(vec, alloc::vec![Some(1), None, Some(3)]);
}
#[test]
fn test_deserialize_map() {
use alloc::collections::BTreeMap;
let mut obj = VObject::new();
obj.insert("a", Value::from(1i64));
obj.insert("b", Value::from(2i64));
let v: Value = obj.into();
let map: BTreeMap<String, i32> = from_value(v).unwrap();
assert_eq!(map.get("a"), Some(&1));
assert_eq!(map.get("b"), Some(&2));
}
}