Last active
March 24, 2024 07:04
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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