Skip to content

Commit

Permalink
Adds Ref to rbx_xml and rbx_tree (#8)
Browse files Browse the repository at this point in the history
* Add Ref to rbx_xml and rbx_tree

* Update README

* Add None round trip test

* Tabs -> Spaces

* Rename reff to ref with raw identifier
  • Loading branch information
Kampfkarren authored and LPGhatguy committed Feb 14, 2019
1 parent 7ff2691 commit e019edb
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Roblox reflection information for working with Instances in external tooling.
| QFont | `Studio.Font` ||||
| Ray | `RayValue.Value` ||||
| Rect2D | `ImageButton.SliceCenter` ||||
| Ref | `Model.PrimaryPart` | | ||
| Ref | `Model.PrimaryPart` | | ||
| Region3int16 | `Terrain.MaxExtents` ||||
| String | `Instance.Name` ||||
| UDim | `UIListLayout.Padding` ||||
Expand Down
10 changes: 9 additions & 1 deletion rbx_tree/src/value.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::id::RbxId;
use serde_derive::{Serialize, Deserialize};

/// An enum that can hold any of the types that [`RbxValue`] can.
Expand All @@ -14,6 +15,7 @@ pub enum RbxValueType {
Float32,
Int32,
PhysicalProperties,
Ref,
String,
Vector2,
Vector2int16,
Expand Down Expand Up @@ -93,7 +95,12 @@ pub enum RbxValue {
#[serde(rename_all = "PascalCase")]
PhysicalProperties {
value: Option<PhysicalProperties>,
}
},

#[serde(rename_all = "PascalCase")]
Ref {
value: Option<RbxId>,
},
}

impl RbxValue {
Expand All @@ -111,6 +118,7 @@ impl RbxValue {
RbxValue::Float32 { .. } => RbxValueType::Float32,
RbxValue::Int32 { .. } => RbxValueType::Int32,
RbxValue::PhysicalProperties { .. } => RbxValueType::PhysicalProperties,
RbxValue::Ref { .. } => RbxValueType::Ref,
RbxValue::String { .. } => RbxValueType::String,
RbxValue::Vector2 { .. } => RbxValueType::Vector2,
RbxValue::Vector2int16 { .. } => RbxValueType::Vector2int16,
Expand Down
64 changes: 64 additions & 0 deletions rbx_xml/src/deserializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::{
deserialize_float32,
deserialize_int32,
deserialize_physical_properties,
deserialize_ref,
deserialize_string,
deserialize_vector2,
deserialize_vector2int16,
Expand Down Expand Up @@ -462,6 +463,7 @@ fn deserialize_properties<R: Read>(
"Color3uint8" => deserialize_color3uint8(reader)?,
"CoordinateFrame" => deserialize_cframe(reader)?,
"PhysicalProperties" => deserialize_physical_properties(reader)?,
"Ref" => deserialize_ref(reader)?,
unknown => {
warn!("rbx_xml can't decode properties of type {}", unknown);
return Err(DecodeError::Message("don't know how to decode this prop type"));
Expand Down Expand Up @@ -804,4 +806,66 @@ mod test {
],
}));
}

#[test]
fn with_ref_some() {
let _ = env_logger::try_init();
let document = r#"
<roblox version="4">
<Item class="Folder" referent="RBX1B9CDD1FD0884F76BFE6091C1731E1FB">
</Item>
<Item class="ObjectValue" referent="hello">
<Properties>
<string name="Name">Test</string>
<Ref name="Value">RBX1B9CDD1FD0884F76BFE6091C1731E1FB</Ref>
</Properties>
</Item>
</roblox>
"#;

let mut tree = new_data_model();
let root_id = tree.get_root_id();

decode_str(&mut tree, root_id, document).expect("should work D:");

let descendant = tree.descendants(root_id).nth(1).unwrap();
assert_eq!(descendant.name, "Test");
assert_eq!(descendant.class_name, "ObjectValue");

let value = descendant.properties.get("Value").expect("no value property");
if let RbxValue::Ref { value } = value {
let value = value.expect("ref was None");
assert_eq!(value.to_string(), "1b9cdd1f-d088-4f76-bfe6-091c1731e1fb");
} else {
panic!("rbxvalue was not ref, but instead {:?}", value.get_type())
}
}

#[test]
fn with_ref_none() {
let _ = env_logger::try_init();
let document = r#"
<roblox version="4">
<Item class="ObjectValue" referent="hello">
<Properties>
<string name="Name">Test</string>
<Ref name="Value">null</Ref>
</Properties>
</Item>
</roblox>
"#;

let mut tree = new_data_model();
let root_id = tree.get_root_id();

decode_str(&mut tree, root_id, document).expect("should work D:");

let descendant = tree.descendants(root_id).nth(1).unwrap();
assert_eq!(descendant.name, "Test");
assert_eq!(descendant.class_name, "ObjectValue");

let value = descendant.properties.get("Value").expect("no value property");
assert_eq!(value, &RbxValue::Ref { value: None });
}
}
2 changes: 2 additions & 0 deletions rbx_xml/src/serializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::{
serialize_float32,
serialize_int32,
serialize_physical_properties,
serialize_ref,
serialize_string,
serialize_vector2,
serialize_vector2int16,
Expand Down Expand Up @@ -130,6 +131,7 @@ fn serialize_value<W: Write>(
RbxValue::Int32 { value } => serialize_int32(writer, xml_name, *value),
RbxValue::Enum { value } => serialize_enum(writer, xml_name, *value),
RbxValue::PhysicalProperties { value } => serialize_physical_properties(writer, xml_name, *value),
RbxValue::Ref { value } => serialize_ref(writer, xml_name, *value),
RbxValue::CFrame { value } => serialize_cframe(writer, xml_name, *value),
RbxValue::Color3 { value } => serialize_color3(writer, xml_name, *value),
RbxValue::Color3uint8 { value } => serialize_color3uint8(writer, xml_name, *value),
Expand Down
2 changes: 2 additions & 0 deletions rbx_xml/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod colors;
mod enumeration;
mod numbers;
mod physical_properties;
mod r#ref;
mod string;
mod vectors;

Expand All @@ -15,5 +16,6 @@ pub use self::colors::*;
pub use self::enumeration::*;
pub use self::numbers::*;
pub use self::physical_properties::*;
pub use self::r#ref::*;
pub use self::string::*;
pub use self::vectors::*;
91 changes: 91 additions & 0 deletions rbx_xml/src/types/ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use std::io::{Read, Write};

use rbx_tree::{RbxId, RbxValue};

use crate::{
deserializer::{DecodeError, EventIterator},
serializer::{EncodeError, XmlWriteEvent, XmlEventWriter},
};

const REF_HEADER: &str = "RBX";

pub fn serialize_ref<W: Write>(
writer: &mut XmlEventWriter<W>,
name: &str,
value: Option<RbxId>,
) -> Result<(), EncodeError> {
writer.write(XmlWriteEvent::start_element("Ref").attr("name", name))?;
writer.write(XmlWriteEvent::characters(&if let Some(id) = value {
format!(
"{}{}",
REF_HEADER,
id.to_string()
.chars()
.filter(|x| *x != '-')
.map(|c| c.to_ascii_uppercase())
.collect::<String>()
)
} else {
"null".to_string()
}))?;
writer.write(XmlWriteEvent::end_element())?;

Ok(())
}

pub fn deserialize_ref<R: Read>(reader: &mut EventIterator<R>) -> Result<RbxValue, DecodeError> {
let ref_contents = reader.read_tag_contents("Ref")?;

Ok(RbxValue::Ref {
value: if ref_contents.starts_with(REF_HEADER) {
RbxId::parse_str(&ref_contents[REF_HEADER.len()..])
} else {
None
},
})
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn round_trip_ref_some() {
let _ = env_logger::try_init();

let test_input: Option<RbxId> = RbxId::parse_str("1b9cdd1f-d088-4f76-bfe6-091c1731e1fb");
let mut buffer = Vec::new();

let mut writer = XmlEventWriter::from_output(&mut buffer);
serialize_ref(&mut writer, "foo", test_input).unwrap();
println!("{}", std::str::from_utf8(&buffer).unwrap());

let mut reader = EventIterator::from_source(buffer.as_slice());
reader.next().unwrap().unwrap(); // Eat StartDocument event
let value = deserialize_ref(&mut reader).unwrap();

assert_eq!(value, RbxValue::Ref {
value: test_input,
});
}

#[test]
fn round_trip_ref_none() {
let _ = env_logger::try_init();

let test_input: Option<RbxId> = None;
let mut buffer = Vec::new();

let mut writer = XmlEventWriter::from_output(&mut buffer);
serialize_ref(&mut writer, "foo", test_input).unwrap();
println!("{}", std::str::from_utf8(&buffer).unwrap());

let mut reader = EventIterator::from_source(buffer.as_slice());
reader.next().unwrap().unwrap(); // Eat StartDocument event
let value = deserialize_ref(&mut reader).unwrap();

assert_eq!(value, RbxValue::Ref {
value: test_input,
});
}
}

0 comments on commit e019edb

Please sign in to comment.