rbx_xml/types/
referent.rs

1//! Referents are strange compared to other property types.
2//!
3//! Refs require extra information, which is why they aren't part of the
4//! `XmlType`-implementing family of structs like other types. I think this is
5//! a better approach than widening the values that `XmlType` accepts just for
6//! this type.
7//!
8//! Specifically, deserializing refs needs access to a special list of
9//! 'rewrites'. It's used as part of a second pass to make sure that refs
10//! pointing to instances that we haven't reached yet work okay.
11
12use std::io::{Read, Write};
13
14use rbx_dom_weak::types::Ref;
15
16use crate::{
17    deserializer::ParseState,
18    deserializer_core::XmlEventReader,
19    error::{DecodeError, EncodeError},
20    serializer::EmitState,
21    serializer_core::{XmlEventWriter, XmlWriteEvent},
22};
23
24pub const XML_TAG_NAME: &str = "Ref";
25
26pub fn write_ref<W: Write>(
27    writer: &mut XmlEventWriter<W>,
28    xml_property_name: &str,
29    value: Ref,
30    state: &mut EmitState,
31) -> Result<(), EncodeError> {
32    writer.write(XmlWriteEvent::start_element(XML_TAG_NAME).attr("name", xml_property_name))?;
33
34    if value.is_none() {
35        writer.write(XmlWriteEvent::characters("null"))?;
36    } else {
37        writer.write_characters(state.map_id(value))?;
38    }
39
40    writer.write(XmlWriteEvent::end_element())?;
41
42    Ok(())
43}
44
45pub fn read_ref<R: Read>(
46    reader: &mut XmlEventReader<R>,
47    id: Ref,
48    property_name: &str,
49    state: &mut ParseState,
50) -> Result<Ref, DecodeError> {
51    let ref_contents = reader.read_tag_contents(XML_TAG_NAME)?;
52
53    if ref_contents != "null" {
54        // We need to rewrite this property as part of a follow-up pass.
55        //
56        // We might not know which ID this referent points to yet, so instead of
57        // trying to handle the case where we do here, we just let all referents
58        // get written later.
59        state.add_referent_rewrite(id, property_name.to_owned(), ref_contents);
60    }
61
62    Ok(Ref::none())
63}