use std::io::{Read, Write};
use rbx_reflection::{PropertyDescriptor, PropertyKind, PropertySerialization, ReflectionDatabase};
use crate::{
deserializer_core::XmlEventReader,
error::{DecodeError, EncodeError},
serializer_core::{XmlEventWriter, XmlWriteEvent},
};
pub trait XmlType: Sized {
const XML_TAG_NAME: &'static str;
fn read_xml<R: Read>(reader: &mut XmlEventReader<R>) -> Result<Self, DecodeError>;
fn write_xml<W: Write>(&self, writer: &mut XmlEventWriter<W>) -> Result<(), EncodeError>;
fn read_outer_xml<R: Read>(reader: &mut XmlEventReader<R>) -> Result<Self, DecodeError> {
reader.expect_start_with_name(Self::XML_TAG_NAME)?;
let value = Self::read_xml(reader)?;
reader.expect_end_with_name(Self::XML_TAG_NAME)?;
Ok(value)
}
fn write_outer_xml<W: Write>(
&self,
name: &str,
writer: &mut XmlEventWriter<W>,
) -> Result<(), EncodeError> {
writer.write(XmlWriteEvent::start_element(Self::XML_TAG_NAME).attr("name", name))?;
self.write_xml(writer)?;
writer.write(XmlWriteEvent::end_element())
}
}
pub fn find_canonical_property_descriptor<'db>(
class_name: &str,
property_name: &str,
database: &'db ReflectionDatabase<'db>,
) -> Option<&'db PropertyDescriptor<'db>> {
find_property_descriptors(class_name, property_name, database)
.map(|(canonical, _serialized)| canonical)
}
pub fn find_serialized_property_descriptor<'db>(
class_name: &str,
property_name: &str,
database: &'db ReflectionDatabase<'db>,
) -> Option<&'db PropertyDescriptor<'db>> {
find_property_descriptors(class_name, property_name, database)
.map(|(_canonical, serialized)| serialized)
}
fn find_property_descriptors<'db>(
class_name: &str,
property_name: &str,
database: &'db ReflectionDatabase<'db>,
) -> Option<(&'db PropertyDescriptor<'db>, &'db PropertyDescriptor<'db>)> {
let class_descriptor = database.classes.get(class_name)?;
let mut current_class_descriptor = class_descriptor;
loop {
if let Some(property_descriptor) = current_class_descriptor.properties.get(property_name) {
match &property_descriptor.kind {
PropertyKind::Canonical { serialization } => match serialization {
PropertySerialization::Serializes | PropertySerialization::Migrate { .. } => {
return Some((property_descriptor, property_descriptor))
}
PropertySerialization::DoesNotSerialize => {
return None;
}
PropertySerialization::SerializesAs(serialized_name) => {
let serialized_descriptor = current_class_descriptor
.properties
.get(serialized_name.as_ref())
.unwrap();
return Some((property_descriptor, serialized_descriptor));
}
_ => unimplemented!(),
},
PropertyKind::Alias { alias_for } => {
let canonical_descriptor = current_class_descriptor
.properties
.get(alias_for.as_ref())
.unwrap();
match &canonical_descriptor.kind {
PropertyKind::Canonical { serialization } => match serialization {
PropertySerialization::Serializes
| PropertySerialization::Migrate { .. } => {
return Some((canonical_descriptor, canonical_descriptor))
}
PropertySerialization::DoesNotSerialize => {
return None;
}
PropertySerialization::SerializesAs(serialized_name) => {
let serialized_descriptor = current_class_descriptor
.properties
.get(serialized_name.as_ref())
.unwrap();
return Some((canonical_descriptor, serialized_descriptor));
}
_ => unimplemented!(),
},
_ => return None,
}
}
_ => unimplemented!(),
}
}
if let Some(superclass_name) = ¤t_class_descriptor.superclass {
current_class_descriptor = database
.classes
.get(superclass_name)
.expect("Superclass in reflection database didn't exist");
} else {
return None;
}
}
}