pub mod format;
#[cfg(test)]
pub mod regression_tests;
use std::{
collections::{BTreeMap, HashMap, HashSet},
string::ToString,
sync::LazyLock,
};
use facet::{
ArrayDef, Def, EnumType, Facet, Field, FieldFlags, ListDef, MapDef, NumericType, OptionDef,
PointerDef, PointerType, PrimitiveType, SequenceType, SetDef, Shape, SliceDef, StructKind,
StructType, TextualType, Type, UserType, Variant,
};
use regex::Regex;
use crate as fg;
use crate::{Registry, error::Error};
use format::{
ContainerFormat, Format, FormatHolder, Named, Namespace, QualifiedTypeName, VariantFormat,
};
const SUPPORTED_GENERIC_TYPES: [&str; 10] = [
"Arc", "Rc", "Box", "Option", "Vec", "HashMap", "HashSet", "BTreeMap", "BTreeSet", "DateTime",
];
#[derive(Debug, Clone, PartialEq)]
struct NamespaceContext {
namespace: Namespace,
explicit: bool, }
impl NamespaceContext {
fn cleared() -> Self {
Self {
namespace: Namespace::Root,
explicit: true,
}
}
fn explicit(namespace: Namespace) -> Self {
Self {
namespace,
explicit: true,
}
}
fn is_explicit(&self) -> bool {
self.explicit
}
fn is_cleared(&self) -> bool {
self.explicit && matches!(self.namespace, Namespace::Root)
}
}
#[derive(Debug, Clone, PartialEq)]
enum NamespaceAction {
SetContext(NamespaceContext),
Inherit,
}
impl NamespaceAction {
fn is_explicit(&self) -> bool {
match self {
NamespaceAction::SetContext(ctx) => ctx.is_explicit(),
NamespaceAction::Inherit => false, }
}
fn should_move_to_namespace(&self, target_namespace: &str) -> bool {
match self {
NamespaceAction::SetContext(ctx) if ctx.is_explicit() => {
match &ctx.namespace {
Namespace::Named(type_ns) => {
type_ns == target_namespace
}
Namespace::Root => {
target_namespace.is_empty() }
}
}
NamespaceAction::SetContext(_) | NamespaceAction::Inherit => {
true
}
}
}
}
#[derive(Debug, Default)]
pub struct RegistryBuilder {
pub registry: Registry,
current: Vec<QualifiedTypeName>,
processed: HashSet<QualifiedTypeName>,
name_mappings: BTreeMap<QualifiedTypeName, QualifiedTypeName>,
generic_type_params: HashMap<String, String>,
processing_nested: bool,
namespace_context_stack: Vec<NamespaceContext>,
type_namespace_sources: HashMap<QualifiedTypeName, bool>, }
impl RegistryBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn build(self) -> Result<Registry, Error> {
for (type_name, format) in &self.registry {
if let Err(err) = format.visit(&mut |_| Ok(())) {
return Err(Error::ReflectionError {
type_name: type_name.clone().to_string(),
message: err.to_string(),
});
}
}
Ok(self.registry)
}
pub fn add_type<'a, T: Facet<'a>>(mut self) -> Result<Self, Error> {
self.format(T::SHAPE)?;
Ok(self)
}
}
impl RegistryBuilder {
fn push(&mut self, name: QualifiedTypeName, container: ContainerFormat) {
self.registry.insert(name.clone(), container);
self.current.push(name);
}
fn push_with_type_check(
&mut self,
name: QualifiedTypeName,
container: ContainerFormat,
shape: &Shape,
) -> Result<(), Error> {
let is_explicit = extract_namespace_from_shape(shape)?.is_explicit();
self.check_namespace_ambiguity(&name, is_explicit)?;
self.registry.insert(name.clone(), container);
self.current.push(name);
Ok(())
}
fn push_temporary(
&mut self,
name: String,
container: ContainerFormat,
parent_context: Option<&Shape>,
) -> QualifiedTypeName {
let temp_name = if let Some(parent) = parent_context {
let parent_name = parent.type_identifier.replace(['<', '>', ' ', ','], "_");
format!("{name}__in__{parent_name}")
} else {
name
};
let qualified_name = QualifiedTypeName {
namespace: Namespace::Named("__temp__".to_string()),
name: temp_name,
};
self.push(qualified_name.clone(), container);
qualified_name
}
fn register_type_mapping(&mut self, original: QualifiedTypeName, renamed: QualifiedTypeName) {
self.name_mappings.insert(original, renamed);
}
fn is_processed(&self, name: &QualifiedTypeName) -> bool {
self.processed.contains(name)
}
fn mark_processed(&mut self, name: QualifiedTypeName) {
self.processed.insert(name);
}
fn pop(&mut self) {
self.current.pop();
}
fn get_mut(&mut self) -> Option<&mut ContainerFormat> {
if let Some(name) = self.current.last() {
self.registry.get_mut(name)
} else {
None
}
}
fn format_with_namespace_override(
&mut self,
shape: &Shape,
namespace: &str,
) -> Result<(), Error> {
let base_name = shape.type_identifier.to_string();
let namespaced_key =
QualifiedTypeName::namespaced(namespace.to_string(), base_name.clone());
if !self.registry.contains_key(&namespaced_key) {
let context = NamespaceContext::explicit(Namespace::Named(namespace.to_string()));
self.push_namespace(NamespaceAction::SetContext(context));
self.format(shape)?;
self.pop_namespace();
let original_key = self.get_name_with_mappings(shape)?;
if original_key != namespaced_key {
let type_level_namespace = extract_namespace_from_shape(shape)?;
let should_move = type_level_namespace.should_move_to_namespace(namespace);
if should_move && let Some(format) = self.registry.remove(&original_key) {
self.registry.insert(namespaced_key.clone(), format);
}
}
}
Ok(())
}
fn format(&mut self, mut shape: &Shape) -> Result<(), Error> {
if is_transparent_shape(shape)
&& let Some(inner) = shape.inner
{
shape = inner;
}
if !self.is_supported_generic_type(shape) {
return Err(Error::UnsupportedGenericType(shape.to_string()));
}
if let Def::Option(option_def) = shape.def {
self.format_option(option_def)?;
return Ok(());
}
if self.try_format_from_type_system(shape)? {
return Ok(());
}
self.format_from_def_system(shape)?;
Ok(())
}
fn is_supported_generic_type(&mut self, shape: &Shape) -> bool {
if shape.type_params.is_empty() {
return true;
}
if shape.type_identifier.starts_with('&') || shape.type_identifier.starts_with('[') {
return true;
}
if SUPPORTED_GENERIC_TYPES.contains(&shape.type_identifier) {
return true;
}
let current_params = format!("{:?}", shape.type_params);
let previous_params = self
.generic_type_params
.entry(shape.type_identifier.to_string())
.or_insert_with(|| current_params.clone());
*previous_params == current_params
}
fn try_format_from_type_system(&mut self, shape: &Shape) -> Result<bool, Error> {
match &shape.ty {
Type::User(UserType::Struct(struct_def)) => {
self.handle_user_struct(shape, struct_def)?;
Ok(true)
}
Type::User(UserType::Enum(enum_def)) => {
self.format_enum(enum_def, shape)?;
Ok(true)
}
Type::Sequence(sequence_type) => {
self.handle_sequence_type(shape, sequence_type)?;
Ok(true)
}
_ => Ok(false),
}
}
fn handle_user_struct(&mut self, shape: &Shape, struct_def: &StructType) -> Result<(), Error> {
let type_name = self.get_name_with_mappings(shape)?;
if !self.processing_nested {
let format = if shape.type_identifier == "()" {
Format::Unit
} else {
Format::TypeName(type_name.clone())
};
self.update_container_format(format, UpdateMode::IfUnknown);
}
self.format_struct(struct_def, shape)?;
Ok(())
}
fn handle_sequence_type(
&mut self,
shape: &Shape,
sequence_type: &SequenceType,
) -> Result<(), Error> {
match sequence_type {
SequenceType::Slice(slice_type) => {
if let Def::Slice(slice_def) = shape.def {
self.format_slice(slice_def)?;
} else {
let target_shape = slice_type.t;
let inner_format = get_inner_format(target_shape)?;
let slice_format = Format::Seq(Box::new(inner_format));
self.update_container_format(slice_format, UpdateMode::Force);
self.process_nested_types(target_shape)?;
}
}
SequenceType::Array(array_type) => {
if let Def::Array(array_def) = shape.def {
self.format_array(array_def)?;
} else {
let target_shape = array_type.t;
let inner_format = get_inner_format(target_shape)?;
let array_format = Format::Seq(Box::new(inner_format)); self.update_container_format(array_format, UpdateMode::Force);
self.process_nested_types(target_shape)?;
}
}
}
Ok(())
}
fn format_from_def_system(&mut self, shape: &Shape) -> Result<(), Error> {
match shape.def {
Def::Scalar => self.format_scalar(shape)?,
Def::Map(map_def) => self.format_map(map_def)?,
Def::List(list_def) => self.format_list(list_def)?,
Def::Slice(slice_def) => self.format_slice(slice_def)?,
Def::Array(array_def) => self.format_array(array_def)?,
Def::Set(set_def) => self.format_set(set_def)?,
Def::Option(option_def) => self.format_option(option_def)?,
Def::Pointer(PointerDef {
pointee: Some(inner_shape),
..
}) => {
self.handle_pointer(inner_shape)?;
}
Def::Pointer(PointerDef { pointee: None, .. }) => {
self.handle_opaque_pointee();
}
Def::Undefined => {
self.handle_undefined_def(shape)?;
}
_ => (),
}
Ok(())
}
fn handle_pointer(&mut self, inner_shape: &Shape) -> Result<(), Error> {
let inner_format = get_format_for_shape(inner_shape)?;
self.update_container_format(inner_format, UpdateMode::IfUnknown);
self.process_nested_types(inner_shape)?;
Ok(())
}
fn handle_undefined_def(&mut self, shape: &Shape) -> Result<(), Error> {
match &shape.ty {
Type::Primitive(primitive) => match primitive {
PrimitiveType::Boolean => {
let format = Format::Bool;
self.update_container_format(format, UpdateMode::Force);
}
PrimitiveType::Numeric(NumericType::Float) => {
let format = Format::F32; self.update_container_format(format, UpdateMode::Force);
}
PrimitiveType::Textual(TextualType::Str) => {
let format = Format::Str;
self.update_container_format(format, UpdateMode::Force);
}
p => {
unimplemented!("Unknown primitive type: {p:?}");
}
},
Type::Pointer(PointerType::Reference(pt) | PointerType::Raw(pt)) => {
self.format(pt.target)?;
}
_ => {}
}
Ok(())
}
fn format_scalar(&mut self, shape: &Shape) -> Result<(), Error> {
if let Some(format) = type_to_format(shape)? {
self.update_container_format(format, UpdateMode::Force);
}
Ok(())
}
fn format_struct(&mut self, struct_type: &StructType, shape: &Shape) -> Result<(), Error> {
let struct_name = self.get_name_with_mappings(shape)?;
if self.is_processed(&struct_name) {
let format = Format::TypeName(struct_name.clone());
self.update_container_format(format, UpdateMode::MutualRecursion);
return Ok(());
}
if struct_name.name != shape.type_identifier {
let name = QualifiedTypeName {
namespace: struct_name.namespace.clone(),
name: shape.type_identifier.to_string(),
};
self.register_type_mapping(name, struct_name.clone());
}
self.mark_processed(struct_name.clone());
let type_level_namespace = extract_namespace_from_shape(shape)?;
self.push_namespace(type_level_namespace);
match struct_type.kind {
StructKind::Unit => {
self.push_with_type_check(
struct_name.clone(),
ContainerFormat::UnitStruct(shape.into()),
shape,
)?;
self.pop();
}
StructKind::TupleStruct => {
if struct_type.fields.len() == 1 {
let field = struct_type.fields[0];
let field_shape = field.shape();
let is_transparent = is_transparent_shape(shape);
if is_transparent {
if !self.try_handle_bytes_attribute(&field) {
self.format(field_shape)?;
}
self.pop_namespace();
return Ok(());
}
let container = ContainerFormat::NewTypeStruct(Box::default(), shape.into());
self.push_with_type_check(struct_name.clone(), container, shape)?;
if !self.try_handle_bytes_attribute(&field) {
self.format(field_shape)?;
}
self.pop();
} else {
let container = ContainerFormat::TupleStruct(vec![], shape.into());
self.push_with_type_check(struct_name.clone(), container, shape)?;
for field in struct_type.fields {
let skip = field.flags.contains(FieldFlags::SKIP);
if skip {
continue;
}
if !self.try_handle_bytes_attribute(field) {
self.format(field.shape())?;
}
}
self.pop();
}
}
StructKind::Struct => {
let container = ContainerFormat::Struct(vec![], shape.into());
self.push_with_type_check(struct_name.clone(), container, shape)?;
for field in struct_type.fields {
let skip = field.flags.contains(FieldFlags::SKIP);
if skip {
continue;
}
self.handle_struct_field(field)?;
}
if let Some(ContainerFormat::Struct(fields, doc)) = self.get_mut()
&& fields.is_empty()
{
let unit_container = ContainerFormat::UnitStruct(doc.clone());
if let Some(current_name) = self.current.last() {
self.registry.insert(current_name.clone(), unit_container);
}
}
self.pop();
}
StructKind::Tuple => {
}
}
self.pop_namespace();
Ok(())
}
fn handle_struct_field(&mut self, field: &Field) -> Result<(), Error> {
let field_shape = field.shape();
if self.try_handle_bytes_attribute(field) {
return Ok(());
}
if self.try_handle_option_field(field)? {
return Ok(());
}
if self.try_handle_tuple_struct_field(field)? {
return Ok(());
}
let field_namespace = extract_namespace_from_field_attributes(field)?;
self.push_namespace(field_namespace.clone());
let Some(field_format) = self.get_user_type_format(field_shape)? else {
self.pop_namespace();
return Ok(());
};
if let NamespaceAction::SetContext(ctx) = &field_namespace {
if ctx.is_explicit() {
if let Namespace::Named(name) = &ctx.namespace {
self.format_with_namespace_override(field_shape, name)?;
} else {
self.format(field_shape)?;
}
} else {
self.format(field_shape)?;
}
} else {
self.format(field_shape)?;
}
self.pop_namespace();
if let Some(ContainerFormat::Struct(named_formats, _doc)) = self.get_mut() {
let format = Named {
name: field_display_name(field),
doc: field.into(),
value: field_format,
};
named_formats.push(format);
}
Ok(())
}
fn try_handle_bytes_attribute(&mut self, field: &Field) -> bool {
let Some(value) = bytes_attribute_format(field) else {
return false;
};
let Some(container) = self.get_mut() else {
return false;
};
match container {
ContainerFormat::NewTypeStruct(format, _doc) => **format = value,
ContainerFormat::TupleStruct(formats, _doc) => formats.push(value),
ContainerFormat::Struct(nameds, _doc) => nameds.push(Named {
name: field_display_name(field),
doc: field.shape().into(),
value,
}),
_ => return false,
}
true
}
fn try_handle_option_field(&mut self, field: &Field) -> Result<bool, Error> {
let field_shape = field.shape();
if field_shape.type_identifier == "Option"
&& let Def::Option(option_def) = field_shape.def
{
let inner_shape = option_def.t();
let inner_format = get_format_for_shape(inner_shape)?;
let option_format = Format::Option(Box::new(inner_format));
if let Some(ContainerFormat::Struct(named_formats, _doc)) = self.get_mut() {
named_formats.push(Named {
name: field_display_name(field),
doc: field.into(),
value: option_format,
});
}
if !matches!(inner_shape.def, Def::Scalar) {
self.format(inner_shape)?;
}
return Ok(true);
}
Ok(false)
}
fn try_handle_tuple_struct_field(&mut self, field: &Field) -> Result<bool, Error> {
let field_shape = field.shape();
if let Type::User(UserType::Struct(inner_struct)) = &field_shape.ty {
if inner_struct.kind == StructKind::Tuple {
let mut tuple_formats = vec![];
for tuple_field in inner_struct.fields {
let tuple_field_shape = tuple_field.shape();
let field_format = get_inner_format(tuple_field_shape)?;
tuple_formats.push(field_format);
}
if let Some(ContainerFormat::Struct(named_formats, _doc)) = self.get_mut() {
let tuple_format = if tuple_formats.is_empty() {
Format::Unit
} else {
Format::Tuple(tuple_formats)
};
named_formats.push(Named {
name: field_display_name(field),
doc: field.into(),
value: tuple_format,
});
}
return Ok(true);
}
let is_referenced_transparent = inner_struct.kind == StructKind::TupleStruct
&& inner_struct.fields.len() == 1
&& is_transparent_shape(field_shape);
if is_referenced_transparent {
let transparent_namespace = extract_namespace_from_shape(field_shape)?;
let inner_field = inner_struct.fields[0];
let inner_field_shape = inner_field.shape();
let inner_format = if let Type::User(UserType::Struct(_) | UserType::Enum(_)) =
&inner_field_shape.ty
{
let namespaced_name = self.get_name_with_mappings(inner_field_shape)?;
Format::TypeName(namespaced_name)
} else {
get_inner_format(inner_field_shape)?
};
if let Some(ContainerFormat::Struct(named_formats, _doc)) = self.get_mut() {
named_formats.push(Named {
name: field_display_name(field),
doc: field.into(),
value: inner_format,
});
}
self.push_namespace(transparent_namespace);
self.format(inner_field_shape)?;
self.pop_namespace();
return Ok(true);
}
}
Ok(false)
}
fn format_enum(&mut self, enum_type: &EnumType, shape: &Shape) -> Result<(), Error> {
let enum_name = self.get_name_with_mappings(shape)?;
if self.is_processed(&enum_name) {
return Ok(());
}
if enum_name.name != shape.type_identifier {
let name = QualifiedTypeName {
namespace: enum_name.namespace.clone(),
name: shape.type_identifier.to_string(),
};
self.register_type_mapping(name, enum_name.clone());
}
self.mark_processed(enum_name.clone());
let type_level_namespace = extract_namespace_from_shape(shape)?;
self.push_namespace(type_level_namespace);
let variants = self.process_enum_variants(enum_type, shape)?;
let container = ContainerFormat::Enum(variants, shape.into());
self.push_with_type_check(enum_name.clone(), container, shape)?;
self.pop();
self.pop_namespace();
Ok(())
}
fn process_enum_variants(
&mut self,
enum_type: &EnumType,
shape: &Shape,
) -> Result<BTreeMap<u32, Named<VariantFormat>>, Error> {
let mut variants = BTreeMap::new();
let mut variant_index = 0u32;
for variant in enum_type.variants {
let skip = variant
.attributes
.iter()
.any(|attr| attr.key == "skip" && attr.is_builtin());
if skip {
continue;
}
let variant_format = self.process_single_variant(variant, shape)?;
variants.insert(
variant_index,
Named {
name: variant_display_name(variant),
doc: variant.into(),
value: variant_format,
},
);
variant_index += 1;
}
Ok(variants)
}
fn process_single_variant(
&mut self,
variant: &Variant,
shape: &Shape,
) -> Result<VariantFormat, Error> {
if variant.data.fields.is_empty() {
Ok(VariantFormat::Unit)
} else if variant.data.fields.len() == 1 {
let is_struct_variant = !variant.data.fields[0]
.name
.chars()
.all(|c| c.is_ascii_digit());
if is_struct_variant {
self.process_struct_variant(variant, shape)
} else {
self.process_newtype_variant(variant, shape)
}
} else {
self.process_multi_field_variant(variant, shape)
}
}
fn process_newtype_variant(
&mut self,
variant: &Variant,
shape: &Shape,
) -> Result<VariantFormat, Error> {
let field = variant.data.fields[0];
if let Some(value) = bytes_attribute_format(&field) {
return Ok(VariantFormat::NewType(Box::new(value)));
}
let field_shape = field.shape();
if is_transparent_shape(field_shape)
&& let Some(inner) = field_shape.inner
&& let Some(format) = self.get_user_type_format(inner)?
{
return Ok(VariantFormat::NewType(Box::new(format)));
}
if let Def::Option(v) = field_shape.def
&& let Some(format) = self.get_user_type_format(v.t)?
{
return Ok(VariantFormat::NewType(Box::new(Format::Option(Box::new(
format,
)))));
}
if field_shape.type_identifier == "()" {
Ok(VariantFormat::NewType(Box::new(Format::Unit)))
} else if let Type::User(UserType::Struct(_) | UserType::Enum(_)) = &field_shape.ty {
let field_namespace = extract_namespace_from_field_attributes(&field)?;
match field_namespace {
NamespaceAction::SetContext(ctx) if ctx.is_explicit() => {
let base_name = field_shape.type_identifier.to_string();
let qualified_name = match &ctx.namespace {
Namespace::Root => {
let context = NamespaceContext::explicit(Namespace::Root);
self.push_namespace(NamespaceAction::SetContext(context));
self.format(field_shape)?;
self.pop_namespace();
QualifiedTypeName::root(base_name)
}
Namespace::Named(name) => {
let context =
NamespaceContext::explicit(Namespace::Named(name.clone()));
self.push_namespace(NamespaceAction::SetContext(context));
self.format(field_shape)?;
self.pop_namespace();
QualifiedTypeName::namespaced(name.clone(), base_name)
}
};
Ok(VariantFormat::NewType(Box::new(Format::TypeName(
qualified_name,
))))
}
NamespaceAction::SetContext(_) | NamespaceAction::Inherit => {
self.format(field_shape)?;
let namespaced_name = self.get_name_with_mappings(field_shape)?;
Ok(VariantFormat::NewType(Box::new(Format::TypeName(
namespaced_name,
))))
}
}
} else {
self.process_newtype_variant_with_temp_container(variant, field_shape, shape)
}
}
fn process_newtype_variant_with_temp_container(
&mut self,
variant: &Variant,
field_shape: &Shape,
_shape: &Shape,
) -> Result<VariantFormat, Error> {
let field = variant.data.fields[0];
let field_namespace = extract_namespace_from_field_attributes(&field)?;
self.push_namespace(field_namespace);
let Some(format) = self.get_user_type_format(field_shape)? else {
self.pop_namespace();
return Ok(VariantFormat::Unit);
};
if matches!(format, Format::Unit) {
self.pop_namespace();
return Ok(VariantFormat::Unit);
}
self.format(field_shape)?;
self.pop_namespace();
Ok(VariantFormat::NewType(Box::new(format)))
}
fn process_multi_field_variant(
&mut self,
variant: &Variant,
shape: &Shape,
) -> Result<VariantFormat, Error> {
let first_field = variant.data.fields[0];
let is_struct_variant = !first_field.name.chars().all(|c| c.is_ascii_digit());
if is_struct_variant {
self.process_struct_variant(variant, shape)
} else {
self.process_tuple_variant(variant, shape)
}
}
fn process_struct_variant(
&mut self,
variant: &Variant,
shape: &Shape,
) -> Result<VariantFormat, Error> {
let temp = self.push_temporary(
variant_display_name(variant),
ContainerFormat::Struct(vec![], variant.into()),
Some(shape),
);
for field in variant.data.fields {
let skip = field.flags.contains(FieldFlags::SKIP);
if skip {
continue;
}
let field_shape = field.shape();
if let Some(value) = bytes_attribute_format(field) {
if let Some(ContainerFormat::Struct(named_formats, _doc)) = self.get_mut() {
named_formats.push(Named {
name: field_display_name(field),
doc: field.into(),
value,
});
}
continue;
}
let field_namespace = extract_namespace_from_field_attributes(field)?;
self.push_namespace(field_namespace.clone());
if field_shape.type_identifier == "Option"
&& let Def::Option(option_def) = field_shape.def
{
let inner_shape = option_def.t();
let inner_format =
get_inner_format_with_context(inner_shape, self.current_namespace())?;
let option_format = Format::Option(Box::new(inner_format));
if !matches!(inner_shape.def, Def::Scalar) {
self.format(inner_shape)?;
}
self.pop_namespace();
if let Some(ContainerFormat::Struct(named_formats, _doc)) = self.get_mut() {
named_formats.push(Named {
name: field_display_name(field),
doc: field.into(),
value: option_format,
});
}
continue;
}
let Some(value) = self.get_user_type_format(field_shape)? else {
self.pop_namespace();
continue;
};
if let NamespaceAction::SetContext(ctx) = &field_namespace {
if ctx.is_explicit() {
if let Namespace::Named(name) = &ctx.namespace {
self.format_with_namespace_override(field_shape, name)?;
} else {
self.format(field_shape)?;
}
} else {
self.format(field_shape)?;
}
} else {
self.format(field_shape)?;
}
self.pop_namespace();
if let Some(ContainerFormat::Struct(named_formats, _doc)) = self.get_mut() {
named_formats.push(Named {
name: field_display_name(field),
doc: field.into(),
value,
});
}
}
let variant_format = match self.registry.get(&temp) {
Some(ContainerFormat::Struct(named_formats, _doc)) => {
if named_formats.is_empty() {
VariantFormat::Unit
} else {
VariantFormat::Struct(named_formats.clone())
}
}
_ => VariantFormat::Unit, };
let _removed = self.registry.remove(&temp);
self.pop();
Ok(variant_format)
}
fn process_tuple_variant(
&mut self,
variant: &Variant,
shape: &Shape,
) -> Result<VariantFormat, Error> {
let temp = self.push_temporary(
variant_display_name(variant),
ContainerFormat::TupleStruct(vec![], variant.into()),
Some(shape),
);
for field in variant.data.fields {
let skip = field.flags.contains(FieldFlags::SKIP);
if skip {
continue;
}
if let Some(value) = bytes_attribute_format(field) {
if let Some(ContainerFormat::TupleStruct(formats, _doc)) = self.get_mut() {
formats.push(value);
}
continue;
}
let transparent_namespace = extract_namespace_from_shape(shape)?;
self.push_namespace(transparent_namespace);
self.format(field.shape())?;
self.pop_namespace();
}
let variant_format =
if let Some(ContainerFormat::TupleStruct(formats, _doc)) = self.registry.get(&temp) {
VariantFormat::Tuple(formats.clone())
} else {
VariantFormat::Unit
};
let _removed = self.registry.remove(&temp);
self.pop();
Ok(variant_format)
}
fn format_list(&mut self, list_def: ListDef) -> Result<(), Error> {
let inner_shape = list_def.t();
let inner_format = get_inner_format(inner_shape)?;
let seq_format = Format::Seq(Box::new(inner_format));
self.update_container_format(seq_format, UpdateMode::Force);
self.process_nested_types(inner_shape)?;
Ok(())
}
fn format_map(&mut self, map_def: MapDef) -> Result<(), Error> {
let key_shape = map_def.k();
let value_shape = map_def.v();
let key_format = get_inner_format(key_shape)?;
let value_format = get_inner_format(value_shape)?;
let map_format = Format::Map {
key: Box::new(key_format),
value: Box::new(value_format),
};
self.update_container_format(map_format, UpdateMode::Force);
self.process_nested_types(key_shape)?;
self.process_nested_types(value_shape)?;
Ok(())
}
fn format_slice(&mut self, slice_def: SliceDef) -> Result<(), Error> {
let inner_shape = slice_def.t();
let inner_format = get_format_for_shape(inner_shape)?;
let slice_format = Format::Seq(Box::new(inner_format));
self.update_container_format(slice_format, UpdateMode::Force);
self.process_nested_types(inner_shape)?;
Ok(())
}
fn format_array(&mut self, array_def: ArrayDef) -> Result<(), Error> {
let inner_shape = array_def.t();
let array_size = array_def.n;
let inner_format = get_inner_format(inner_shape)?;
let array_format = Format::TupleArray {
content: Box::new(inner_format),
size: array_size,
};
self.update_container_format(array_format, UpdateMode::Force);
if !matches!(inner_shape.def, Def::Scalar) {
self.format(inner_shape)?;
}
Ok(())
}
fn format_option(&mut self, option_def: OptionDef) -> Result<(), Error> {
let inner_shape = option_def.t();
let inner_format = get_format_for_shape(inner_shape)?;
let option_format = Format::Option(Box::new(inner_format));
self.update_container_format(option_format, UpdateMode::Force);
self.process_nested_types(inner_shape)?;
Ok(())
}
fn format_set(&mut self, set_def: SetDef) -> Result<(), Error> {
let element_shape = set_def.t();
let element_format = get_inner_format(element_shape)?;
let set_format = Format::Set(Box::new(element_format));
self.update_container_format(set_format, UpdateMode::Force);
self.process_nested_types(element_shape)?;
Ok(())
}
fn handle_opaque_pointee(&mut self) {
let format = Format::Unit;
self.update_container_format(format, UpdateMode::Force);
}
fn push_namespace(&mut self, action: NamespaceAction) {
let context = match action {
NamespaceAction::SetContext(ctx) => ctx,
NamespaceAction::Inherit => {
self.namespace_context_stack
.last()
.cloned()
.unwrap_or_else(NamespaceContext::cleared)
}
};
self.namespace_context_stack.push(context);
}
fn pop_namespace(&mut self) {
self.namespace_context_stack.pop();
}
fn current_namespace_context(&self) -> Option<&NamespaceContext> {
match self.namespace_context_stack.last() {
Some(ctx) if ctx.is_cleared() => {
None
}
Some(ctx) => Some(ctx),
None => None,
}
}
fn current_namespace(&self) -> Option<&Namespace> {
self.current_namespace_context().map(|ctx| &ctx.namespace)
}
fn get_user_type_format(&mut self, mut field_shape: &Shape) -> Result<Option<Format>, Error> {
if is_transparent_shape(field_shape)
&& let Some(inner) = field_shape.inner
{
field_shape = inner;
}
match &field_shape.ty {
Type::User(UserType::Struct(_) | UserType::Enum(_)) => {
if field_shape.type_identifier == "()" {
Ok(Some(Format::Unit))
} else if let Def::Option(v) = field_shape.def {
let renamed_name = self.get_name_with_mappings(v.t)?;
Ok(Some(Format::Option(Box::new(Format::TypeName(
renamed_name,
)))))
} else {
let renamed_name = self.get_name_with_mappings(field_shape)?;
Ok(Some(Format::TypeName(renamed_name)))
}
}
Type::Pointer(PointerType::Reference(pt) | PointerType::Raw(pt)) => {
let target_shape = pt.target;
match get_inner_format_with_context(target_shape, self.current_namespace()) {
Ok(format) => Ok(Some(format)),
Err(_) => Ok(None), }
}
_ => {
if matches!(field_shape.def, Def::Undefined) {
Ok(None)
} else {
match get_inner_format_with_context(field_shape, self.current_namespace()) {
Ok(format) => Ok(Some(format)),
Err(_) => Ok(None), }
}
}
}
}
fn update_container_format(&mut self, format: Format, mode: UpdateMode) {
if let Some(container_format) = self.get_mut() {
match container_format {
ContainerFormat::UnitStruct(_doc) => {}
ContainerFormat::NewTypeStruct(inner_format, _doc) => match mode {
UpdateMode::Force => {
**inner_format = format;
}
UpdateMode::IfUnknown | UpdateMode::MutualRecursion => {
if inner_format.is_unknown() {
**inner_format = format;
}
}
},
ContainerFormat::TupleStruct(formats, _doc) => {
match mode {
UpdateMode::Force | UpdateMode::IfUnknown => {
formats.push(format);
}
UpdateMode::MutualRecursion => {
}
}
}
ContainerFormat::Struct(fields, _doc) => {
if let Some(last_named) = fields.last_mut() {
match mode {
UpdateMode::Force => {
if last_named.value.is_unknown() {
last_named.value = format;
}
}
UpdateMode::IfUnknown | UpdateMode::MutualRecursion => {
if last_named.value.is_unknown() {
last_named.value = format;
}
}
}
}
}
ContainerFormat::Enum(_, _doc) => {
if matches!(mode, UpdateMode::Force) {
todo!("Enum container format update not implemented");
}
}
}
}
}
fn process_nested_types(&mut self, shape: &Shape) -> Result<(), Error> {
self.processing_nested = true;
match shape.def {
Def::Scalar => {
}
Def::List(inner_list_def) => {
let inner_shape = inner_list_def.t();
self.process_nested_types(inner_shape)?;
}
Def::Option(option_def) => {
let inner_shape = option_def.t();
if should_process_nested_type(inner_shape) {
self.process_nested_types(inner_shape)?;
}
}
Def::Map(map_def) => {
let key_shape = map_def.k();
let value_shape = map_def.v();
if should_process_nested_type(key_shape) {
self.process_nested_types(key_shape)?;
}
if should_process_nested_type(value_shape) {
self.process_nested_types(value_shape)?;
}
}
Def::Slice(slice_def) => {
let inner_shape = slice_def.t();
self.process_nested_types(inner_shape)?;
}
_ => {
if should_process_nested_type(shape) {
self.format(shape)?;
}
}
}
self.processing_nested = false;
Ok(())
}
fn get_name_with_mappings(&mut self, shape: &Shape) -> Result<QualifiedTypeName, Error> {
let base_key = QualifiedTypeName::root(shape.type_identifier.to_string());
if let Some(mapped_name) = self.name_mappings.get(&base_key) {
return Ok(mapped_name.clone());
}
let original_name = get_name(shape)?;
let has_explicit_namespace = extract_namespace_from_shape(shape)?.is_explicit();
if has_explicit_namespace {
self.check_namespace_ambiguity(&original_name, true)?;
return Ok(original_name);
}
if let Some(namespace_context) = self.current_namespace_context() {
match &namespace_context.namespace {
Namespace::Root => {
let root_name = QualifiedTypeName::root(original_name.name.clone());
self.check_namespace_ambiguity(&root_name, namespace_context.is_explicit())?;
return Ok(root_name);
}
Namespace::Named(context_ns) => {
let namespaced_name = QualifiedTypeName::namespaced(
context_ns.clone(),
original_name.name.clone(),
);
self.check_namespace_ambiguity(
&namespaced_name,
namespace_context.is_explicit(),
)?;
return Ok(namespaced_name);
}
}
}
Ok(original_name)
}
fn check_namespace_ambiguity(
&mut self,
new_name: &QualifiedTypeName,
new_has_explicit_namespace: bool,
) -> Result<(), Error> {
for (existing_name, existing_has_explicit_namespace) in &self.type_namespace_sources {
if existing_name.name == new_name.name
&& existing_name.namespace != new_name.namespace
&& (!matches!(existing_name.namespace, Namespace::Root)
&& !matches!(new_name.namespace, Namespace::Root))
{
let at_least_one_namespace_inherited =
!new_has_explicit_namespace || !*existing_has_explicit_namespace;
if at_least_one_namespace_inherited {
return Err(Error::AmbiguousNamespaceInheritance {
type_name: new_name.name.clone(),
existing_namespace: existing_name.namespace.to_string(),
new_namespace: new_name.namespace.to_string(),
});
}
}
}
self.type_namespace_sources
.insert(new_name.clone(), new_has_explicit_namespace);
Ok(())
}
}
fn get_name(shape: &Shape) -> Result<QualifiedTypeName, Error> {
if let Some(type_tag) = shape.type_tag {
return Ok(QualifiedTypeName::root(type_tag.to_string()));
}
let shape_namespace = extract_namespace_from_shape(shape)?;
if is_transparent_shape(shape)
&& let Some(inner) = shape.inner
{
return get_name(inner);
}
let base_name = if let Some(rename) = shape.rename {
rename.to_string()
} else if let Some(rename) = extract_rename_from_shape_attributes(shape) {
rename.to_string()
} else {
shape.type_identifier.to_string()
};
Ok(match shape_namespace {
NamespaceAction::SetContext(ctx) => match &ctx.namespace {
Namespace::Root => QualifiedTypeName::root(base_name),
Namespace::Named(name) => QualifiedTypeName::namespaced(name.clone(), base_name),
},
NamespaceAction::Inherit => QualifiedTypeName::root(base_name),
})
}
fn get_format_for_shape(shape: &Shape) -> Result<Format, Error> {
let mut shape = match &shape.ty {
Type::Pointer(PointerType::Reference(pt) | PointerType::Raw(pt)) => pt.target,
_ => shape,
};
if is_transparent_shape(shape)
&& let Some(inner) = shape.inner
{
shape = inner;
}
get_inner_format(shape)
}
fn type_to_format(shape: &Shape) -> Result<Option<Format>, Error> {
let format = match &shape.ty {
Type::Primitive(primitive) => Some(match primitive {
PrimitiveType::Boolean => Format::Bool,
PrimitiveType::Numeric(numeric_type) => match numeric_type {
NumericType::Float => {
match shape.type_identifier {
"f32" => Format::F32,
"f64" => Format::F64,
_ => unimplemented!("Unsupported float type: {}", shape.type_identifier),
}
}
NumericType::Integer { signed } => {
let size_bytes = shape
.layout
.sized_layout()
.map_err(|_| Error::LayoutUnsized(shape.to_string()))?
.size();
let size_bits = size_bytes * 8;
match (*signed, size_bits) {
(false, 8) => Format::U8,
(false, 16) => Format::U16,
(false, 32) => Format::U32,
(false, 64) => Format::U64,
(false, 128) => Format::U128,
(true, 8) => Format::I8,
(true, 16) => Format::I16,
(true, 32) => Format::I32,
(true, 64) => Format::I64,
(true, 128) => Format::I128,
_ => unimplemented!(
"Unsupported integer type: {size_bits} bits, signed: {signed}"
),
}
}
},
PrimitiveType::Textual(textual_type) => match textual_type {
TextualType::Str => Format::Str,
TextualType::Char => Format::Char,
},
PrimitiveType::Never => {
unimplemented!("Never type not supported: {}", shape.type_identifier)
}
}),
Type::User(UserType::Opaque) => match shape.type_identifier {
"String" | "DateTime<Utc>" => Some(Format::Str),
_ => None,
},
Type::User(UserType::Struct(st))
if st.kind == StructKind::Tuple && st.fields.is_empty() =>
{
Some(Format::Unit)
}
_ => unimplemented!(
"Unsupported type for scalar format: {}: {:?}",
shape.type_identifier,
shape.ty
),
};
Ok(format)
}
fn extract_rename_from_shape_attributes(shape: &Shape) -> Option<&'static str> {
for attr in shape.attributes {
if attr.ns.is_none()
&& attr.key == "rename"
&& let Some(s) = attr.get_as::<&str>()
{
return Some(s);
}
}
None
}
fn extract_namespace_from_shape(shape: &Shape) -> Result<NamespaceAction, Error> {
for attr in shape.attributes {
if let Some(action) = extract_namespace_from_attr(attr)? {
return Ok(action);
}
}
Ok(NamespaceAction::Inherit)
}
fn extract_namespace_from_field_attributes(field: &Field) -> Result<NamespaceAction, Error> {
for attr in field.attributes {
if let Some(action) = extract_namespace_from_attr(attr)? {
return Ok(action);
}
}
Ok(NamespaceAction::Inherit)
}
fn field_display_name(field: &Field) -> String {
field.rename.unwrap_or(field.name).to_string()
}
fn variant_display_name(variant: &facet::Variant) -> String {
variant.rename.unwrap_or(variant.name).to_string()
}
fn extract_namespace_from_attr(attr: &facet::Attr) -> Result<Option<NamespaceAction>, Error> {
if attr.ns != Some("fg") || attr.key != "namespace" {
return Ok(None);
}
let Some(gen_attr) = attr.get_as::<fg::Attr>() else {
return Err(Error::InvalidNamespaceFormat);
};
match gen_attr {
fg::Attr::Namespace(Some(ns_str)) => {
static VALID_IDENT: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^[a-zA-Z_]\w*$").expect("Invalid regex"));
if !VALID_IDENT.is_match(ns_str) {
return Err(Error::InvalidNamespaceIdentifier);
}
Ok(Some(NamespaceAction::SetContext(
NamespaceContext::explicit(Namespace::Named(ns_str.to_string())),
)))
}
fg::Attr::Namespace(None) => Ok(Some(NamespaceAction::SetContext(
NamespaceContext::cleared(),
))),
_ => Ok(None),
}
}
fn is_transparent_shape(shape: &Shape) -> bool {
shape
.attributes
.iter()
.any(|attr| attr.ns.is_none() && attr.key == "transparent")
}
#[derive(Debug, Clone, Copy)]
enum UpdateMode {
Force,
IfUnknown,
MutualRecursion,
}
fn should_process_nested_type(shape: &Shape) -> bool {
!matches!(shape.def, Def::Scalar) && shape.type_identifier != "()"
}
fn bytes_attribute_format(field: &Field) -> Option<Format> {
let mut shape = field.shape();
let is_bytes_attr = |field: &Field| {
field
.attributes
.iter()
.any(|attr| attr.key == "bytes" && attr.ns == Some("fg"))
};
let mut is_transparent_bytes = || {
if is_transparent_shape(shape) {
match shape.ty {
Type::User(ty) => match ty {
UserType::Struct(ty) => match ty.kind {
StructKind::TupleStruct => {
if ty.fields.len() == 1 {
let field = ty.fields[0];
shape = field.shape();
is_bytes_attr(&field)
} else {
false
}
}
_ => false,
},
UserType::Enum(_) => todo!(),
UserType::Union(_) => todo!(),
UserType::Opaque => false,
},
_ => false,
}
} else {
false
}
};
if !is_bytes_attr(field) && !is_transparent_bytes() {
return None;
}
let (is_option, field_shape) = {
let base_shape = shape;
if let Def::Option(opt) = base_shape.def {
(true, opt.t)
} else {
(false, base_shape)
}
};
let format = || {
if is_option {
Format::Option(Box::new(Format::Bytes))
} else {
Format::Bytes
}
};
if field_shape.type_identifier == "Vec" {
if let Def::List(list_def) = field_shape.def {
let inner_shape = list_def.t();
if inner_shape.type_identifier == "u8" {
return Some(format());
}
}
}
if field_shape.type_identifier == "Bytes" {
return Some(format());
}
if let Def::Array(ArrayDef { t, .. }) = field_shape.def
&& t.type_identifier == "u8"
{
return Some(format());
}
if let Type::Pointer(PointerType::Reference(pd) | PointerType::Raw(pd)) = &field_shape.ty {
let target_shape = pd.target;
if let Def::Slice(slice_def) = target_shape.def {
let element_shape = slice_def.t();
if element_shape.type_identifier == "u8" {
return Some(format());
}
}
}
eprintln!(
"{} is not a valid bytes attribute",
field_shape.type_identifier
);
None
}
fn get_inner_format(shape: &Shape) -> Result<Format, Error> {
get_inner_format_with_context(shape, None)
}
#[allow(clippy::too_many_lines)]
fn get_inner_format_with_context(
mut shape: &Shape,
namespace_context: Option<&Namespace>,
) -> Result<Format, Error> {
if is_transparent_shape(shape)
&& let Some(inner) = shape.inner
{
shape = inner;
}
let format = match shape.def {
Def::Scalar => match type_to_format(shape)? {
Some(format) => format,
None => {
return Err(Error::ReflectionError {
type_name: shape.type_identifier.to_string(),
message: "Scalar type is not supported and should be skipped".to_string(),
});
}
},
Def::List(inner_list_def) => {
let inner_shape = inner_list_def.t();
Format::Seq(Box::new(get_inner_format_with_context(
inner_shape,
namespace_context,
)?))
}
Def::Option(option_def) => {
let inner_shape = option_def.t();
let inner_format = get_inner_format_with_context(inner_shape, namespace_context)?;
Format::Option(Box::new(inner_format))
}
Def::Map(map_def) => {
let key_shape = map_def.k();
let value_shape = map_def.v();
let key_format = get_inner_format_with_context(key_shape, namespace_context)?;
let value_format = get_inner_format_with_context(value_shape, namespace_context)?;
Format::Map {
key: Box::new(key_format),
value: Box::new(value_format),
}
}
Def::Set(set_def) => {
let inner_shape = set_def.t();
Format::Set(Box::new(get_inner_format_with_context(
inner_shape,
namespace_context,
)?))
}
Def::Array(array_def) => {
let inner_shape = array_def.t();
let inner_format = get_inner_format_with_context(inner_shape, namespace_context)?;
Format::TupleArray {
content: Box::new(inner_format),
size: array_def.n,
}
}
Def::Undefined => {
if let Type::User(UserType::Struct(struct_type)) = &shape.ty
&& struct_type.kind == StructKind::Tuple
&& !struct_type.fields.is_empty()
{
let mut tuple_formats = vec![];
for field in struct_type.fields {
let field_shape = field.shape();
let field_format =
get_inner_format_with_context(field_shape, namespace_context)?;
tuple_formats.push(field_format);
}
return Ok(Format::Tuple(tuple_formats));
}
if shape.type_identifier == "()" {
Format::Unit
} else if let Type::Pointer(PointerType::Reference(pt) | PointerType::Raw(pt)) =
&shape.ty
{
let target_shape = pt.target;
get_inner_format_with_context(target_shape, namespace_context)?
} else {
let original_name = get_name(shape)?;
let name = if let Some(namespace) = namespace_context {
if matches!(original_name.namespace, Namespace::Root) {
match namespace {
Namespace::Root => QualifiedTypeName::root(original_name.name.clone()),
Namespace::Named(name) => QualifiedTypeName::namespaced(
name.clone(),
original_name.name.clone(),
),
}
} else {
original_name
}
} else {
original_name
};
Format::TypeName(name)
}
}
Def::Slice(slice_def) => {
let inner_shape = slice_def.t();
Format::Seq(Box::new(get_inner_format_with_context(
inner_shape,
namespace_context,
)?))
}
Def::Pointer(pointer_def) => {
if let Some(inner_shape) = pointer_def.pointee {
get_inner_format_with_context(inner_shape, namespace_context)?
} else {
Format::Unit
}
}
_ => todo!(),
};
Ok(format)
}
#[cfg(test)]
mod tests;
#[cfg(test)]
#[path = "namespace_tests.rs"]
mod namespace_tests;