You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
477 lines
13 KiB
Rust
477 lines
13 KiB
Rust
use crate::prelude::*;
|
|
|
|
#[derive(Error, Debug)]
|
|
pub(crate) enum SaveEntityParseError {
|
|
#[error("Failed to parse `{0}`")]
|
|
Component(String),
|
|
#[error("Failed to parse Entity")]
|
|
Nom(nom::Err<nom::error::Error<Box<str>>>),
|
|
}
|
|
|
|
// Convert Nom error to parse error
|
|
// https://stackoverflow.com/a/77974858/3096574
|
|
impl From<nom::Err<nom::error::Error<&str>>> for SaveEntityParseError {
|
|
fn from(err: nom::Err<nom::error::Error<&str>>) -> Self {
|
|
SaveEntityParseError::Nom(err.map_input(|input| input.into()))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
pub(crate) enum Token {
|
|
Tag(String),
|
|
Str(String),
|
|
Num(f32),
|
|
Comment(String),
|
|
Etc,
|
|
}
|
|
|
|
///
|
|
/// Simple function to tokenize a string into an array of Token values
|
|
///
|
|
pub(crate) fn tokenize(line: &str) -> Vec<Token> {
|
|
let mut l = line;
|
|
let mut tokens = Vec::new();
|
|
|
|
// Check for comment
|
|
if let Ok((_, (_start, content))) = tuple((char::<&str, ()>('#'), not_line_ending))(line) {
|
|
tokens.push(Token::Comment(content.strip_prefix(" ").unwrap().into()));
|
|
} else {
|
|
// Check for all other token types in a loop
|
|
while l.len() > 0 {
|
|
if let Ok((rem, (_, s, _))) = tuple((
|
|
char::<&str, ()>('"'),
|
|
take_until("\""),
|
|
char::<&str, ()>('"'),
|
|
))(l)
|
|
{
|
|
debug!("Parsed string {:?}", s);
|
|
tokens.push(Token::Str(s.into()));
|
|
l = rem;
|
|
} else if let Ok((rem, num)) = float::<&str, ()>(l) {
|
|
debug!("Parsed float {:?}", num);
|
|
tokens.push(Token::Num(num.into()));
|
|
l = rem;
|
|
} else if let Ok((rem, (_, etc, _))) =
|
|
tuple((space0, tag::<&str, &str, ()>("..."), space0))(l)
|
|
{
|
|
debug!("Parsed etc. {:?}", etc);
|
|
tokens.push(Token::Etc);
|
|
l = rem;
|
|
} else if let Ok((rem, (_, tag, _))) =
|
|
tuple((space0, alphanumeric1::<&str, ()>, space0))(l)
|
|
{
|
|
debug!("Parsed tag {:?}", tag);
|
|
tokens.push(Token::Tag(tag.into()));
|
|
l = rem;
|
|
} else {
|
|
debug!("Breaking loop");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
debug!("Parsed tokens: {:?}", tokens);
|
|
|
|
tokens
|
|
}
|
|
|
|
#[test]
|
|
fn test_tokenize() {
|
|
let line = "foo \"bar\" 1.23 baz -3.45 \"asdf\" \"multi word string\" etc";
|
|
assert_eq!(
|
|
tokenize(line),
|
|
vec![
|
|
Token::Tag("foo".into()),
|
|
Token::Str("bar".into()),
|
|
Token::Num(1.23),
|
|
Token::Tag("baz".into()),
|
|
Token::Num(-3.45),
|
|
Token::Str("asdf".into()),
|
|
Token::Str("multi word string".into()),
|
|
Token::Tag("etc".into())
|
|
]
|
|
);
|
|
}
|
|
|
|
///
|
|
/// Returns reflected `Transform`
|
|
///
|
|
pub(crate) fn parse_save_transform(
|
|
tokens: &Vec<Token>,
|
|
) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
|
|
// Tag(Transform),
|
|
// Tag(Translation), Number, Number, Number
|
|
// Tag(Rotation), Number, Number, Number, Number
|
|
// Tag(Scale), Number, Number, Number
|
|
|
|
// return Err(SaveEntityParseError::Transform);
|
|
|
|
todo!("parse_save_transform");
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_transform() {
|
|
let line = "transform translation 1.0 2.0 3.0 rotation 0.1 0.2 0.3 1.0 scale 1.1 1.2 1.3";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_transform(&tokens).unwrap();
|
|
let expected = Transform {
|
|
translation: Vec3::new(1.0, 2.0, 3.0),
|
|
rotation: Quat::from_xyzw(0.1, 0.2, 0.3, 1.0),
|
|
scale: Vec3::new(1.1, 1.2, 1.3),
|
|
};
|
|
|
|
assert!(expected
|
|
.clone_value()
|
|
.reflect_partial_eq(parsed.as_reflect())
|
|
.unwrap());
|
|
}
|
|
|
|
///
|
|
/// Returns a reflected `Name`
|
|
///
|
|
pub(crate) fn parse_save_name(
|
|
tokens: &Vec<Token>,
|
|
) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
|
|
if let Some((Token::Tag(t), &[Token::Str(ref s)])) = tokens.split_first()
|
|
&& *t == String::from("name")
|
|
{
|
|
Ok(Name::new(s.clone()).clone_value())
|
|
} else {
|
|
Err(SaveEntityParseError::Component("Name".into()))
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_name() {
|
|
{
|
|
let line = "name Van";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_name(&tokens).unwrap();
|
|
let expected = Name::new("Van");
|
|
|
|
assert!(expected
|
|
.clone_value()
|
|
.reflect_partial_eq(parsed.as_reflect())
|
|
.unwrap());
|
|
}
|
|
{
|
|
let line = "name Big Mike";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_name(&tokens).unwrap();
|
|
let expected = Name::new("Big Mike");
|
|
|
|
assert!(expected
|
|
.clone_value()
|
|
.reflect_partial_eq(parsed.as_reflect())
|
|
.unwrap());
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, Reflect, PartialEq)]
|
|
#[reflect(Component, PartialEq)]
|
|
pub(crate) struct SaveModel {
|
|
gltf_file: PathBuf,
|
|
scene_name: String,
|
|
}
|
|
|
|
impl Component for SaveModel {
|
|
const STORAGE_TYPE: StorageType = StorageType::Table;
|
|
|
|
fn register_component_hooks(hooks: &mut ComponentHooks) {
|
|
todo!("Assign Scene Handle for SaveModel")
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Returns a reflected `SaveModel`
|
|
///
|
|
pub(crate) fn parse_save_model(
|
|
tokens: &Vec<Token>,
|
|
) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
|
|
todo!("parse_save_model");
|
|
|
|
/*
|
|
Ok(SaveModel {
|
|
gltf_file: gltf_path.into(),
|
|
scene_name: scene_name.into(),
|
|
}
|
|
.clone_value())
|
|
*/
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_model() {
|
|
let line = "model \"models/foo.glb\" \"My Scene\"";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_model(&tokens).unwrap();
|
|
let expected = SaveModel {
|
|
gltf_file: "models/foo.glb".into(),
|
|
scene_name: "My Scene".into(),
|
|
};
|
|
|
|
assert!(expected
|
|
.clone_value()
|
|
.reflect_partial_eq(parsed.as_reflect())
|
|
.unwrap());
|
|
}
|
|
|
|
#[derive(Debug, Default, PartialEq, Reflect, Clone)]
|
|
#[reflect_value(Component, Default, PartialEq)]
|
|
pub(crate) enum SaveCameraRenderTarget {
|
|
#[default]
|
|
Default,
|
|
Window(PathBuf),
|
|
}
|
|
|
|
impl Component for SaveCameraRenderTarget {
|
|
const STORAGE_TYPE: StorageType = StorageType::Table;
|
|
|
|
fn register_component_hooks(hooks: &mut ComponentHooks) {
|
|
todo!("Assign Render Target")
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Returns a reflected `SaveCameraRenderTarget
|
|
///
|
|
pub(crate) fn parse_save_camera(
|
|
tokens: &Vec<Token>,
|
|
) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
|
|
todo!("parse_save_camera");
|
|
|
|
// Nothing parsed well
|
|
// Err(SaveEntityParseError::Camera)
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_camera() {
|
|
{
|
|
let line = "camera";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_camera(&tokens).unwrap();
|
|
let expected = SaveCameraRenderTarget::Default;
|
|
assert!(expected
|
|
.clone_value()
|
|
.reflect_partial_eq(parsed.as_reflect())
|
|
.unwrap());
|
|
}
|
|
{
|
|
let line = "camera target window";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_camera(&tokens).unwrap();
|
|
let expected = SaveCameraRenderTarget::Default;
|
|
assert!(expected
|
|
.clone_value()
|
|
.reflect_partial_eq(parsed.as_reflect())
|
|
.unwrap());
|
|
}
|
|
{
|
|
let line = "camera target";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_camera(&tokens);
|
|
assert!(parsed.is_err());
|
|
}
|
|
{
|
|
let line = "camera target window \"some.entity\"";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_camera(&tokens).unwrap();
|
|
let expected = SaveCameraRenderTarget::Window("some.entity".into());
|
|
assert!(expected
|
|
.clone_value()
|
|
.reflect_partial_eq(parsed.as_reflect())
|
|
.unwrap());
|
|
}
|
|
{
|
|
let line = "notcamera";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_camera(&tokens);
|
|
assert!(parsed.is_err());
|
|
}
|
|
}
|
|
|
|
/// SaveParent entity which is a reference to which entity this is a child of
|
|
/// A run-time system converts this to a bevy Parent component
|
|
#[derive(PartialEq, Debug, Reflect, Clone)]
|
|
#[reflect_value(Component, PartialEq)]
|
|
pub(crate) struct SaveParent(String);
|
|
|
|
impl Component for SaveParent {
|
|
const STORAGE_TYPE: StorageType = StorageType::Table;
|
|
|
|
fn register_component_hooks(hooks: &mut ComponentHooks) {
|
|
todo!("Assign parent for entity")
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Parses a parent entity with this format:
|
|
/// ```text
|
|
/// parent some_other_file.entity
|
|
/// ```
|
|
///
|
|
/// Returns a reflected `SaveParent`
|
|
///
|
|
pub(crate) fn parse_save_parent(
|
|
tokens: &Vec<Token>,
|
|
) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
|
|
todo!("parse_save_parent");
|
|
|
|
// Ok(SaveParent(parent_file.into()).clone_value())
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_parent() {
|
|
let line = "parent \"some_other_file.entity\"";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_parent(&tokens).unwrap();
|
|
let expected = SaveParent("some_other_file.entity".into());
|
|
assert!(expected
|
|
.clone_value()
|
|
.reflect_partial_eq(parsed.as_reflect())
|
|
.unwrap());
|
|
}
|
|
|
|
///
|
|
/// Parse the Window component (which is very big!)
|
|
/// We only sparsely define the bits that we want to edit (for now)
|
|
///
|
|
/// Returns a reflected `Window`
|
|
///
|
|
pub(crate) fn parse_save_window(
|
|
tokens: &Vec<Token>,
|
|
) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
|
|
todo!("parse_save_window");
|
|
|
|
/*
|
|
Ok(Window {
|
|
title: window_title.into(),
|
|
visible: visibility,
|
|
..default()
|
|
}
|
|
.clone_value())
|
|
*/
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_window() {
|
|
let line = "window \"Editor\" visible false";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_window(&tokens).unwrap();
|
|
let expected = Window {
|
|
visible: false,
|
|
title: "Editor".into(),
|
|
..default()
|
|
};
|
|
assert!(expected
|
|
.clone_value()
|
|
.reflect_partial_eq(parsed.as_reflect())
|
|
.unwrap());
|
|
}
|
|
|
|
///
|
|
/// The UI Text bundle specified as a sparse subset of a bundle
|
|
/// ```text
|
|
/// uiText "This is the text" color #abc123 size 12.34
|
|
/// ```
|
|
///
|
|
/// Returns a reflected `Text`
|
|
///
|
|
pub(crate) fn parse_save_ui_text(
|
|
tokens: &Vec<Token>,
|
|
) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
|
|
todo!("parse_save_ui_text");
|
|
|
|
/*
|
|
let style = TextStyle {
|
|
color: Color::Srgba(Srgba::hex(hex_color).unwrap()),
|
|
font_size,
|
|
..default()
|
|
};
|
|
Ok(Text::from_section(text, style).clone_value())
|
|
*/
|
|
}
|
|
|
|
#[test]
|
|
fn test_save_ui_text() {
|
|
let line = "uiText \"This is the text\" color #caffee size 14.6";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_ui_text(&tokens).unwrap();
|
|
let expected = Text::from_section(
|
|
"This is the text",
|
|
TextStyle {
|
|
color: Srgba::hex("#caffee").unwrap().into(),
|
|
font_size: 14.6,
|
|
..default()
|
|
},
|
|
);
|
|
assert!(expected
|
|
.clone_value()
|
|
.reflect_partial_eq(parsed.as_reflect())
|
|
.unwrap());
|
|
}
|
|
|
|
///
|
|
/// Returns a parser function for a basic stand-alone tag
|
|
/// e.g., "editor_tag" or "ui_node"
|
|
///
|
|
pub(crate) fn parse_save_tag<T: Component + Reflect + Default>(
|
|
t: &str,
|
|
) -> impl FnOnce(&Vec<Token>) -> Result<Box<dyn Reflect>, SaveEntityParseError> + '_ {
|
|
move |tokens: &Vec<Token>| -> Result<Box<dyn Reflect>, SaveEntityParseError> {
|
|
todo!("parse_save_tag")
|
|
}
|
|
}
|
|
|
|
#[derive(Component, Reflect, PartialEq, Debug, Default)]
|
|
#[reflect(Component)]
|
|
struct TestingTag;
|
|
|
|
#[test]
|
|
fn test_save_tag() {
|
|
let line = "testing";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_tag::<TestingTag>("testing")(&tokens).unwrap();
|
|
let expected = TestingTag;
|
|
assert!(expected
|
|
.clone_value()
|
|
.reflect_partial_eq(parsed.as_reflect())
|
|
.unwrap());
|
|
}
|
|
|
|
#[derive(Clone, Debug, Reflect, PartialEq)]
|
|
#[reflect(Component)]
|
|
pub(crate) struct SaveTargetCamera(String);
|
|
|
|
impl Component for SaveTargetCamera {
|
|
const STORAGE_TYPE: StorageType = StorageType::Table;
|
|
|
|
fn register_component_hooks(hooks: &mut ComponentHooks) {
|
|
todo!("Assign target camera")
|
|
}
|
|
}
|
|
|
|
/// Parses the a SaveTargetCamera which at runtime is converted to a TargetCamera
|
|
/// ```text
|
|
/// targetCamera "./level-camera.entity"
|
|
/// ```
|
|
///
|
|
/// Returns reflected `SaveTargetCamera`
|
|
pub(crate) fn parse_save_target_camera(
|
|
tokens: &Vec<Token>,
|
|
) -> Result<Box<dyn Reflect>, SaveEntityParseError> {
|
|
todo!("parse_save_target_camera")
|
|
}
|
|
|
|
#[test]
|
|
fn test_target_camera() {
|
|
let line = "targetCamera \"./level-camera.entity\"";
|
|
let tokens = tokenize(line);
|
|
let parsed = parse_save_target_camera(&tokens).unwrap();
|
|
let expected = SaveTargetCamera("./level-camera.entity".into());
|
|
|
|
assert!(expected
|
|
.clone_value()
|
|
.reflect_partial_eq(parsed.as_reflect())
|
|
.unwrap());
|
|
}
|