lune/packages/lib-roblox/src/document/kind.rs
2023-03-09 18:35:26 +01:00

209 lines
5.7 KiB
Rust

use std::path::Path;
use rbx_dom_weak::WeakDom;
use crate::instance::util::instance_is_a_service;
/**
A document kind specifier.
Valid variants are the following:
- `Model`
- `Place`
Other variants are only to be used for logic internal to this crate.
*/
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum DocumentKind {
InternalRoot,
Place,
Model,
}
impl DocumentKind {
/**
Try to convert a file extension into a valid document kind specifier.
Returns `None` if the file extension is not a canonical roblox file format extension.
*/
pub fn from_extension(extension: impl AsRef<str>) -> Option<Self> {
match extension.as_ref() {
"rbxl" | "rbxlx" => Some(Self::Place),
"rbxm" | "rbxmx" => Some(Self::Model),
_ => None,
}
}
/**
Try to convert a file path into a valid document kind specifier.
Returns `None` if the file extension of the path
is not a canonical roblox file format extension.
*/
pub fn from_path(path: impl AsRef<Path>) -> Option<Self> {
match path
.as_ref()
.extension()
.map(|ext| ext.to_string_lossy())
.as_deref()
{
Some("rbxl") | Some("rbxlx") => Some(Self::Place),
Some("rbxm") | Some("rbxmx") => Some(Self::Model),
_ => None,
}
}
/**
Try to detect a document kind specifier from a weak dom.
Returns `None` if the given dom is empty and as such can not have its kind inferred.
*/
pub fn from_weak_dom(dom: &WeakDom) -> Option<Self> {
let mut has_top_level_child = false;
let mut has_top_level_service = false;
for child_ref in dom.root().children() {
if let Some(child_inst) = dom.get_by_ref(*child_ref) {
has_top_level_child = true;
if instance_is_a_service(&child_inst.class).unwrap_or(false) {
has_top_level_service = true;
break;
}
}
}
if has_top_level_service {
Some(Self::Place)
} else if has_top_level_child {
Some(Self::Model)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use rbx_dom_weak::InstanceBuilder;
use super::*;
#[test]
fn from_extension_place() {
assert_eq!(
DocumentKind::from_extension("rbxl"),
Some(DocumentKind::Place)
);
assert_eq!(
DocumentKind::from_extension("rbxlx"),
Some(DocumentKind::Place)
);
}
#[test]
fn from_extension_model() {
assert_eq!(
DocumentKind::from_extension("rbxm"),
Some(DocumentKind::Model)
);
assert_eq!(
DocumentKind::from_extension("rbxmx"),
Some(DocumentKind::Model)
);
}
#[test]
fn from_extension_invalid() {
assert_eq!(DocumentKind::from_extension("csv"), None);
assert_eq!(DocumentKind::from_extension("json"), None);
assert_eq!(DocumentKind::from_extension("rbx"), None);
assert_eq!(DocumentKind::from_extension("rbxn"), None);
assert_eq!(DocumentKind::from_extension("xlx"), None);
assert_eq!(DocumentKind::from_extension("xmx"), None);
}
#[test]
fn from_path_place() {
assert_eq!(
DocumentKind::from_path(PathBuf::from("place.rbxl")),
Some(DocumentKind::Place)
);
assert_eq!(
DocumentKind::from_path(PathBuf::from("place.rbxlx")),
Some(DocumentKind::Place)
);
}
#[test]
fn from_path_model() {
assert_eq!(
DocumentKind::from_path(PathBuf::from("model.rbxm")),
Some(DocumentKind::Model)
);
assert_eq!(
DocumentKind::from_path(PathBuf::from("model.rbxmx")),
Some(DocumentKind::Model)
);
}
#[test]
fn from_path_invalid() {
assert_eq!(
DocumentKind::from_path(PathBuf::from("data-file.csv")),
None
);
assert_eq!(
DocumentKind::from_path(PathBuf::from("nested/path/file.json")),
None
);
assert_eq!(
DocumentKind::from_path(PathBuf::from(".no-name-strange-rbx")),
None
);
assert_eq!(
DocumentKind::from_path(PathBuf::from("file_without_extension")),
None
);
}
#[test]
fn from_weak_dom() {
let empty = WeakDom::new(InstanceBuilder::new("Instance"));
assert_eq!(DocumentKind::from_weak_dom(&empty), None);
let with_services = WeakDom::new(
InstanceBuilder::new("Instance")
.with_child(InstanceBuilder::new("Workspace"))
.with_child(InstanceBuilder::new("ReplicatedStorage")),
);
assert_eq!(
DocumentKind::from_weak_dom(&with_services),
Some(DocumentKind::Place)
);
let with_children = WeakDom::new(
InstanceBuilder::new("Instance")
.with_child(InstanceBuilder::new("Model"))
.with_child(InstanceBuilder::new("Part")),
);
assert_eq!(
DocumentKind::from_weak_dom(&with_children),
Some(DocumentKind::Model)
);
let with_mixed = WeakDom::new(
InstanceBuilder::new("Instance")
.with_child(InstanceBuilder::new("Workspace"))
.with_child(InstanceBuilder::new("Part")),
);
assert_eq!(
DocumentKind::from_weak_dom(&with_mixed),
Some(DocumentKind::Place)
);
}
}