use facet_core::{Def, Shape, StructType, Type, UserType};
use crate::jit::Tier2Incompatibility;
pub fn ensure_format_jit_compatible(
shape: &'static Shape,
type_name: &'static str,
) -> Result<(), Tier2Incompatibility> {
ensure_format_jit_compatible_with_encoding(shape, crate::jit::StructEncoding::Map, type_name)
}
pub fn ensure_format_jit_compatible_with_encoding(
shape: &'static Shape,
encoding: crate::jit::StructEncoding,
type_name: &'static str,
) -> Result<(), Tier2Incompatibility> {
#[cfg(not(target_pointer_width = "64"))]
{
return Err(Tier2Incompatibility::Not64BitPlatform);
}
#[cfg(target_pointer_width = "64")]
{
use facet_core::ScalarType;
if let Def::List(list_def) = &shape.def {
return ensure_format_jit_element_supported(list_def.t, type_name);
}
if let Def::Map(map_def) = &shape.def {
if map_def.k.scalar_type() != Some(ScalarType::String) {
return Err(Tier2Incompatibility::MapNonStringKey { type_name });
}
return ensure_format_jit_element_supported(map_def.v, type_name);
}
if let Type::User(UserType::Struct(struct_def)) = &shape.ty {
return ensure_format_jit_struct_supported_with_encoding(
struct_def, encoding, type_name,
);
}
if let Type::User(UserType::Enum(enum_def)) = &shape.ty {
if encoding != crate::jit::StructEncoding::Positional {
return Err(Tier2Incompatibility::EnumRequiresPositionalFormat { type_name });
}
return ensure_format_jit_enum_supported(enum_def, type_name);
}
Err(Tier2Incompatibility::UnrecognizedShapeType { type_name })
}
}
fn ensure_format_jit_struct_supported(
struct_def: &StructType,
type_name: &'static str,
) -> Result<(), Tier2Incompatibility> {
ensure_format_jit_struct_supported_with_encoding(
struct_def,
crate::jit::StructEncoding::Map,
type_name,
)
}
fn ensure_format_jit_struct_supported_with_encoding(
struct_def: &StructType,
encoding: crate::jit::StructEncoding,
type_name: &'static str,
) -> Result<(), Tier2Incompatibility> {
use facet_core::StructKind;
match encoding {
crate::jit::StructEncoding::Map => {
if !matches!(struct_def.kind, StructKind::Struct) {
return Err(Tier2Incompatibility::TupleStructWithMapFormat { type_name });
}
}
crate::jit::StructEncoding::Positional => {
if !matches!(
struct_def.kind,
StructKind::Struct | StructKind::TupleStruct | StructKind::Unit
) {
return Err(Tier2Incompatibility::TupleStructWithMapFormat { type_name });
}
}
}
for field in struct_def.fields {
if field.is_flattened() {
let field_shape = field.shape();
if let Def::Map(map_def) = &field_shape.def {
if map_def.k.scalar_type() != Some(facet_core::ScalarType::String) {
return Err(Tier2Incompatibility::FlattenedMapNonStringKey {
type_name,
field_name: field.name,
});
}
ensure_format_jit_element_supported(map_def.v, type_name)?;
continue;
}
match &field_shape.ty {
facet_core::Type::User(facet_core::UserType::Enum(enum_type)) => {
ensure_format_jit_flattened_enum_supported(enum_type, type_name)?;
continue;
}
facet_core::Type::User(facet_core::UserType::Struct(inner_struct)) => {
ensure_format_jit_struct_supported(inner_struct, type_name)?;
continue;
}
_ => {
return Err(Tier2Incompatibility::UnsupportedFlattenType {
type_name,
field_name: field.name,
});
}
}
}
if field.has_default() {
return Err(Tier2Incompatibility::FieldHasCustomDefault {
type_name,
field_name: field.name,
});
}
ensure_format_jit_field_type_supported(field.shape(), type_name, field.name)?;
}
Ok(())
}
fn ensure_format_jit_flattened_enum_supported(
enum_type: &facet_core::EnumType,
type_name: &'static str,
) -> Result<(), Tier2Incompatibility> {
use facet_core::StructKind;
ensure_format_jit_enum_supported(enum_type, type_name)?;
for variant in enum_type.variants {
if matches!(variant.data.kind, StructKind::Unit) {
return Err(Tier2Incompatibility::FlattenedEnumUnitVariant {
type_name,
variant_name: variant.name,
});
}
if variant.data.fields.is_empty() {
return Err(Tier2Incompatibility::FlattenedEnumUnitVariant {
type_name,
variant_name: variant.name,
});
}
}
Ok(())
}
fn ensure_format_jit_enum_supported(
enum_type: &facet_core::EnumType,
type_name: &'static str,
) -> Result<(), Tier2Incompatibility> {
use facet_core::{BaseRepr, EnumRepr, ScalarType, StructKind};
if !matches!(enum_type.repr.base, BaseRepr::C | BaseRepr::Rust) {
let repr_name = match enum_type.repr.base {
BaseRepr::C => "C",
BaseRepr::Rust => "Rust",
BaseRepr::Transparent => "transparent",
};
return Err(Tier2Incompatibility::UnsupportedEnumRepr {
type_name,
repr: repr_name,
});
}
match enum_type.enum_repr {
EnumRepr::U8
| EnumRepr::U16
| EnumRepr::U32
| EnumRepr::U64
| EnumRepr::USize
| EnumRepr::I8
| EnumRepr::I16
| EnumRepr::I32
| EnumRepr::I64
| EnumRepr::ISize => {
}
EnumRepr::Rust => {
return Err(Tier2Incompatibility::UnsupportedEnumRepr {
type_name,
repr: "default Rust (unspecified discriminant layout)",
});
}
EnumRepr::RustNPO => {
return Err(Tier2Incompatibility::UnsupportedEnumRepr {
type_name,
repr: "niche/NPO (Option-like optimization)",
});
}
}
for variant in enum_type.variants {
if variant.discriminant.is_none() {
return Err(Tier2Incompatibility::EnumVariantNoDiscriminant {
type_name,
variant_name: variant.name,
});
}
match variant.data.kind {
StructKind::Unit => {
}
StructKind::TupleStruct | StructKind::Struct | StructKind::Tuple => {
for field in variant.data.fields {
let field_shape = field.shape();
if let facet_core::Type::User(facet_core::UserType::Struct(struct_def)) =
&field_shape.ty
{
ensure_format_jit_struct_supported(struct_def, type_name)?;
} else if let Some(scalar_type) = field_shape.scalar_type() {
if !matches!(
scalar_type,
ScalarType::Bool
| ScalarType::I8
| ScalarType::I16
| ScalarType::I32
| ScalarType::I64
| ScalarType::U8
| ScalarType::U16
| ScalarType::U32
| ScalarType::U64
| ScalarType::String
) {
return Err(Tier2Incompatibility::UnsupportedEnumVariantField {
type_name,
variant_name: variant.name,
field_name: field.name,
});
}
} else {
return Err(Tier2Incompatibility::UnsupportedEnumVariantField {
type_name,
variant_name: variant.name,
field_name: field.name,
});
}
}
}
}
}
Ok(())
}
pub(crate) fn ensure_format_jit_field_type_supported(
shape: &'static Shape,
type_name: &'static str,
field_name: &'static str,
) -> Result<(), Tier2Incompatibility> {
use facet_core::ScalarType;
if let Def::Option(opt_def) = &shape.def {
return ensure_format_jit_field_type_supported(opt_def.t, type_name, field_name);
}
if let Def::Result(result_def) = &shape.def {
ensure_format_jit_field_type_supported(result_def.t, type_name, field_name).map_err(
|_| Tier2Incompatibility::UnsupportedResultType {
type_name,
which: "Ok",
},
)?;
ensure_format_jit_field_type_supported(result_def.e, type_name, field_name).map_err(
|_| Tier2Incompatibility::UnsupportedResultType {
type_name,
which: "Err",
},
)?;
return Ok(());
}
if let Def::List(list_def) = &shape.def {
return ensure_format_jit_element_supported(list_def.t, type_name);
}
if let Def::Map(map_def) = &shape.def {
if map_def.k.scalar_type() != Some(ScalarType::String) {
return Err(Tier2Incompatibility::MapNonStringKey { type_name });
}
return ensure_format_jit_element_supported(map_def.v, type_name);
}
if let Some(scalar_type) = shape.scalar_type()
&& matches!(
scalar_type,
ScalarType::Bool
| ScalarType::I8
| ScalarType::I16
| ScalarType::I32
| ScalarType::I64
| ScalarType::U8
| ScalarType::U16
| ScalarType::U32
| ScalarType::U64
| ScalarType::F32
| ScalarType::F64
| ScalarType::String
)
{
return Ok(());
}
if let Type::User(UserType::Struct(struct_def)) = &shape.ty {
return ensure_format_jit_struct_supported(struct_def, type_name);
}
if let Type::User(UserType::Enum(enum_def)) = &shape.ty {
return ensure_format_jit_enum_supported(enum_def, type_name);
}
let field_type = shape_type_description(shape);
Err(Tier2Incompatibility::UnsupportedFieldType {
type_name,
field_name,
field_type,
})
}
pub(crate) fn ensure_format_jit_element_supported(
elem_shape: &'static Shape,
type_name: &'static str,
) -> Result<(), Tier2Incompatibility> {
use facet_core::ScalarType;
if let Some(scalar_type) = elem_shape.scalar_type() {
if matches!(
scalar_type,
ScalarType::Bool
| ScalarType::I8
| ScalarType::I16
| ScalarType::I32
| ScalarType::I64
| ScalarType::U8
| ScalarType::U16
| ScalarType::U32
| ScalarType::U64
| ScalarType::F32
| ScalarType::F64
| ScalarType::String
) {
return Ok(());
}
}
if let Def::Result(result_def) = &elem_shape.def {
ensure_format_jit_element_supported(result_def.t, type_name).map_err(|_| {
Tier2Incompatibility::UnsupportedResultType {
type_name,
which: "Ok",
}
})?;
ensure_format_jit_element_supported(result_def.e, type_name).map_err(|_| {
Tier2Incompatibility::UnsupportedResultType {
type_name,
which: "Err",
}
})?;
return Ok(());
}
if let Def::List(list_def) = &elem_shape.def {
return ensure_format_jit_element_supported(list_def.t, type_name);
}
if let Def::Map(map_def) = &elem_shape.def {
if map_def.k.scalar_type() != Some(ScalarType::String) {
return Err(Tier2Incompatibility::MapNonStringKey { type_name });
}
return ensure_format_jit_element_supported(map_def.v, type_name);
}
if let Type::User(UserType::Struct(struct_def)) = &elem_shape.ty {
return ensure_format_jit_struct_supported(struct_def, type_name);
}
let elem_type = shape_type_description(elem_shape);
Err(Tier2Incompatibility::UnsupportedFieldType {
type_name,
field_name: "(element)",
field_type: elem_type,
})
}
const fn shape_type_description(shape: &'static Shape) -> &'static str {
match &shape.def {
Def::Undefined => "undefined",
Def::Scalar => "scalar",
Def::List(_) => "Vec<_>",
Def::Map(_) => "HashMap<_, _>",
Def::Set(_) => "HashSet<_>",
Def::Option(_) => "Option<_>",
Def::Result(_) => "Result<_, _>",
Def::Pointer(_) => "smart pointer",
Def::Array(_) => "array",
Def::NdArray(_) => "nd-array",
Def::Slice(_) => "slice",
Def::DynamicValue(_) => "dynamic value",
_ => "unknown",
}
}