use alloc::sync::Arc;
use alloc::vec::Vec;
use facet_core::{
Characteristic, ConstTypeId, Def, DefaultInPlaceFn, DefaultSource, EnumType, Field, ProxyDef,
ScalarType, SequenceType, Shape, StructType, Type, UserType, ValidatorFn, Variant,
};
use hashbrown::HashMap;
use smallvec::SmallVec;
use super::arena::{Arena, Idx, SliceRange};
use crate::AllocError;
#[cfg(feature = "std")]
#[allow(clippy::std_instead_of_core)]
fn type_plan_cache() -> &'static std::sync::Mutex<HashMap<&'static Shape, Arc<TypePlanCore>>> {
use std::sync::{Mutex, OnceLock};
static PLAN_CACHE: OnceLock<Mutex<HashMap<&'static Shape, Arc<TypePlanCore>>>> =
OnceLock::new();
PLAN_CACHE.get_or_init(|| Mutex::new(HashMap::new()))
}
pub type NodeId = Idx<TypePlanNode>;
pub type FieldRange = SliceRange<FieldPlan>;
pub type VariantRange = SliceRange<VariantPlanMeta>;
pub type ValidatorRange = SliceRange<PrecomputedValidator>;
#[derive(Debug)]
pub struct TypePlan<T: ?Sized> {
core: Arc<TypePlanCore>,
_marker: core::marker::PhantomData<fn() -> T>,
}
#[derive(Debug)]
pub struct TypePlanCore {
nodes: Arena<TypePlanNode>,
fields: Arena<FieldPlan>,
variants: Arena<VariantPlanMeta>,
validators: Arena<PrecomputedValidator>,
field_entries: Arena<FieldLookupEntry>,
buckets: Arena<(u64, u32, u32)>,
node_lookup: Vec<(ConstTypeId, NodeId)>,
root: NodeId,
}
#[derive(Debug)]
pub struct TypePlanNode {
pub shape: &'static Shape,
pub kind: TypePlanNodeKind,
pub strategy: DeserStrategy,
pub has_default: bool,
pub proxies: ProxyNodes,
}
#[derive(Debug, Clone, Default)]
pub struct ProxyNodes {
pub generic: Option<NodeId>,
pub format_specific: SmallVec<(&'static str, NodeId), 2>,
}
impl ProxyNodes {
#[inline]
pub fn node_for(&self, format: Option<&str>) -> Option<NodeId> {
if let Some(fmt) = format {
if let Some((_, node)) = self.format_specific.iter().find(|(f, _)| *f == fmt) {
return Some(*node);
}
}
self.generic
}
#[inline]
pub fn has_any(&self) -> bool {
self.generic.is_some() || !self.format_specific.is_empty()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.generic.is_none() && self.format_specific.is_empty()
}
}
#[derive(Debug, Clone)]
struct FieldProxies {
generic: Option<&'static ProxyDef>,
format_specific: SmallVec<(&'static str, &'static ProxyDef), 2>,
}
impl FieldProxies {
fn from_field(field: &'static Field) -> Option<Self> {
let generic = field.proxy();
let format_specific: SmallVec<_, 2> = field
.format_proxies
.iter()
.map(|fp| (fp.format, fp.proxy))
.collect();
if generic.is_none() && format_specific.is_empty() {
None
} else {
Some(Self {
generic,
format_specific,
})
}
}
}
#[derive(Debug)]
pub enum DeserStrategy {
ContainerProxy,
FieldProxy,
Pointer {
pointee_node: NodeId,
},
OpaquePointer,
Opaque,
TransparentConvert {
inner_node: NodeId,
},
Scalar {
scalar_type: Option<ScalarType>,
is_from_str: bool,
},
Struct,
Tuple {
field_count: usize,
is_single_field_transparent: bool,
},
Enum,
Option {
some_node: NodeId,
},
Result {
ok_node: NodeId,
err_node: NodeId,
},
List {
item_node: NodeId,
is_byte_vec: bool,
},
Map {
key_node: NodeId,
value_node: NodeId,
},
Set {
item_node: NodeId,
},
Array {
len: usize,
item_node: NodeId,
},
DynamicValue,
MetadataContainer,
BackRef {
target_type_id: ConstTypeId,
},
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)] pub enum TypePlanNodeKind {
Scalar,
Struct(StructPlan),
Enum(EnumPlan),
Option,
Result,
List,
Slice,
Map,
Set,
Array {
len: usize,
},
Pointer,
OpaquePointer,
Opaque,
DynamicValue,
Transparent,
BackRef(ConstTypeId),
}
#[derive(Debug)]
pub struct StructPlan {
pub struct_def: &'static StructType,
pub fields: FieldRange,
pub field_lookup: FieldLookup,
pub has_flatten: bool,
pub deny_unknown_fields: bool,
}
#[derive(Debug, Clone)]
pub struct FieldPlan {
pub field: &'static Field,
pub name: &'static str,
pub effective_name: &'static str,
pub alias: Option<&'static str>,
pub is_flattened: bool,
pub type_node: NodeId,
pub index: usize,
pub offset: usize,
pub field_shape: &'static Shape,
pub fill_rule: FillRule,
pub validators: ValidatorRange,
}
impl FieldPlan {
#[inline]
pub fn has_default(&self) -> bool {
matches!(self.fill_rule, FillRule::Defaultable(_))
}
#[inline]
pub fn is_required(&self) -> bool {
matches!(self.fill_rule, FillRule::Required)
}
}
pub type FieldInitPlan = FieldPlan;
#[derive(Debug, Clone)]
pub enum FillRule {
Defaultable(FieldDefault),
Required,
}
#[derive(Debug, Clone, Copy)]
pub enum FieldDefault {
Custom(DefaultInPlaceFn),
FromTrait(&'static Shape),
}
#[derive(Debug, Clone)]
pub struct PrecomputedValidator {
pub kind: ValidatorKind,
}
impl PrecomputedValidator {
#[allow(unsafe_code)]
pub fn run(
&self,
field_ptr: facet_core::PtrConst,
field_name: &'static str,
container_shape: &'static Shape,
) -> Result<(), crate::ReflectErrorKind> {
use crate::ReflectErrorKind;
use alloc::format;
let result: Result<(), alloc::string::String> = match self.kind {
ValidatorKind::Custom(validator_fn) => {
unsafe { validator_fn(field_ptr) }
}
ValidatorKind::Min { limit, scalar_type } => {
Self::validate_min(field_ptr, limit, scalar_type)
}
ValidatorKind::Max { limit, scalar_type } => {
Self::validate_max(field_ptr, limit, scalar_type)
}
ValidatorKind::MinLength { limit, scalar_type } => {
let len = Self::get_string_length(field_ptr, scalar_type);
if len < limit {
Err(format!("length must be >= {}, got {}", limit, len))
} else {
Ok(())
}
}
ValidatorKind::MaxLength { limit, scalar_type } => {
let len = Self::get_string_length(field_ptr, scalar_type);
if len > limit {
Err(format!("length must be <= {}, got {}", limit, len))
} else {
Ok(())
}
}
ValidatorKind::Email { scalar_type } => {
let s = unsafe { Self::get_string(field_ptr, scalar_type) };
if Self::is_valid_email(s) {
Ok(())
} else {
Err(format!("'{}' is not a valid email address", s))
}
}
ValidatorKind::Url { scalar_type } => {
let s = unsafe { Self::get_string(field_ptr, scalar_type) };
if Self::is_valid_url(s) {
Ok(())
} else {
Err(format!("'{}' is not a valid URL", s))
}
}
ValidatorKind::Regex {
pattern,
scalar_type,
} => {
let s = unsafe { Self::get_string(field_ptr, scalar_type) };
if Self::matches_pattern(s, pattern) {
Ok(())
} else {
Err(format!("'{}' does not match pattern '{}'", s, pattern))
}
}
ValidatorKind::Contains {
needle,
scalar_type,
} => {
let s = unsafe { Self::get_string(field_ptr, scalar_type) };
if s.contains(needle) {
Ok(())
} else {
Err(format!("'{}' does not contain '{}'", s, needle))
}
}
};
result.map_err(|message| ReflectErrorKind::ValidationFailed {
shape: container_shape,
field_name,
message,
})
}
#[allow(unsafe_code)]
fn validate_min(
field_ptr: facet_core::PtrConst,
limit: i64,
scalar_type: ScalarType,
) -> Result<(), alloc::string::String> {
use alloc::format;
match scalar_type {
ScalarType::I8 => {
let v = unsafe { *field_ptr.get::<i8>() } as i64;
if v < limit {
Err(format!("must be >= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::I16 => {
let v = unsafe { *field_ptr.get::<i16>() } as i64;
if v < limit {
Err(format!("must be >= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::I32 => {
let v = unsafe { *field_ptr.get::<i32>() } as i64;
if v < limit {
Err(format!("must be >= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::I64 => {
let v = unsafe { *field_ptr.get::<i64>() };
if v < limit {
Err(format!("must be >= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::U8 => {
let v = unsafe { *field_ptr.get::<u8>() } as i64;
if v < limit {
Err(format!("must be >= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::U16 => {
let v = unsafe { *field_ptr.get::<u16>() } as i64;
if v < limit {
Err(format!("must be >= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::U32 => {
let v = unsafe { *field_ptr.get::<u32>() } as i64;
if v < limit {
Err(format!("must be >= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::U64 => {
let v = unsafe { *field_ptr.get::<u64>() };
if v > i64::MAX as u64 {
Ok(()) } else if (v as i64) < limit {
Err(format!("must be >= {}, got {}", limit, v))
} else {
Ok(())
}
}
_ => Ok(()), }
}
#[allow(unsafe_code)]
fn validate_max(
field_ptr: facet_core::PtrConst,
limit: i64,
scalar_type: ScalarType,
) -> Result<(), alloc::string::String> {
use alloc::format;
match scalar_type {
ScalarType::I8 => {
let v = unsafe { *field_ptr.get::<i8>() } as i64;
if v > limit {
Err(format!("must be <= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::I16 => {
let v = unsafe { *field_ptr.get::<i16>() } as i64;
if v > limit {
Err(format!("must be <= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::I32 => {
let v = unsafe { *field_ptr.get::<i32>() } as i64;
if v > limit {
Err(format!("must be <= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::I64 => {
let v = unsafe { *field_ptr.get::<i64>() };
if v > limit {
Err(format!("must be <= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::U8 => {
let v = unsafe { *field_ptr.get::<u8>() } as i64;
if v > limit {
Err(format!("must be <= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::U16 => {
let v = unsafe { *field_ptr.get::<u16>() } as i64;
if v > limit {
Err(format!("must be <= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::U32 => {
let v = unsafe { *field_ptr.get::<u32>() } as i64;
if v > limit {
Err(format!("must be <= {}, got {}", limit, v))
} else {
Ok(())
}
}
ScalarType::U64 => {
let v = unsafe { *field_ptr.get::<u64>() };
if v > i64::MAX as u64 || (v as i64) > limit {
Err(format!("must be <= {}, got {}", limit, v))
} else {
Ok(())
}
}
_ => Ok(()), }
}
#[allow(unsafe_code)]
fn get_string_length(field_ptr: facet_core::PtrConst, scalar_type: ScalarType) -> usize {
unsafe {
match scalar_type {
ScalarType::String => (*field_ptr.get::<alloc::string::String>()).len(),
ScalarType::CowStr => (*field_ptr.get::<alloc::borrow::Cow<'_, str>>()).len(),
_ => 0, }
}
}
#[allow(unsafe_code)]
unsafe fn get_string(field_ptr: facet_core::PtrConst, scalar_type: ScalarType) -> &'static str {
match scalar_type {
ScalarType::String => {
let s: &str = unsafe { (*field_ptr.get::<alloc::string::String>()).as_str() };
unsafe { core::mem::transmute::<&str, &'static str>(s) }
}
ScalarType::CowStr => {
let s: &str = unsafe { (*field_ptr.get::<alloc::borrow::Cow<'_, str>>()).as_ref() };
unsafe { core::mem::transmute::<&str, &'static str>(s) }
}
_ => "", }
}
fn is_valid_email(s: &str) -> bool {
let parts: Vec<&str> = s.split('@').collect();
parts.len() == 2 && !parts[0].is_empty() && !parts[1].is_empty() && parts[1].contains('.')
}
fn is_valid_url(s: &str) -> bool {
s.starts_with("http://") || s.starts_with("https://")
}
fn matches_pattern(s: &str, pattern: &str) -> bool {
#[cfg(feature = "regex")]
{
regex::Regex::new(pattern)
.map(|re| re.is_match(s))
.unwrap_or(false)
}
#[cfg(not(feature = "regex"))]
{
s.contains(pattern)
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum ValidatorKind {
Custom(ValidatorFn),
Min {
limit: i64,
scalar_type: ScalarType,
},
Max {
limit: i64,
scalar_type: ScalarType,
},
MinLength {
limit: usize,
scalar_type: ScalarType,
},
MaxLength {
limit: usize,
scalar_type: ScalarType,
},
Email {
scalar_type: ScalarType,
},
Url {
scalar_type: ScalarType,
},
Regex {
pattern: &'static str,
scalar_type: ScalarType,
},
Contains {
needle: &'static str,
scalar_type: ScalarType,
},
}
#[derive(Debug)]
pub struct EnumPlan {
pub enum_def: &'static EnumType,
pub variants: VariantRange,
pub variant_lookup: VariantLookup,
pub num_variants: usize,
pub other_variant_idx: Option<usize>,
}
#[derive(Debug, Clone)]
pub struct VariantPlanMeta {
pub variant: &'static Variant,
pub name: &'static str,
pub fields: FieldRange,
pub field_lookup: FieldLookup,
pub has_flatten: bool,
}
#[derive(Debug, Clone)]
#[allow(clippy::large_enum_variant)] pub enum FieldLookup {
Small(SmallVec<FieldLookupEntry, 16>),
PrefixBuckets {
prefix_len: usize,
entries: SliceRange<FieldLookupEntry>,
buckets: SliceRange<(u64, u32, u32)>,
},
}
#[derive(Debug, Clone)]
pub struct FieldLookupEntry {
pub name: &'static str,
pub index: usize,
pub is_alias: bool,
}
#[derive(Debug, Clone)]
#[allow(clippy::large_enum_variant)] pub enum VariantLookup {
Small(SmallVec<(&'static str, usize), 8>),
Sorted(Vec<(&'static str, usize)>),
}
const LOOKUP_THRESHOLD: usize = 8;
#[inline]
fn compute_prefix(name: &str, prefix_len: usize) -> u64 {
let bytes = name.as_bytes();
let actual_len = bytes.len().min(prefix_len);
let mut prefix: u64 = 0;
for (i, &byte) in bytes.iter().take(actual_len).enumerate() {
prefix |= (byte as u64) << (i * 8);
}
prefix
}
impl FieldLookup {
#[inline]
pub fn find(&self, name: &str, core: &TypePlanCore) -> Option<usize> {
match self {
FieldLookup::Small(entries) => entries.iter().find(|e| e.name == name).map(|e| e.index),
FieldLookup::PrefixBuckets {
prefix_len,
entries,
buckets,
} => {
let prefix = compute_prefix(name, *prefix_len);
let bucket_slice = core.buckets.get_slice(*buckets);
let bucket_idx = bucket_slice
.binary_search_by_key(&prefix, |(p, _, _)| *p)
.ok()?;
let (_, start, count) = bucket_slice[bucket_idx];
let all_entries = core.field_entries.get_slice(*entries);
let bucket_entries = &all_entries[start as usize..(start + count) as usize];
bucket_entries
.iter()
.find(|e| e.name == name)
.map(|e| e.index)
}
}
}
#[inline]
pub fn find_small(&self, name: &str) -> Option<usize> {
match self {
FieldLookup::Small(entries) => entries.iter().find(|e| e.name == name).map(|e| e.index),
FieldLookup::PrefixBuckets { .. } => {
panic!("find_small called on PrefixBuckets - use find() with TypePlanCore")
}
}
}
#[inline]
pub fn is_empty(&self) -> bool {
match self {
FieldLookup::Small(entries) => entries.is_empty(),
FieldLookup::PrefixBuckets { entries, .. } => entries.is_empty(),
}
}
}
impl VariantLookup {
#[inline]
pub fn find(&self, name: &str) -> Option<usize> {
match self {
VariantLookup::Small(entries) => {
entries.iter().find(|(n, _)| *n == name).map(|(_, i)| *i)
}
VariantLookup::Sorted(entries) => entries
.binary_search_by_key(&name, |(n, _)| *n)
.ok()
.map(|i| entries[i].1),
}
}
}
struct TypePlanBuilder {
nodes: Arena<TypePlanNode>,
fields: Arena<FieldPlan>,
variants: Arena<VariantPlanMeta>,
validators: Arena<PrecomputedValidator>,
field_entries: Arena<FieldLookupEntry>,
buckets: Arena<(u64, u32, u32)>,
building: hashbrown::HashSet<ConstTypeId>,
finished: HashMap<ConstTypeId, NodeId>,
}
impl TypePlanBuilder {
fn new() -> Self {
Self {
nodes: Arena::new(),
fields: Arena::new(),
variants: Arena::new(),
validators: Arena::new(),
field_entries: Arena::new(),
buckets: Arena::new(),
building: hashbrown::HashSet::new(),
finished: HashMap::new(),
}
}
fn finish(self, root: NodeId) -> TypePlanCore {
let mut node_lookup: Vec<_> = self.finished.into_iter().collect();
node_lookup.sort_by_key(|(id, _)| *id);
TypePlanCore {
nodes: self.nodes,
fields: self.fields,
variants: self.variants,
validators: self.validators,
field_entries: self.field_entries,
buckets: self.buckets,
node_lookup,
root,
}
}
fn build_node(&mut self, shape: &'static Shape) -> Result<NodeId, AllocError> {
self.build_node_with_proxy(shape, None)
}
fn build_node_with_proxy(
&mut self,
shape: &'static Shape,
field_proxies: Option<FieldProxies>,
) -> Result<NodeId, AllocError> {
let type_id = shape.id;
if self.building.contains(&type_id) {
let backref_node = TypePlanNode {
shape,
kind: TypePlanNodeKind::BackRef(type_id),
strategy: DeserStrategy::BackRef {
target_type_id: type_id,
},
has_default: shape.is(Characteristic::Default),
proxies: ProxyNodes::default(), };
let idx = self.nodes.alloc(backref_node);
return Ok(idx);
}
self.building.insert(type_id);
let (proxies, has_container_proxy, has_field_proxy) =
self.build_all_proxy_nodes(shape, field_proxies.as_ref())?;
let (kind, children) = self.build_kind(shape)?;
let strategy = self.compute_strategy(
shape,
&kind,
has_container_proxy,
has_field_proxy,
&children,
)?;
let node = TypePlanNode {
shape,
kind,
strategy,
has_default: shape.is(Characteristic::Default),
proxies,
};
let idx = self.nodes.alloc(node);
self.building.remove(&type_id);
self.finished.insert(type_id, idx);
Ok(idx)
}
fn build_all_proxy_nodes(
&mut self,
shape: &'static Shape,
field_proxies: Option<&FieldProxies>,
) -> Result<(ProxyNodes, bool, bool), AllocError> {
let mut proxies = ProxyNodes::default();
if let Some(fp) = field_proxies {
if let Some(generic_proxy) = fp.generic {
proxies.generic = Some(self.build_node(generic_proxy.shape)?);
}
for &(format, proxy_def) in fp.format_specific.iter() {
let node = self.build_node(proxy_def.shape)?;
proxies.format_specific.push((format, node));
}
let has_field_proxy = proxies.has_any();
return Ok((proxies, false, has_field_proxy));
}
if let Some(generic_proxy) = shape.proxy {
proxies.generic = Some(self.build_node(generic_proxy.shape)?);
}
for format_proxy in shape.format_proxies.iter() {
let node = self.build_node(format_proxy.proxy.shape)?;
proxies.format_specific.push((format_proxy.format, node));
}
let has_container_proxy = proxies.has_any();
Ok((proxies, has_container_proxy, false))
}
fn compute_strategy(
&self,
shape: &'static Shape,
kind: &TypePlanNodeKind,
has_container_proxy: bool,
has_field_proxy: bool,
children: &[NodeId],
) -> Result<DeserStrategy, AllocError> {
let nth_child = |n: usize| -> NodeId { children[n] };
let first_child = || children[0];
if has_field_proxy {
return Ok(DeserStrategy::FieldProxy);
}
if has_container_proxy {
return Ok(DeserStrategy::ContainerProxy);
}
if matches!(kind, TypePlanNodeKind::Pointer) {
return Ok(DeserStrategy::Pointer {
pointee_node: first_child(),
});
}
if shape.is_metadata_container() {
return Ok(DeserStrategy::MetadataContainer);
}
if shape.inner.is_some()
&& shape.vtable.has_try_from()
&& !matches!(
&shape.def,
Def::List(_) | Def::Map(_) | Def::Set(_) | Def::Array(_)
)
{
return Ok(DeserStrategy::TransparentConvert {
inner_node: first_child(),
});
}
if matches!(kind, TypePlanNodeKind::Transparent) && shape.vtable.has_try_from() {
return Ok(DeserStrategy::TransparentConvert {
inner_node: first_child(),
});
}
if matches!(&shape.def, Def::Scalar) && shape.vtable.has_parse() {
return Ok(DeserStrategy::Scalar {
scalar_type: shape.scalar_type(),
is_from_str: shape.vtable.has_parse(),
});
}
Ok(match kind {
TypePlanNodeKind::Scalar => {
if let Type::User(UserType::Struct(struct_def)) = &shape.ty {
use facet_core::StructKind;
if matches!(struct_def.kind, StructKind::Tuple | StructKind::TupleStruct) {
let field_count = struct_def.fields.len();
return Ok(DeserStrategy::Tuple {
field_count,
is_single_field_transparent: field_count == 1 && shape.is_transparent(),
});
}
}
DeserStrategy::Scalar {
scalar_type: shape.scalar_type(),
is_from_str: shape.vtable.has_parse(),
}
}
TypePlanNodeKind::Struct(struct_plan) => {
use facet_core::StructKind;
match struct_plan.struct_def.kind {
StructKind::Tuple | StructKind::TupleStruct => {
let field_count = struct_plan.struct_def.fields.len();
DeserStrategy::Tuple {
field_count,
is_single_field_transparent: field_count == 1 && shape.is_transparent(),
}
}
StructKind::Struct | StructKind::Unit => DeserStrategy::Struct,
}
}
TypePlanNodeKind::Enum(_) => DeserStrategy::Enum,
TypePlanNodeKind::Option => DeserStrategy::Option {
some_node: first_child(),
},
TypePlanNodeKind::Result => DeserStrategy::Result {
ok_node: nth_child(0),
err_node: nth_child(1),
},
TypePlanNodeKind::List | TypePlanNodeKind::Slice => {
let is_byte_vec = *shape == *<alloc::vec::Vec<u8> as facet_core::Facet>::SHAPE;
DeserStrategy::List {
item_node: first_child(),
is_byte_vec,
}
}
TypePlanNodeKind::Map => DeserStrategy::Map {
key_node: nth_child(0),
value_node: nth_child(1),
},
TypePlanNodeKind::Set => DeserStrategy::Set {
item_node: first_child(),
},
TypePlanNodeKind::Array { len } => DeserStrategy::Array {
len: *len,
item_node: first_child(),
},
TypePlanNodeKind::DynamicValue => DeserStrategy::DynamicValue,
TypePlanNodeKind::Pointer => DeserStrategy::Pointer {
pointee_node: first_child(),
},
TypePlanNodeKind::OpaquePointer => DeserStrategy::OpaquePointer,
TypePlanNodeKind::Opaque => DeserStrategy::Opaque,
TypePlanNodeKind::Transparent => {
return Err(AllocError {
shape,
operation: "transparent wrapper requires try_from for deserialization",
});
}
TypePlanNodeKind::BackRef(type_id) => DeserStrategy::BackRef {
target_type_id: *type_id,
},
})
}
fn build_kind(
&mut self,
shape: &'static Shape,
) -> Result<(TypePlanNodeKind, Vec<NodeId>), AllocError> {
let mut children = Vec::new();
let kind = match &shape.def {
Def::Scalar => {
if let Some(inner_shape) = shape.inner {
children.push(self.build_node(inner_shape)?);
}
TypePlanNodeKind::Scalar
}
Def::Option(opt_def) => {
children.push(self.build_node(opt_def.t())?);
TypePlanNodeKind::Option
}
Def::Result(res_def) => {
children.push(self.build_node(res_def.t())?);
children.push(self.build_node(res_def.e())?);
TypePlanNodeKind::Result
}
Def::List(list_def) => {
children.push(self.build_node(list_def.t())?);
TypePlanNodeKind::List
}
Def::Map(map_def) => {
children.push(self.build_node(map_def.k())?);
children.push(self.build_node(map_def.v())?);
TypePlanNodeKind::Map
}
Def::Set(set_def) => {
children.push(self.build_node(set_def.t())?);
TypePlanNodeKind::Set
}
Def::Array(arr_def) => {
children.push(self.build_node(arr_def.t())?);
TypePlanNodeKind::Array { len: arr_def.n }
}
Def::Pointer(ptr_def) => {
if let Some(pointee) = ptr_def.pointee() {
children.push(self.build_node(pointee)?);
TypePlanNodeKind::Pointer
} else {
TypePlanNodeKind::OpaquePointer
}
}
Def::DynamicValue(_) => TypePlanNodeKind::DynamicValue,
_ => {
match &shape.ty {
Type::User(UserType::Struct(struct_type)) => {
let struct_plan = self.build_struct_plan(shape, struct_type)?;
return Ok((TypePlanNodeKind::Struct(struct_plan), Vec::new()));
}
Type::User(UserType::Enum(enum_type)) => {
TypePlanNodeKind::Enum(self.build_enum_plan(enum_type)?)
}
Type::Sequence(SequenceType::Slice(slice_type)) => {
children.push(self.build_node(slice_type.t)?);
TypePlanNodeKind::Slice
}
Type::User(UserType::Opaque) | Type::Undefined
if shape.builder_shape.is_some() && shape.vtable.has_try_from() =>
{
children.push(self.build_node(shape.builder_shape.unwrap())?);
TypePlanNodeKind::Transparent
}
Type::User(UserType::Opaque) | Type::Undefined => TypePlanNodeKind::Opaque,
_ => {
if let Some(inner) = shape.inner {
children.push(self.build_node(inner)?);
TypePlanNodeKind::Transparent
} else {
return Err(AllocError {
shape,
operation: "unsupported type for deserialization",
});
}
}
}
}
};
Ok((kind, children))
}
fn build_struct_plan(
&mut self,
shape: &'static Shape,
struct_def: &'static StructType,
) -> Result<StructPlan, AllocError> {
let mut field_plans = Vec::with_capacity(struct_def.fields.len());
let container_has_default = shape.is(Characteristic::Default);
for (index, field) in struct_def.fields.iter().enumerate() {
let field_proxies = FieldProxies::from_field(field);
let child_node = self.build_node_with_proxy(field.shape(), field_proxies)?;
let validators = self.extract_validators(field);
let fill_rule = Self::determine_fill_rule(field, container_has_default);
field_plans
.push(self.create_field_plan(index, field, child_node, fill_rule, validators));
}
let has_flatten = field_plans.iter().any(|f| f.is_flattened);
let fields = self.fields.alloc_extend(field_plans.iter().cloned());
let field_lookup = self.build_field_lookup(&field_plans);
let deny_unknown_fields = shape.has_deny_unknown_fields_attr();
Ok(StructPlan {
struct_def,
fields,
field_lookup,
has_flatten,
deny_unknown_fields,
})
}
fn create_field_plan(
&mut self,
index: usize,
field: &'static Field,
type_node: NodeId,
fill_rule: FillRule,
validators: ValidatorRange,
) -> FieldPlan {
let name = field.name;
let effective_name = field.effective_name();
let alias = field.alias;
let is_flattened = field.is_flattened();
FieldPlan {
field,
name,
effective_name,
alias,
is_flattened,
type_node,
index,
offset: field.offset,
field_shape: field.shape(),
fill_rule,
validators,
}
}
fn build_field_lookup(&mut self, field_plans: &[FieldPlan]) -> FieldLookup {
let mut entries: Vec<FieldLookupEntry> = Vec::with_capacity(field_plans.len() * 2);
for (index, field_plan) in field_plans.iter().enumerate() {
entries.push(FieldLookupEntry {
name: field_plan.effective_name,
index,
is_alias: false,
});
if let Some(alias) = field_plan.alias {
entries.push(FieldLookupEntry {
name: alias,
index,
is_alias: true,
});
}
}
self.build_field_lookup_from_entries(entries)
}
fn build_field_lookup_from_entries(&mut self, entries: Vec<FieldLookupEntry>) -> FieldLookup {
let total_entries = entries.len();
if total_entries <= LOOKUP_THRESHOLD {
return FieldLookup::Small(entries.into_iter().collect());
}
let long_key_count = entries.iter().filter(|e| e.name.len() >= 8).count();
let prefix_len = if long_key_count > total_entries / 2 {
8
} else {
4
};
let mut prefix_map: hashbrown::HashMap<u64, Vec<FieldLookupEntry>> =
hashbrown::HashMap::new();
for entry in entries {
let prefix = compute_prefix(entry.name, prefix_len);
prefix_map.entry(prefix).or_default().push(entry);
}
let mut bucket_list: Vec<_> = prefix_map.into_iter().collect();
bucket_list.sort_by_key(|(prefix, _)| *prefix);
let mut all_entries = Vec::with_capacity(total_entries);
let mut bucket_data = Vec::with_capacity(bucket_list.len());
for (prefix, bucket_entries) in bucket_list {
let start = all_entries.len() as u32;
let count = bucket_entries.len() as u32;
bucket_data.push((prefix, start, count));
all_entries.extend(bucket_entries);
}
let entries_range = self.field_entries.alloc_extend(all_entries);
let buckets_range = self.buckets.alloc_extend(bucket_data);
FieldLookup::PrefixBuckets {
prefix_len,
entries: entries_range,
buckets: buckets_range,
}
}
fn determine_fill_rule(field: &'static Field, container_has_default: bool) -> FillRule {
let field_shape = field.shape();
if let Some(default_source) = field.default_source() {
let field_default = match default_source {
DefaultSource::Custom(f) => FieldDefault::Custom(*f),
DefaultSource::FromTrait => FieldDefault::FromTrait(field_shape),
};
return FillRule::Defaultable(field_default);
}
let is_option = matches!(field_shape.def, Def::Option(_));
if is_option && field_shape.is(Characteristic::Default) {
return FillRule::Defaultable(FieldDefault::FromTrait(field_shape));
}
if field.should_skip_deserializing() && field_shape.is(Characteristic::Default) {
return FillRule::Defaultable(FieldDefault::FromTrait(field_shape));
}
if let Type::User(UserType::Struct(struct_type)) = field_shape.ty
&& struct_type.fields.is_empty()
&& field_shape.is(Characteristic::Default)
{
return FillRule::Defaultable(FieldDefault::FromTrait(field_shape));
}
if container_has_default && field_shape.is(Characteristic::Default) {
return FillRule::Defaultable(FieldDefault::FromTrait(field_shape));
}
FillRule::Required
}
fn extract_validators(&mut self, field: &'static Field) -> ValidatorRange {
let mut validators = Vec::new();
let field_shape = field.shape();
let scalar_type = field_shape.scalar_type();
for attr in field.attributes.iter() {
if attr.ns != Some("validate") {
continue;
}
let kind = match attr.key {
"custom" => {
let validator_fn = unsafe { *attr.data.ptr().get::<ValidatorFn>() };
ValidatorKind::Custom(validator_fn)
}
"min" => {
let limit = *attr
.get_as::<i64>()
.expect("validate::min attribute must contain i64");
let scalar_type =
scalar_type.expect("validate::min requires numeric field type");
ValidatorKind::Min { limit, scalar_type }
}
"max" => {
let limit = *attr
.get_as::<i64>()
.expect("validate::max attribute must contain i64");
let scalar_type =
scalar_type.expect("validate::max requires numeric field type");
ValidatorKind::Max { limit, scalar_type }
}
"min_length" => {
let limit = *attr
.get_as::<usize>()
.expect("validate::min_length attribute must contain usize");
let scalar_type =
scalar_type.expect("validate::min_length requires string field type");
ValidatorKind::MinLength { limit, scalar_type }
}
"max_length" => {
let limit = *attr
.get_as::<usize>()
.expect("validate::max_length attribute must contain usize");
let scalar_type =
scalar_type.expect("validate::max_length requires string field type");
ValidatorKind::MaxLength { limit, scalar_type }
}
"email" => {
let scalar_type =
scalar_type.expect("validate::email requires string field type");
ValidatorKind::Email { scalar_type }
}
"url" => {
let scalar_type =
scalar_type.expect("validate::url requires string field type");
ValidatorKind::Url { scalar_type }
}
"regex" => {
let pattern = *attr
.get_as::<&'static str>()
.expect("validate::regex attribute must contain &'static str");
let scalar_type =
scalar_type.expect("validate::regex requires string field type");
ValidatorKind::Regex {
pattern,
scalar_type,
}
}
"contains" => {
let needle = *attr
.get_as::<&'static str>()
.expect("validate::contains attribute must contain &'static str");
let scalar_type =
scalar_type.expect("validate::contains requires string field type");
ValidatorKind::Contains {
needle,
scalar_type,
}
}
_ => continue, };
validators.push(PrecomputedValidator { kind });
}
self.validators.alloc_extend(validators)
}
fn build_enum_plan(&mut self, enum_def: &'static EnumType) -> Result<EnumPlan, AllocError> {
let mut variant_metas = Vec::with_capacity(enum_def.variants.len());
for variant in enum_def.variants.iter() {
let mut field_plans = Vec::with_capacity(variant.data.fields.len());
for (index, field) in variant.data.fields.iter().enumerate() {
let field_proxies = FieldProxies::from_field(field);
let child_node = self.build_node_with_proxy(field.shape(), field_proxies)?;
let validators = self.extract_validators(field);
let fill_rule = Self::determine_fill_rule(field, false);
field_plans
.push(self.create_field_plan(index, field, child_node, fill_rule, validators));
}
let has_flatten = field_plans.iter().any(|f| f.is_flattened);
let fields = self.fields.alloc_extend(field_plans.iter().cloned());
let field_lookup = self.build_field_lookup(&field_plans);
variant_metas.push(VariantPlanMeta {
variant,
name: variant.effective_name(),
fields,
field_lookup,
has_flatten,
});
}
let variants = self.variants.alloc_extend(variant_metas.iter().cloned());
let variant_lookup = self.build_variant_lookup(&variant_metas);
let num_variants = variant_metas.len();
let other_variant_idx = variant_metas.iter().position(|v| v.variant.is_other());
Ok(EnumPlan {
enum_def,
variants,
variant_lookup,
num_variants,
other_variant_idx,
})
}
fn build_variant_lookup(&self, variants: &[VariantPlanMeta]) -> VariantLookup {
let entries: Vec<_> = variants
.iter()
.enumerate()
.filter(|(_, v)| !v.variant.is_other())
.map(|(i, v)| (v.name, i))
.collect();
if entries.len() <= LOOKUP_THRESHOLD {
VariantLookup::Small(entries.into_iter().collect())
} else {
let mut sorted = entries;
sorted.sort_by_key(|(name, _)| *name);
VariantLookup::Sorted(sorted)
}
}
}
impl<'facet, T: facet_core::Facet<'facet> + ?Sized> TypePlan<T> {
pub fn build() -> Result<Self, AllocError> {
let core = unsafe { TypePlanCore::from_shape(T::SHAPE)? };
Ok(TypePlan {
core,
_marker: core::marker::PhantomData,
})
}
#[deprecated(
since = "0.44.0",
note = "format namespace no longer needed at build time; use build() instead"
)]
pub fn build_for_format(_format_namespace: Option<&'static str>) -> Result<Self, AllocError> {
Self::build()
}
#[inline]
pub fn core(&self) -> Arc<TypePlanCore> {
self.core.clone()
}
#[inline]
pub fn root(&self) -> &TypePlanNode {
self.core.root()
}
}
impl TypePlanCore {
pub unsafe fn from_shape(shape: &'static Shape) -> Result<Arc<Self>, AllocError> {
#[cfg(feature = "std")]
{
let mut guard = type_plan_cache()
.lock()
.unwrap_or_else(|poison| poison.into_inner());
if let Some(plan) = guard.get(&shape) {
return Ok(Arc::clone(plan));
}
let plan = unsafe { Self::build_uncached(shape)? };
guard.insert(shape, Arc::clone(&plan));
Ok(plan)
}
#[cfg(not(feature = "std"))]
{
unsafe { Self::build_uncached(shape) }
}
}
unsafe fn build_uncached(shape: &'static Shape) -> Result<Arc<Self>, AllocError> {
let mut builder = TypePlanBuilder::new();
let root = builder.build_node(shape)?;
Ok(Arc::new(builder.finish(root)))
}
#[inline]
pub fn root(&self) -> &TypePlanNode {
self.node(self.root)
}
#[inline]
pub fn root_id(&self) -> NodeId {
self.root
}
#[inline]
pub fn node(&self, idx: NodeId) -> &TypePlanNode {
self.nodes.get(idx)
}
#[inline]
pub fn field(&self, idx: Idx<FieldPlan>) -> &FieldPlan {
self.fields.get(idx)
}
#[inline]
pub fn fields(&self, range: FieldRange) -> &[FieldPlan] {
self.fields.get_slice(range)
}
#[inline]
pub fn variant(&self, idx: Idx<VariantPlanMeta>) -> &VariantPlanMeta {
self.variants.get(idx)
}
#[inline]
pub fn variants(&self, range: VariantRange) -> &[VariantPlanMeta] {
self.variants.get_slice(range)
}
#[inline]
pub fn validators(&self, range: ValidatorRange) -> &[PrecomputedValidator] {
self.validators.get_slice(range)
}
#[inline]
fn lookup_node(&self, type_id: &ConstTypeId) -> Option<NodeId> {
let idx = self
.node_lookup
.binary_search_by_key(type_id, |(id, _)| *id)
.ok()?;
Some(self.node_lookup[idx].1)
}
#[inline]
pub fn struct_field_node(&self, parent: &TypePlanNode, idx: usize) -> Option<&TypePlanNode> {
let resolved = self.resolve_backref(parent);
let struct_plan = match &resolved.kind {
TypePlanNodeKind::Struct(p) => p,
_ => return None,
};
let fields = self.fields(struct_plan.fields);
Some(self.node(fields.get(idx)?.type_node))
}
#[inline]
pub fn enum_variant_field_node(
&self,
parent: &TypePlanNode,
variant_idx: usize,
field_idx: usize,
) -> Option<&TypePlanNode> {
let resolved = self.resolve_backref(parent);
let enum_plan = match &resolved.kind {
TypePlanNodeKind::Enum(p) => p,
_ => return None,
};
let variants = self.variants(enum_plan.variants);
let variant = variants.get(variant_idx)?;
let fields = self.fields(variant.fields);
Some(self.node(fields.get(field_idx)?.type_node))
}
#[inline]
pub fn list_item_node(&self, parent: &TypePlanNode) -> Option<&TypePlanNode> {
match &parent.strategy {
DeserStrategy::List { item_node, .. } | DeserStrategy::Array { item_node, .. } => {
Some(self.node(*item_node))
}
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.list_item_node(self.node(target))
}
_ => None,
}
}
#[inline]
pub fn set_item_node(&self, parent: &TypePlanNode) -> Option<&TypePlanNode> {
match &parent.strategy {
DeserStrategy::Set { item_node } => Some(self.node(*item_node)),
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.set_item_node(self.node(target))
}
_ => None,
}
}
#[inline]
pub fn map_key_node(&self, parent: &TypePlanNode) -> Option<&TypePlanNode> {
match &parent.strategy {
DeserStrategy::Map { key_node, .. } => Some(self.node(*key_node)),
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.map_key_node(self.node(target))
}
_ => None,
}
}
#[inline]
pub fn map_value_node(&self, parent: &TypePlanNode) -> Option<&TypePlanNode> {
match &parent.strategy {
DeserStrategy::Map { value_node, .. } => Some(self.node(*value_node)),
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.map_value_node(self.node(target))
}
_ => None,
}
}
#[inline]
pub fn option_inner_node(&self, parent: &TypePlanNode) -> Option<&TypePlanNode> {
match &parent.strategy {
DeserStrategy::Option { some_node } => Some(self.node(*some_node)),
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.option_inner_node(self.node(target))
}
_ => None,
}
}
#[inline]
pub fn result_ok_node(&self, parent: &TypePlanNode) -> Option<&TypePlanNode> {
match &parent.strategy {
DeserStrategy::Result { ok_node, .. } => Some(self.node(*ok_node)),
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.result_ok_node(self.node(target))
}
_ => None,
}
}
#[inline]
pub fn result_err_node(&self, parent: &TypePlanNode) -> Option<&TypePlanNode> {
match &parent.strategy {
DeserStrategy::Result { err_node, .. } => Some(self.node(*err_node)),
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.result_err_node(self.node(target))
}
_ => None,
}
}
#[inline]
pub fn pointer_pointee_node(&self, parent: &TypePlanNode) -> Option<&TypePlanNode> {
match &parent.strategy {
DeserStrategy::Pointer { pointee_node } => Some(self.node(*pointee_node)),
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.pointer_pointee_node(self.node(target))
}
_ => None,
}
}
#[inline]
pub fn inner_node(&self, parent: &TypePlanNode) -> Option<&TypePlanNode> {
if parent.shape.inner.is_some() {
match &parent.strategy {
DeserStrategy::TransparentConvert { inner_node } => Some(self.node(*inner_node)),
_ => None,
}
} else {
None
}
}
#[inline]
pub fn resolve_backref<'a>(&'a self, node: &'a TypePlanNode) -> &'a TypePlanNode {
match &node.kind {
TypePlanNodeKind::BackRef(type_id) => self.node(
self.lookup_node(type_id)
.expect("BackRef target must exist in node_lookup"),
),
_ => node,
}
}
#[inline]
pub fn as_struct_plan<'a>(&'a self, node: &'a TypePlanNode) -> Option<&'a StructPlan> {
let resolved = self.resolve_backref(node);
match &resolved.kind {
TypePlanNodeKind::Struct(plan) => Some(plan),
_ => None,
}
}
#[inline]
pub fn as_enum_plan<'a>(&'a self, node: &'a TypePlanNode) -> Option<&'a EnumPlan> {
let resolved = self.resolve_backref(node);
match &resolved.kind {
TypePlanNodeKind::Enum(plan) => Some(plan),
_ => None,
}
}
#[inline]
pub fn resolve_backref_id(&self, node_id: NodeId) -> &TypePlanNode {
let node = self.node(node_id);
self.resolve_backref(node)
}
#[inline]
pub fn struct_plan_by_id(&self, node_id: NodeId) -> Option<&StructPlan> {
self.as_struct_plan(self.node(node_id))
}
#[inline]
pub fn enum_plan_by_id(&self, node_id: NodeId) -> Option<&EnumPlan> {
self.as_enum_plan(self.node(node_id))
}
#[inline]
pub fn struct_field_node_id(&self, parent_id: NodeId, idx: usize) -> Option<NodeId> {
let parent = self.node(parent_id);
let resolved = self.resolve_backref(parent);
let struct_plan = match &resolved.kind {
TypePlanNodeKind::Struct(p) => p,
_ => return None,
};
let fields = self.fields(struct_plan.fields);
Some(fields.get(idx)?.type_node)
}
#[inline]
pub fn enum_variant_field_node_id(
&self,
parent_id: NodeId,
variant_idx: usize,
field_idx: usize,
) -> Option<NodeId> {
let parent = self.node(parent_id);
let resolved = self.resolve_backref(parent);
let enum_plan = match &resolved.kind {
TypePlanNodeKind::Enum(p) => p,
_ => return None,
};
let variants = self.variants(enum_plan.variants);
let variant = variants.get(variant_idx)?;
let fields = self.fields(variant.fields);
Some(fields.get(field_idx)?.type_node)
}
#[inline]
pub fn list_item_node_id(&self, parent_id: NodeId) -> Option<NodeId> {
let parent = self.node(parent_id);
match &parent.strategy {
DeserStrategy::List { item_node, .. } | DeserStrategy::Array { item_node, .. } => {
Some(*item_node)
}
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.list_item_node_id(target)
}
_ => None,
}
}
#[inline]
pub fn set_item_node_id(&self, parent_id: NodeId) -> Option<NodeId> {
let parent = self.node(parent_id);
match &parent.strategy {
DeserStrategy::Set { item_node } => Some(*item_node),
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.set_item_node_id(target)
}
_ => None,
}
}
#[inline]
pub fn map_key_node_id(&self, parent_id: NodeId) -> Option<NodeId> {
let parent = self.node(parent_id);
match &parent.strategy {
DeserStrategy::Map { key_node, .. } => Some(*key_node),
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.map_key_node_id(target)
}
_ => None,
}
}
#[inline]
pub fn map_value_node_id(&self, parent_id: NodeId) -> Option<NodeId> {
let parent = self.node(parent_id);
match &parent.strategy {
DeserStrategy::Map { value_node, .. } => Some(*value_node),
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.map_value_node_id(target)
}
_ => None,
}
}
#[inline]
pub fn option_some_node_id(&self, parent_id: NodeId) -> Option<NodeId> {
let parent = self.node(parent_id);
match &parent.strategy {
DeserStrategy::Option { some_node, .. } => Some(*some_node),
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.option_some_node_id(target)
}
_ => None,
}
}
#[inline]
pub fn result_nodes_id(&self, parent_id: NodeId) -> Option<(NodeId, NodeId)> {
let parent = self.node(parent_id);
match &parent.strategy {
DeserStrategy::Result {
ok_node, err_node, ..
} => Some((*ok_node, *err_node)),
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.result_nodes_id(target)
}
_ => None,
}
}
#[inline]
pub fn pointer_inner_node_id(&self, parent_id: NodeId) -> Option<NodeId> {
let parent = self.node(parent_id);
match &parent.strategy {
DeserStrategy::Pointer { pointee_node, .. } => Some(*pointee_node),
DeserStrategy::BackRef { target_type_id } => {
let target = self.lookup_node(target_type_id)?;
self.pointer_inner_node_id(target)
}
_ => None,
}
}
#[inline]
pub fn inner_node_id(&self, parent_id: NodeId) -> Option<NodeId> {
let parent = self.node(parent_id);
if parent.shape.inner.is_some() {
match &parent.strategy {
DeserStrategy::TransparentConvert { inner_node } => Some(*inner_node),
_ => None,
}
} else {
None
}
}
pub(crate) fn empty() -> TypePlanCore {
TypePlanCore {
nodes: Arena::new(),
fields: Arena::new(),
variants: Arena::new(),
validators: Arena::new(),
field_entries: Arena::new(),
buckets: Arena::new(),
node_lookup: Vec::new(),
root: NodeId::new(0),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use facet::Facet;
#[derive(Facet)]
struct TestStruct {
name: String,
age: u32,
email: Option<String>,
}
#[derive(Facet)]
#[repr(u8)]
#[allow(dead_code)] enum TestEnum {
Unit,
Tuple(u32),
Struct { value: String },
}
#[derive(Facet)]
struct RecursiveStruct {
value: u32,
next: Option<Box<RecursiveStruct>>,
}
#[test]
fn test_typeplan_build_reuses_cached_core() {
#[derive(Facet)]
struct CacheProbe {
value: u32,
}
let first = TypePlan::<CacheProbe>::build().unwrap().core();
let second = TypePlan::<CacheProbe>::build().unwrap().core();
assert!(Arc::ptr_eq(&first, &second));
let third =
unsafe { TypePlanCore::from_shape(<CacheProbe as facet_core::Facet<'static>>::SHAPE) }
.unwrap();
assert!(Arc::ptr_eq(&first, &third));
}
#[test]
fn test_typeplan_struct() {
let plan = TypePlan::<TestStruct>::build().unwrap();
let root = plan.root();
let core = plan.core();
assert_eq!(root.shape, TestStruct::SHAPE);
assert!(!root.has_default);
match &root.kind {
TypePlanNodeKind::Struct(struct_plan) => {
let fields = core.fields(struct_plan.fields);
assert_eq!(fields.len(), 3);
assert!(!struct_plan.has_flatten);
assert_eq!(struct_plan.field_lookup.find("name", &core), Some(0));
assert_eq!(struct_plan.field_lookup.find("age", &core), Some(1));
assert_eq!(struct_plan.field_lookup.find("email", &core), Some(2));
assert_eq!(struct_plan.field_lookup.find("unknown", &core), None);
assert_eq!(fields[0].name, "name");
assert!(fields[0].is_required());
assert_eq!(fields[1].name, "age");
assert!(fields[1].is_required());
assert_eq!(fields[2].name, "email");
assert!(!fields[2].is_required());
let email_node = core.struct_field_node(plan.root(), 2).unwrap();
match &email_node.kind {
TypePlanNodeKind::Option => {
let inner_node = core.option_inner_node(email_node).unwrap();
match &inner_node.kind {
TypePlanNodeKind::Scalar => {}
other => panic!("Expected Scalar for String, got {:?}", other),
}
}
other => panic!("Expected Option, got {:?}", other),
}
}
other => panic!("Expected Struct, got {:?}", other),
}
}
#[test]
fn test_typeplan_enum() {
let plan = TypePlan::<TestEnum>::build().unwrap();
let root = plan.root();
let core = plan.core();
assert_eq!(root.shape, TestEnum::SHAPE);
match &root.kind {
TypePlanNodeKind::Enum(enum_plan) => {
let variants = core.variants(enum_plan.variants);
assert_eq!(enum_plan.num_variants, 3);
assert_eq!(enum_plan.variant_lookup.find("Unit"), Some(0));
assert_eq!(enum_plan.variant_lookup.find("Tuple"), Some(1));
assert_eq!(enum_plan.variant_lookup.find("Struct"), Some(2));
assert_eq!(enum_plan.variant_lookup.find("Unknown"), None);
assert!(core.fields(variants[0].fields).is_empty());
assert_eq!(core.fields(variants[1].fields).len(), 1);
let struct_variant_fields = core.fields(variants[2].fields);
assert_eq!(struct_variant_fields.len(), 1);
assert_eq!(variants[2].field_lookup.find("value", &core), Some(0));
}
other => panic!("Expected Enum, got {:?}", other),
}
}
#[test]
fn test_typeplan_list() {
let plan = TypePlan::<Vec<u32>>::build().unwrap();
let root = plan.root();
let core = plan.core();
match &root.kind {
TypePlanNodeKind::List => {
let item_node = core.list_item_node(plan.root()).unwrap();
match &item_node.kind {
TypePlanNodeKind::Scalar => {}
other => panic!("Expected Scalar for u32, got {:?}", other),
}
}
other => panic!("Expected List, got {:?}", other),
}
}
#[test]
fn test_typeplan_recursive() {
let plan = TypePlan::<RecursiveStruct>::build().unwrap();
let root = plan.root();
let core = plan.core();
match &root.kind {
TypePlanNodeKind::Struct(struct_plan) => {
let fields = core.fields(struct_plan.fields);
assert_eq!(fields.len(), 2);
assert_eq!(fields[0].name, "value");
assert_eq!(fields[1].name, "next");
let next_node = core.struct_field_node(plan.root(), 1).unwrap();
assert!(matches!(next_node.kind, TypePlanNodeKind::Option));
let inner_node = core.option_inner_node(next_node).unwrap();
assert!(matches!(inner_node.kind, TypePlanNodeKind::Pointer));
let pointee_node = core.pointer_pointee_node(inner_node).unwrap();
match &pointee_node.kind {
TypePlanNodeKind::BackRef(type_id) => {
assert_eq!(type_id, &plan.root().shape.id);
}
_ => panic!(
"Expected BackRef for recursive type, got {:?}",
pointee_node.kind
),
}
}
other => panic!("Expected Struct, got {:?}", other),
}
}
#[test]
fn test_field_lookup_small() {
let lookup = FieldLookup::Small(smallvec::smallvec![
FieldLookupEntry {
name: "foo",
index: 0,
is_alias: false,
},
FieldLookupEntry {
name: "bar",
index: 1,
is_alias: false,
},
FieldLookupEntry {
name: "baz",
index: 2,
is_alias: false,
},
]);
assert_eq!(lookup.find_small("foo"), Some(0));
assert_eq!(lookup.find_small("bar"), Some(1));
assert_eq!(lookup.find_small("baz"), Some(2));
assert_eq!(lookup.find_small("qux"), None);
}
#[test]
fn test_variant_lookup_small() {
let lookup = VariantLookup::Small(smallvec::smallvec![("A", 0), ("B", 1), ("C", 2)]);
assert_eq!(lookup.find("A"), Some(0));
assert_eq!(lookup.find("B"), Some(1));
assert_eq!(lookup.find("C"), Some(2));
assert_eq!(lookup.find("D"), None);
}
}