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),
|
||||
#[error("Failed to convert into a DataModel - the given document is not a place")]
|
||||
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,
|
||||
#[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,
|
||||
#[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,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use rbx_dom_weak::WeakDom;
|
||||
use rbx_dom_weak::{InstanceBuilder as DomInstanceBuilder, WeakDom};
|
||||
use rbx_xml::{
|
||||
DecodeOptions as XmlDecodeOptions, DecodePropertyBehavior as XmlDecodePropertyBehavior,
|
||||
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
|
||||
reading and writing different kinds and formats of roblox files.
|
||||
|
||||
---
|
||||
|
||||
### Code Sample #1
|
||||
|
||||
```rust ignore
|
||||
// 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()?)?;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 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 {
|
||||
kind: DocumentKind,
|
||||
format: DocumentFormat,
|
||||
dom: Arc<RwLock<WeakDom>>,
|
||||
dom: WeakDom,
|
||||
}
|
||||
|
||||
impl Document {
|
||||
|
@ -96,11 +114,7 @@ impl Document {
|
|||
pub fn from_bytes_auto(bytes: impl AsRef<[u8]>) -> DocumentResult<Self> {
|
||||
let (format, dom) = Self::from_bytes_inner(bytes)?;
|
||||
let kind = DocumentKind::from_weak_dom(&dom).ok_or(DocumentError::UnknownKind)?;
|
||||
Ok(Self {
|
||||
kind,
|
||||
format,
|
||||
dom: Arc::new(RwLock::new(dom)),
|
||||
})
|
||||
Ok(Self { kind, format, dom })
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,17 +122,10 @@ impl Document {
|
|||
|
||||
This will automatically handle and detect if the document
|
||||
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> {
|
||||
let (format, dom) = Self::from_bytes_inner(bytes)?;
|
||||
Ok(Self {
|
||||
kind,
|
||||
format,
|
||||
dom: Arc::new(RwLock::new(dom)),
|
||||
})
|
||||
Ok(Self { kind, format, dom })
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,15 +145,16 @@ impl Document {
|
|||
be written to a file or sent over the network.
|
||||
*/
|
||||
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();
|
||||
match format {
|
||||
DocumentFormat::Binary => rbx_binary::to_writer(&mut bytes, &dom, &[dom.root_ref()])
|
||||
.map_err(|err| DocumentError::WriteError(err.to_string())),
|
||||
DocumentFormat::Binary => {
|
||||
rbx_binary::to_writer(&mut bytes, &self.dom, &[self.dom.root_ref()])
|
||||
.map_err(|err| DocumentError::WriteError(err.to_string()))
|
||||
}
|
||||
DocumentFormat::Xml => {
|
||||
let xml_options = XmlEncodeOptions::new()
|
||||
.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()))
|
||||
}
|
||||
}?;
|
||||
|
@ -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.
|
||||
*/
|
||||
pub fn into_data_model_instance(self) -> DocumentResult<Instance> {
|
||||
pub fn into_data_model_instance(mut self) -> DocumentResult<Instance> {
|
||||
if self.kind != DocumentKind::Place {
|
||||
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.
|
||||
*/
|
||||
pub fn into_instance_array(self) -> DocumentResult<Vec<Instance>> {
|
||||
pub fn into_instance_array(mut self) -> DocumentResult<Vec<Instance>> {
|
||||
if self.kind != DocumentKind::Model {
|
||||
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.
|
||||
*/
|
||||
|
@ -210,13 +238,23 @@ impl Document {
|
|||
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> {
|
||||
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 {
|
||||
/**
|
||||
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 {
|
||||
let reader = INTERNAL_DOM
|
||||
|
@ -50,9 +52,7 @@ impl Instance {
|
|||
/**
|
||||
Creates a new orphaned `Instance` with a given class name.
|
||||
|
||||
An orphaned instance does not belong to any particular document and
|
||||
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.
|
||||
An orphaned instance is an instance at the root of a weak dom.
|
||||
*/
|
||||
fn new_orphaned(class_name: impl AsRef<str>) -> Self {
|
||||
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.
|
||||
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use mlua::prelude::*;
|
||||
use rbx_dom_weak::{InstanceBuilder as DomInstanceBuilder, WeakDom};
|
||||
|
||||
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> {
|
||||
// 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()?;
|
||||
for (name, tab) in make_all_datatypes(lua)? {
|
||||
exports.set(name, tab)?;
|
||||
|
|
Loading…
Reference in a new issue