mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Fully implement document methods for converting to/from instances
This commit is contained in:
parent
7553e91dc6
commit
178f7b41ab
4 changed files with 125 additions and 49 deletions
|
@ -13,11 +13,11 @@ pub enum DocumentError {
|
||||||
WriteError(String),
|
WriteError(String),
|
||||||
#[error("Failed to convert into a DataModel - the given document is not a place")]
|
#[error("Failed to convert into a DataModel - the given document is not a place")]
|
||||||
IntoDataModelInvalidArgs,
|
IntoDataModelInvalidArgs,
|
||||||
#[error("Failed to convert into array of Instances - the given document is a place")]
|
#[error("Failed to convert into array of Instances - the given document is a model")]
|
||||||
IntoInstanceArrayInvalidArgs,
|
IntoInstanceArrayInvalidArgs,
|
||||||
#[error("Failed to convert into a document - the given instance is not a DataModel")]
|
#[error("Failed to convert into a place - the given instance is not a DataModel")]
|
||||||
FromDataModelInvalidArgs,
|
FromDataModelInvalidArgs,
|
||||||
#[error("Failed to convert into a document - a given instances is a DataModel")]
|
#[error("Failed to convert into a model - a given instance is a DataModel")]
|
||||||
FromInstanceArrayInvalidArgs,
|
FromInstanceArrayInvalidArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use std::sync::{Arc, RwLock};
|
use rbx_dom_weak::{InstanceBuilder as DomInstanceBuilder, WeakDom};
|
||||||
|
|
||||||
use rbx_dom_weak::WeakDom;
|
|
||||||
use rbx_xml::{
|
use rbx_xml::{
|
||||||
DecodeOptions as XmlDecodeOptions, DecodePropertyBehavior as XmlDecodePropertyBehavior,
|
DecodeOptions as XmlDecodeOptions, DecodePropertyBehavior as XmlDecodePropertyBehavior,
|
||||||
EncodeOptions as XmlEncodeOptions, EncodePropertyBehavior as XmlEncodePropertyBehavior,
|
EncodeOptions as XmlEncodeOptions, EncodePropertyBehavior as XmlEncodePropertyBehavior,
|
||||||
|
@ -22,6 +20,10 @@ pub type DocumentResult<T> = Result<T, DocumentError>;
|
||||||
A container for [`rbx_dom_weak::WeakDom`] that also takes care of
|
A container for [`rbx_dom_weak::WeakDom`] that also takes care of
|
||||||
reading and writing different kinds and formats of roblox files.
|
reading and writing different kinds and formats of roblox files.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Code Sample #1
|
||||||
|
|
||||||
```rust ignore
|
```rust ignore
|
||||||
// Reading a document from a file
|
// Reading a document from a file
|
||||||
|
|
||||||
|
@ -37,12 +39,28 @@ pub type DocumentResult<T> = Result<T, DocumentError>;
|
||||||
|
|
||||||
std::fs::write(&file_path, document.to_bytes()?)?;
|
std::fs::write(&file_path, document.to_bytes()?)?;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Code Sample #2
|
||||||
|
|
||||||
|
```rust ignore
|
||||||
|
// Converting a Document to a DataModel or model child instances
|
||||||
|
let data_model = document.into_data_model_instance()?;
|
||||||
|
|
||||||
|
let model_children = document.into_instance_array()?;
|
||||||
|
|
||||||
|
// Converting a DataModel or model child instances into a Document
|
||||||
|
let place_doc = Document::from_data_model_instance(data_model)?;
|
||||||
|
|
||||||
|
let model_doc = Document::from_instance_array(model_children)?;
|
||||||
|
```
|
||||||
*/
|
*/
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct Document {
|
pub struct Document {
|
||||||
kind: DocumentKind,
|
kind: DocumentKind,
|
||||||
format: DocumentFormat,
|
format: DocumentFormat,
|
||||||
dom: Arc<RwLock<WeakDom>>,
|
dom: WeakDom,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Document {
|
impl Document {
|
||||||
|
@ -96,11 +114,7 @@ impl Document {
|
||||||
pub fn from_bytes_auto(bytes: impl AsRef<[u8]>) -> DocumentResult<Self> {
|
pub fn from_bytes_auto(bytes: impl AsRef<[u8]>) -> DocumentResult<Self> {
|
||||||
let (format, dom) = Self::from_bytes_inner(bytes)?;
|
let (format, dom) = Self::from_bytes_inner(bytes)?;
|
||||||
let kind = DocumentKind::from_weak_dom(&dom).ok_or(DocumentError::UnknownKind)?;
|
let kind = DocumentKind::from_weak_dom(&dom).ok_or(DocumentError::UnknownKind)?;
|
||||||
Ok(Self {
|
Ok(Self { kind, format, dom })
|
||||||
kind,
|
|
||||||
format,
|
|
||||||
dom: Arc::new(RwLock::new(dom)),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,17 +122,10 @@ impl Document {
|
||||||
|
|
||||||
This will automatically handle and detect if the document
|
This will automatically handle and detect if the document
|
||||||
should be decoded using a roblox binary or roblox xml format.
|
should be decoded using a roblox binary or roblox xml format.
|
||||||
|
|
||||||
Note that passing [`DocumentKind`] enum values other than [`DocumentKind::Place`] and
|
|
||||||
[`DocumentKind::Model`] is possible but should only be done within the `lune-roblox` crate.
|
|
||||||
*/
|
*/
|
||||||
pub fn from_bytes(bytes: impl AsRef<[u8]>, kind: DocumentKind) -> DocumentResult<Self> {
|
pub fn from_bytes(bytes: impl AsRef<[u8]>, kind: DocumentKind) -> DocumentResult<Self> {
|
||||||
let (format, dom) = Self::from_bytes_inner(bytes)?;
|
let (format, dom) = Self::from_bytes_inner(bytes)?;
|
||||||
Ok(Self {
|
Ok(Self { kind, format, dom })
|
||||||
kind,
|
|
||||||
format,
|
|
||||||
dom: Arc::new(RwLock::new(dom)),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -138,15 +145,16 @@ impl Document {
|
||||||
be written to a file or sent over the network.
|
be written to a file or sent over the network.
|
||||||
*/
|
*/
|
||||||
pub fn to_bytes_with_format(&self, format: DocumentFormat) -> DocumentResult<Vec<u8>> {
|
pub fn to_bytes_with_format(&self, format: DocumentFormat) -> DocumentResult<Vec<u8>> {
|
||||||
let dom = self.dom.try_read().expect("Failed to lock dom");
|
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
match format {
|
match format {
|
||||||
DocumentFormat::Binary => rbx_binary::to_writer(&mut bytes, &dom, &[dom.root_ref()])
|
DocumentFormat::Binary => {
|
||||||
.map_err(|err| DocumentError::WriteError(err.to_string())),
|
rbx_binary::to_writer(&mut bytes, &self.dom, &[self.dom.root_ref()])
|
||||||
|
.map_err(|err| DocumentError::WriteError(err.to_string()))
|
||||||
|
}
|
||||||
DocumentFormat::Xml => {
|
DocumentFormat::Xml => {
|
||||||
let xml_options = XmlEncodeOptions::new()
|
let xml_options = XmlEncodeOptions::new()
|
||||||
.property_behavior(XmlEncodePropertyBehavior::WriteUnknown);
|
.property_behavior(XmlEncodePropertyBehavior::WriteUnknown);
|
||||||
rbx_xml::to_writer(&mut bytes, &dom, &[dom.root_ref()], xml_options)
|
rbx_xml::to_writer(&mut bytes, &self.dom, &[self.dom.root_ref()], xml_options)
|
||||||
.map_err(|err| DocumentError::WriteError(err.to_string()))
|
.map_err(|err| DocumentError::WriteError(err.to_string()))
|
||||||
}
|
}
|
||||||
}?;
|
}?;
|
||||||
|
@ -175,33 +183,53 @@ impl Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Creates a DataModel instance out of this document.
|
Creates a DataModel instance out of this place document.
|
||||||
|
|
||||||
Will error if the document is not a place.
|
Will error if the document is not a place.
|
||||||
*/
|
*/
|
||||||
pub fn into_data_model_instance(self) -> DocumentResult<Instance> {
|
pub fn into_data_model_instance(mut self) -> DocumentResult<Instance> {
|
||||||
if self.kind != DocumentKind::Place {
|
if self.kind != DocumentKind::Place {
|
||||||
return Err(DocumentError::IntoDataModelInvalidArgs);
|
return Err(DocumentError::IntoDataModelInvalidArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
todo!()
|
let dom_root = self.dom.root_ref();
|
||||||
|
|
||||||
|
let data_model_ref = self
|
||||||
|
.dom
|
||||||
|
.insert(dom_root, DomInstanceBuilder::new("DataModel"));
|
||||||
|
let data_model_child_refs = self.dom.root().children().to_vec();
|
||||||
|
|
||||||
|
for child_ref in data_model_child_refs {
|
||||||
|
if child_ref != data_model_ref {
|
||||||
|
self.dom.transfer_within(child_ref, data_model_ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Instance::from_external_dom(&mut self.dom, data_model_ref))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Creates an array of instances out of this document.
|
Creates an array of instances out of this model document.
|
||||||
|
|
||||||
Will error if the document is not a model.
|
Will error if the document is not a model.
|
||||||
*/
|
*/
|
||||||
pub fn into_instance_array(self) -> DocumentResult<Vec<Instance>> {
|
pub fn into_instance_array(mut self) -> DocumentResult<Vec<Instance>> {
|
||||||
if self.kind != DocumentKind::Model {
|
if self.kind != DocumentKind::Model {
|
||||||
return Err(DocumentError::IntoInstanceArrayInvalidArgs);
|
return Err(DocumentError::IntoInstanceArrayInvalidArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
todo!()
|
let dom_child_refs = self.dom.root().children().to_vec();
|
||||||
|
|
||||||
|
let root_child_instances = dom_child_refs
|
||||||
|
.into_iter()
|
||||||
|
.map(|child_ref| Instance::from_external_dom(&mut self.dom, child_ref))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(root_child_instances)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Creates a Document out of a DataModel instance.
|
Creates a place document out of a DataModel instance.
|
||||||
|
|
||||||
Will error if the instance is not a DataModel.
|
Will error if the instance is not a DataModel.
|
||||||
*/
|
*/
|
||||||
|
@ -210,13 +238,23 @@ impl Document {
|
||||||
return Err(DocumentError::FromDataModelInvalidArgs);
|
return Err(DocumentError::FromDataModelInvalidArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
todo!()
|
let mut dom = WeakDom::new(DomInstanceBuilder::new("ROOT"));
|
||||||
|
|
||||||
|
for data_model_child in instance.get_children() {
|
||||||
|
data_model_child.into_external_dom(&mut dom);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
kind: DocumentKind::Place,
|
||||||
|
format: DocumentFormat::default(),
|
||||||
|
dom,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Creates an array of instances out of this document.
|
Creates a model document out of an array of instances.
|
||||||
|
|
||||||
Will error if the document is not a model.
|
Will error if any of the instances is a DataModel.
|
||||||
*/
|
*/
|
||||||
pub fn from_instance_array(instances: Vec<Instance>) -> DocumentResult<Self> {
|
pub fn from_instance_array(instances: Vec<Instance>) -> DocumentResult<Self> {
|
||||||
for instance in &instances {
|
for instance in &instances {
|
||||||
|
@ -225,6 +263,16 @@ impl Document {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
todo!()
|
let mut dom = WeakDom::new(DomInstanceBuilder::new("ROOT"));
|
||||||
|
|
||||||
|
for instance in instances {
|
||||||
|
instance.into_external_dom(&mut dom);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
kind: DocumentKind::Model,
|
||||||
|
format: DocumentFormat::default(),
|
||||||
|
dom,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,9 @@ pub struct Instance {
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
/**
|
/**
|
||||||
Creates a new `Instance` from a document and dom object ref.
|
Creates a new `Instance` from an existing dom object ref.
|
||||||
|
|
||||||
|
Panics if the instance does not exist in the internal dom.
|
||||||
*/
|
*/
|
||||||
fn new(dom_ref: DomRef) -> Self {
|
fn new(dom_ref: DomRef) -> Self {
|
||||||
let reader = INTERNAL_DOM
|
let reader = INTERNAL_DOM
|
||||||
|
@ -50,9 +52,7 @@ impl Instance {
|
||||||
/**
|
/**
|
||||||
Creates a new orphaned `Instance` with a given class name.
|
Creates a new orphaned `Instance` with a given class name.
|
||||||
|
|
||||||
An orphaned instance does not belong to any particular document and
|
An orphaned instance is an instance at the root of a weak dom.
|
||||||
is instead part of the internal weak dom for orphaned lua instances,
|
|
||||||
it can however be re-parented to a "real" document and weak dom.
|
|
||||||
*/
|
*/
|
||||||
fn new_orphaned(class_name: impl AsRef<str>) -> Self {
|
fn new_orphaned(class_name: impl AsRef<str>) -> Self {
|
||||||
let mut dom = INTERNAL_DOM
|
let mut dom = INTERNAL_DOM
|
||||||
|
@ -73,6 +73,43 @@ impl Instance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a new orphaned `Instance` by transferring
|
||||||
|
it from an external weak dom to the internal one.
|
||||||
|
|
||||||
|
An orphaned instance is an instance at the root of a weak dom.
|
||||||
|
*/
|
||||||
|
pub fn from_external_dom(external_dom: &mut WeakDom, external_dom_ref: DomRef) -> Self {
|
||||||
|
{
|
||||||
|
let mut dom = INTERNAL_DOM
|
||||||
|
.try_write()
|
||||||
|
.expect("Failed to get write access to document");
|
||||||
|
let dom_root = dom.root_ref();
|
||||||
|
|
||||||
|
external_dom.transfer(external_dom_ref, &mut dom, dom_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::new(external_dom_ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Transfers an instance to an external weak dom.
|
||||||
|
|
||||||
|
This will place the instance at the root of the weak dom and return its referent.
|
||||||
|
*/
|
||||||
|
pub fn into_external_dom(self, external_dom: &mut WeakDom) -> DomRef {
|
||||||
|
let mut dom = INTERNAL_DOM
|
||||||
|
.try_write()
|
||||||
|
.expect("Failed to get write access to document");
|
||||||
|
|
||||||
|
let internal_dom_ref = self.dom_ref;
|
||||||
|
let external_root_ref = external_dom.root_ref();
|
||||||
|
|
||||||
|
dom.transfer(internal_dom_ref, external_dom, external_root_ref);
|
||||||
|
|
||||||
|
internal_dom_ref
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Clones the instance and all of its descendants, and orphans it.
|
Clones the instance and all of its descendants, and orphans it.
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
use std::sync::{Arc, RwLock};
|
|
||||||
|
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
use rbx_dom_weak::{InstanceBuilder as DomInstanceBuilder, WeakDom};
|
|
||||||
|
|
||||||
use crate::instance::Instance;
|
use crate::instance::Instance;
|
||||||
|
|
||||||
|
@ -56,12 +53,6 @@ fn make_all_datatypes(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaValue)>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
|
pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
|
||||||
// Create an internal weak dom that will be used
|
|
||||||
// for any instance that does not yet have a parent
|
|
||||||
let internal_root = DomInstanceBuilder::new("<<<ROOT>>>");
|
|
||||||
let internal_dom = Arc::new(RwLock::new(WeakDom::new(internal_root)));
|
|
||||||
lua.set_app_data(internal_dom);
|
|
||||||
// Create all datatypes and singletons and export them
|
|
||||||
let exports = lua.create_table()?;
|
let exports = lua.create_table()?;
|
||||||
for (name, tab) in make_all_datatypes(lua)? {
|
for (name, tab) in make_all_datatypes(lua)? {
|
||||||
exports.set(name, tab)?;
|
exports.set(name, tab)?;
|
||||||
|
|
Loading…
Reference in a new issue