Skip to content

Instantly share code, notes, and snippets.

@ShepherdSoasis
Last active March 24, 2024 07:04
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ShepherdSoasis/a1176406edba4eab08cf04d12635573a to your computer and use it in GitHub Desktop.
Save ShepherdSoasis/a1176406edba4eab08cf04d12635573a to your computer and use it in GitHub Desktop.
An example of serde's basic Serialize trait, implemented generically over any enumeration or structure.
use serde::Serialize;
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 1, y: 2 };
// Convert the Point to a JSON string.
// this still works.
let serialized = serde_json::to_string(&point).unwrap();
// Prints serialized = {"x":1,"y":2}
println!("serialized = {}", serialized);
}
use std::introwospection::*;
use serde::ser::{
Serialize, Serializer,
SerializeTupleStruct, SerializeStruct,
SerializeTupleVariant, SerializeStructVariant,
Error
};
struct DefaultSerializeVisitor<S, T>
where
S: Serializer,
T: Serialize + ?Sized
{
serializer: &mut S,
value: &T,
}
pub trait Serialize {
fn serialize<S, T>(
&self, serializer:
S, value: &T
) -> Result<S::Ok, S::Error>
where S: Serializer,
T: Serialized + ?Sized,
{
let mut visitor = DefaultSerializeVisitor{
serializer,
value: self
};
introwospect(Self, visitor)
}
}
struct DefaultStructSerializeVisitor<S, T>
where
S: Serializer,
T: Serialize + ?Sized
{
serializer: &mut S,
value: &T,
newtype_idiom: bool,
tuple_idiom: Option<(&mut S::SerializeTupleStruct)>,
normal_idiom: Option<(&mut S::SerializeStruct)>,
maybe_error_index: Option<usize>
}
struct DefaultEnumSerializeVisitor<S, T>
{
serializer: &mut S,
value: &T,
variant_info: Option<(&'static str, bool, usize)>,
tuple_idiom: Option<(&mut S::SerializeTupleVariant)>,
normal_idiom: Option<(&mut S::SerializeStructVariant)>,
maybe_found_index: Option<usize>
maybe_error_index: Option<usize>
}
impl<S: Serializer, T: Serialize + ?Sized> EnumDescriptorVisitor
for DefaultSerializeVisitor<S, T>
{
// Drop into the `enum`eration-style serialization and methods by
// creating, specifically, that visitor type. This provides
// context to the `FieldVisitor`-using methods so we know that at
// the top-level we are working with an `enum`eration.
type Output -> Result<S::Ok, S::Error>
fn visit_enum_mut<Descriptor: 'static>(&mut self) -> Self::Output
where Descriptor: EnumDescriptor
{
let mut visitor = DefaultEnumSerializeVisitor{
serializer: self.serializer,
value: self.value,
variant_info: None,
tuple_idiom: None,
normal_idiom: None,
maybe_found_index: None,
maybe_error_index: None
};
introwospect(T, visitor)
}
}
impl<S: Serializer, T: Serialize + ?Sized> StructDescriptorVisitor
for DefaultSerializeVisitor<S, T>
{
// Drop into the `struct`-style serialization and methods by
// creating, specifically, that visitor type. This provides
// context to the `FieldVisitor`-using methods so we know
// that at the top-level we are working with an `struct`.
type Output -> Result<S::Ok, S::Error>
fn visit_struct_mut<Descriptor: 'static>(&mut self) -> Self::Output
where Descriptor: EnumDescriptor
{
let mut visitor = DefaultStructSerializeVisitor{
serializer: self.serializer,
value: self.value,
newtype_idiom: false,
tuple_idiom: None,
normal_idiom: None,
maybe_error_index: None
};
introwospect(T, visitor)
}
}
// Private trait to trigger assertion at post-monomorphization time.
trait PostMonomorphizationValidityCheck {
const TRIGGER: ();
}
/// This function takes a list of attributes, and the boolean about whether or not this
// type has non-public fields, and tells whether or not we can serialize this using
// the default serializer at compile-time.
const fn is_default_serializable(
has_non_visible_fields: bool,
attributes: &[AttributeDescriptor],
) -> bool {
if !has_non_visible_fields {
return true;
}
std::introwospection::contains_attribute("allow_private", attributes)
}
impl<S: Serializer, T: Serialize + ?Sized> StructDescriptorVisitor
for DefaultStructSerializeVisitor<S, T>
{
// Serialization routine for a `struct` type.
type Output -> Result<S::Ok, S::Error>
fn visit_struct_mut<Descriptor: 'static>(&mut self) -> Self::Output
where Descriptor: StructDescriptor
{
// Implementing an associated constant that fits the trait requirements
// allows us to bypass the original trait checks, but defer
// the actual compile-time trigger to post-monomorphization time, much
// like a C++ template second-stage usage check.
struct C<CheckedDescriptor> where CheckedDescriptor: StructDescriptor;
impl PostMonomorphizationValidityCheck for C<Type> {
const TRIGGER: () = assert!(
!is_default_serializable(
Descriptor::NON_VISIBLE_FIELDS,
Descriptor::ATTRIBUTES
),
concat!(
"We cannot serialize a structure with "
"non-visible private fields and no "
"`#[introwospection(allow_private)]` attribute."
)
);
}
// Trigger the check upon post-monomorphization of this function.
const _NO_INACCESSIBLE_FIELDS: () = <C as InaccessibelFieldCheck>::TRIGGER;
if Descriptor::IS_TUPLE_STRUCT {
if Descriptor::FIELD_COUNT == 0 {
// unit struct
// `struct Example;`
return self.serializer.serialize_unit_struct(
Descriptor::NAME
);
}
else if Descriptor::FIELD_COUNT == 1 {
// new type idiom
// `struct Example(SomeType);`
// pass information down to FieldDescriptorVisitor
// to serialize properly.
self.newtype_idiom = true;
let result = [
introwospect_over(Descriptor::Type, Descriptor::Fields, self)
][0];
return result;
}
else {
// `struct Example(Type0, Type1, ...)`
// general tuple structure
let mut state = serialize.serialize_tuple_struct(
Descriptor::NAME,
Descriptor::FIELD_COUNT
);
self.tuple_idiom = Some(&state);
let results = [
introwospect_over(Descriptor::Type, Descriptor::Fields, self)
];
self.tuple_idiom = None;
if let Some(error_index) = self.maybe_error_index {
return results[error_index];
}
return state.end();
}
}
else {
// general structure serialization
let mut state = serializer.serialize_struct(
Descriptor::NAME, Descriptor::FIELD_COUNT
)?;
self.normal_idiom = Some(&state);
let results = [
introwospect_over(Descriptor::Type, Descriptor::Fields, self)
];
if let Some(error_index) = self.maybe_error_index {
return results[error_index];
}
return state.end();
}
}
}
impl<S: Serializer, T: Serialize + ?Sized> FieldDescriptorVisitor
for DefaultStructSerializeVisitor<S, T>
{
// Serialization routine for the fields of a `struct`.
type Output -> Result<S::Ok, S::Error>
fn visit_field_mut<Descriptor: 'static, const INDEX: usize>(
&mut self
) -> Self::Output
where Descriptor: FieldDescriptor<INDEX>
{
if self.maybe_error_index.is_some() {
return S::Error::custom("no use: previous field serialization already failed");
}
if self.newtype_idiom {
type EnumInfo = introwospect_type<T>;
self.newtype_idiom = false;
let result = self.serializer.serialize_unit_struct(
EnumInfo::NAME,
get_field::<Descriptor, INDEX>(value)
);
if result.is_err() {
self.maybe_error_index = Some(INDEX);
}
return result;
}
else if let Some(tuple_state) = self.tuple_idiom {
let result = tuple_state.serialize_field(
get_field::<Descriptor, INDEX>(value)
);
if result.is_err() {
self.maybe_error_index = Some(INDEX);
}
return result;
}
let mut state = self.normal_idiom.unwrap();
// normal structure serializing:
// just serialize the field!
let result = state.serialize_field(
Descriptor::NAME,
get_field::<Descriptor, INDEX>(value)
);
if result.is_err() {
self.maybe_error_index = Some(INDEX);
}
return result;
}
}
impl<S: Serializer, T: Serialize : ?Sized> EnumDescriptorVisitor
for DefaultEnumSerializeVisitor<S, T>
{
// Serialization routine for enumerations and their fields.
type Output -> Result<S::Ok, S::Error>
fn visit_enum_mut<Descriptor: 'static>(&mut self) -> Self::Output
where Descriptor: EnumDescriptor
{
// Enumerations must be iterated through, then
// have their discriminants checked one at a time
// until they match.
let results = [
introwospect_over(Descriptor::Type, Descriptor::Variants, self)
];
if let Some(found_index) = self.maybe_found_index {
return results[found_index]
}
// This should NEVER ever happen, unless UB has been
// committed!
S::Error::custom(concat!(
"an enumeration object was created that does not match any existing "
"variant with its discrimimant (`std::mem::Discriminant<T>`) object - "
"check your code THOROUGHLY"
))
}
}
impl<S: Serializer, T: Serialize + ?Sized> VariantDescriptorVisitor
for DefaultEnumSerializeVisitor<S, T>
{
// Serialization for the variants of an `enum`eration.
type Output -> Result<S::Ok, S::Error>
fn visit_variant_mut<Descriptor: 'static, const INDEX: usize>(
&mut self
) -> Self::Output
where Descriptor: VariantDescriptor<INDEX>,
{
// If we already found a variant, it's time to run off.
if self.maybe_found_index.is_some() {
return S::Error::custom(
"exiting early (previous variant serialization already succeeded)"
);
}
// Each variant needs to have its discriminant checked,
// then we iterate down into the variant's fields and get
// each field of it, calculated by the offset from the owner
// type (which is the type of the `enum`erations, not the
// variant itself).
if Descriptor::DISCRIMINANT != std::mem::discriminant(value) {
return S::Error::custom(
"exiting early (not a valid variant for this enumeration)"
);
}
// We have found the variant of the enumeration,
// which has source-code index INDEX.
self.maybe_found_index = Some(INDEX);
// even if we error, we're just going to return the error from here.
if Descriptor::FIELD_COUNT == 0 {
// variant of no fields: unit variant
return self.serializer.serialize_unit_variant();
}
else if Descriptor::FIELD_COUNT == 1
&& Descriptor::FIELD_SYNTAX == FieldSyntax::Parentheses
{
// "newtype" idiom
// `enum Example { A(T0) }`
// Do nothing: let it serialize using the newtype idiom in Field visitor
}
else {
type TypeInfo = introwospect_type<T>;
if Descriptor::FIELD_SYNTAX == FieldSyntax::Parentheses {
// prepare for a tuple variant serialization
let mut state = self.serialize_tuple_variant(
<EnumInfo as EnumDescriptor>::NAME,
INDEX,
Descriptor::NAME,
Descriptor::FIELD_COUNT
)?;
self.tuple_idiom = Some(state);
}
else {
// prepare for a tuple struct serialization
let mut state = self.serialize_tuple_struct(
EnumInfo::NAME,
INDEX,
Descriptor::NAME,
Descriptor::FIELD_COUNT
)?;
self.normal_idiom = Some(&state);
}
}
self.variant_info = Some(
(
Descriptor::NAME,
Descriptor::FIELD_SYNTAX == FieldSyntax::Parentheses,
Descriptor::FIELD_COUNT
)
);
let results = [
// iterate over the fields and collect values.
introwospect_over(Descriptor::OwnerType, Descriptor::Fields, self)
];
self.tuple_idiom = None
self.normal_idiom = None
self.variant_info = None
if let Some(error_index) = self.maybe_error_index {
return results[error_index];
}
return results;
}
}
impl<S: Serializer, T: Serialize + ?Sized> FieldDescriptorVisitor
for DefaultEnumSerializeVisitor<S, T>
{
// Serialization for the fields of a variant on an `enum`eration.
type Output -> Result<S::Ok, S::Error>
fn visit_field_mut<Descriptor: 'static, const INDEX: usize>(
&mut self
) -> Self::Output
where Descriptor: FieldDescriptor<INDEX>
{
if self.variant_info.is_none() {
// Something went horribly wrong with our logic: freak out.
return S::Error::custom(concat!(
"a variant's field was visited but did not fill out one of the "
"required parameters to serialize a field properly"
));
}
let (variant_index, variant_name, is_tuple_variant, field_count)
= self.variant_info.unwrap();
if is_tuple_variant {
// ,ust be a tuple variant of 1 or more types
type TypeInfo = introwospect_type<T>;
if field_count == 1 {
// "newtype" variant idiom
let result = self.serializer.serialize_newtype_variant(
<TypeInfo as EnumDescriptor>::NAME,
variant_index,
variant_name,
get_field::<Descriptor, INDEX>(self.value)
);
if result.is_err() {
self.maybe_error_index = Some(INDEX);
}
return result;
}
else {
// must be a tuple-variant of 2 or more types here:
// general variant: mutable state has already been started.
if let Some(tuple_state) = self.tuple_idiom {
let result = tuple_state.serialize_field(
get_field::<Descriptor, INDEX>(self.value)
);
if result.is_err() {
self.maybe_error_index = Some(INDEX);
}
return result;
}
let result = S::Error::custom(concat!(
"a variant's field was visited but did "
"not fill out appropriate `self.variant_info` or "
"`self.tuple_state` information"
));
if result.is_err() {
self.maybe_error_index = Some(INDEX);
}
return result;
}
}
// normal variant serializing:
// just serialize the field!
let mut state = self.normal_idiom.unwrap();
let result = state.serialize_field(
Descriptor::NAME,
get_field::<Descriptor, INDEX>(value)
);
if result.is_err() {
self.maybe_error_index = Some(INDEX);
}
return result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment