diff --git a/Cargo.lock b/Cargo.lock index d04656b..3de2007 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,7 +81,6 @@ dependencies = [ "bevy", "bevy_mod_picking", "nom", - "parser", "thiserror", "uuid", "wee_alloc", @@ -313,6 +312,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e938630e9f472b1899c78ef84aa907081b23bad8333140e2295c620485b6ee7" dependencies = [ + "bevy_dylib", "bevy_internal", ] @@ -521,6 +521,15 @@ dependencies = [ "sysinfo", ] +[[package]] +name = "bevy_dylib" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8494bf550eb30f570da1563217bcea25530cf29b35d35887ca6c2d76a411d00" +dependencies = [ + "bevy_internal", +] + [[package]] name = "bevy_ecs" version = "0.14.0" @@ -3172,15 +3181,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "parser" -version = "0.1.0" -dependencies = [ - "nom", - "quote", - "syn 2.0.71", -] - [[package]] name = "paste" version = "1.0.15" diff --git a/Cargo.toml b/Cargo.toml index f3efa02..247a825 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,13 +3,8 @@ name = "acts-of-gods" version = "0.1.0" edition = "2021" -[workspace] -members = [ - "crates/*" -] - [dependencies] -bevy = { version = "0.14", features = ["file_watcher"] } # , "dynamic_linking"] } +bevy = { version = "0.14", features = ["file_watcher", "dynamic_linking"] } wee_alloc = "*" bevy_mod_picking = "0.20" thiserror = "1" diff --git a/src/main.rs b/src/main.rs index 1f5d308..9f0df45 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,9 @@ pub(crate) mod save; /// Window handling pub(crate) mod window; +/// Save file parsing +pub(crate) mod parser; + use crate::prelude::*; fn main() { diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..dccf05d --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,327 @@ +use crate::prelude::*; + +#[derive(Error, Debug)] +pub(crate) enum SaveEntityParseError { + #[error("Failed to parse Uuid: {0}")] + Uuid(#[from] uuid::Error), + #[error("Failed to parse name")] + Name, + #[error("Failed to parse Transform")] + Transform, + #[error("Failed to parse Entity")] + Nom(nom::Err>>), + #[error("Failed to parse camera")] + Camera, +} + +// Convert Nom error to parse error +// https://stackoverflow.com/a/77974858/3096574 +impl From>> for SaveEntityParseError { + fn from(err: nom::Err>) -> Self { + SaveEntityParseError::Nom(err.map_input(|input| input.into())) + } +} + +fn parse_word<'a>(w: &'a str) -> impl Fn(&'a str) -> IResult<&'a str, &'a str> { + move |i: &'a str| tag(w)(i) +} + +fn parse_xyz(i: &str) -> IResult<&str, (f32, f32, f32)> { + tuple((float, space1, float, space1, float))(i) + .map(|(s, (x, _, y, _, z))| (s, (x, y, z))) +} + +fn parse_wxyz(i: &str) -> IResult<&str, (f32, f32, f32, f32)> { + tuple(( + float, + space1, + float, + space1, + float, + space1, + float, + ))(i) + .map(|(s, (w, _, x, _, y, _, z))| (s, (w, x, y, z))) +} + +fn parse_string(i: &str) -> IResult<&str, &str> { + let (rem, (_, out, _)) = tuple((tag("\""), take_until1("\""), tag("\"")))(i)?; + Ok((rem, out)) +} + +/// +/// +pub(crate) fn parse_save_transform(line: &str) -> Result { + let (rem, _) = tag("transform")(line)?; + + let mut transform = Transform::default(); + + let mut curr = rem.trim_start(); + for _ in 0..3 { + if let Ok((rem, (_, _, (x, y, z)))) = tuple((parse_word("translation"), space1, parse_xyz))(curr.trim_start()) { + transform.translation = Vec3::new(x, y, z); + curr = rem.trim_start(); + } else if let Ok((rem, (_, _, (x, y, z, w)))) = tuple((parse_word("rotation"), space1, parse_wxyz))(curr.trim_start()) { + transform.rotation = Quat::from_xyzw(x, y, z, w); + curr = rem.trim_start(); + } else if let Ok((rem, (_, _, (x, y, z)))) = tuple((parse_word("scale"), space1, parse_xyz))(curr.trim_start()) { + transform.scale = Vec3::new(x, y, z); + curr = rem.trim_start(); + } else { + return Err(SaveEntityParseError::Transform); + } + } + + // Assert there are no trailing characters on the line + debug_assert_eq!(curr, ""); + + Ok(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 parsed = parse_save_transform(line).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_eq!(parsed, expected); +} + +/// +/// +pub(crate) fn parse_save_name(line: &str) -> Result { + let (remainder, _) = tag("name")(line)?; + let n = remainder.trim().to_string(); + if n.is_empty() { + Err(SaveEntityParseError::Name) + } else { + let name = Name::new(n); + Ok(name) + } +} + +#[test] +fn test_parse_name() { + { + let line = "name Van"; + let parsed = parse_save_name(line).unwrap(); + let expected = Name::new("Van"); + + assert_eq!(parsed, expected); + } + { + let line = "name Big Mike"; + let parsed = parse_save_name(line).unwrap(); + let expected = Name::new("Big Mike"); + + assert_eq!(parsed, expected); + } +} + + +#[derive(Component, Clone, Debug, Reflect, PartialEq)] +#[reflect(Component)] +pub(crate) struct EntityUuid { + id: String +} + +/// +/// +pub(crate) fn parse_save_uuid(line: &str) -> Result { + let (remainder, _) = tag("uuid")(line)?; + let id = remainder.trim().into(); + Ok(EntityUuid { id }) +} + +#[test] +fn test_parse_uuid() { + let line = "uuid 1c16ab9a-5f79-4340-8469-4086f69c64f2"; + let parsed = parse_save_uuid(line).unwrap(); + let expected = EntityUuid { id: "1c16ab9a-5f79-4340-8469-4086f69c64f2".into() }; + + assert_eq!(parsed, expected); +} + +/// +/// +pub(crate) fn parse_save_model(line: &str) -> Result<(String, String), SaveEntityParseError> { + let (rem, (_, _, gltf_path, _, scene_name)) = tuple(( + tag("model"), + space1, + parse_string, + space1, + parse_string, + ))(line)?; + + debug_assert!(rem == ""); + + Ok((gltf_path.into(), scene_name.into())) +} + +#[test] +fn test_parse_model() { + let line = "model \"models/foo.glb\" \"My Scene\""; + let parsed = parse_save_model(line).unwrap(); + let expected = (String::from("models/foo.glb"), String::from("My Scene")); + + assert_eq!(parsed, expected); +} + +#[derive(Component, Debug, Default, PartialEq, Reflect, Clone)] +#[reflect_value(Component, Default)] +pub(crate) enum SaveCameraRenderTarget { + #[default] + Default, + Window(Uuid), +} + +pub(crate) fn parse_save_camera(line: &str) -> Result { + if let Ok((rem, _camera)) = tag::<&str, &str, ()>("camera")(line) { + if let Ok((rem, (_, _target))) = tuple((space1::<&str, ()>, tag("target")))(rem) { + if let Ok((rem, (_, _window))) = tuple((space1::<&str, ()>, tag("window")))(rem) { + // Camera + target + window + UUID + if let Ok((rem, (_, uuid))) = tuple((space1::<&str, ()>, take(36usize)))(rem) { + debug_assert!(rem == ""); + + let parsed_uuid = Uuid::parse_str(uuid)?; + Ok(SaveCameraRenderTarget::Window(parsed_uuid)) + // Camera + target + widow + } else { + debug_assert!(rem == ""); + + Ok(SaveCameraRenderTarget::Default) + } + // camera + target + } else { + debug_assert!(rem == ""); + + Err(SaveEntityParseError::Camera) + } + // Just camera + } else { + debug_assert!(rem == ""); + + Ok(SaveCameraRenderTarget::Default) + } + // Nothing parsed well + } else { + Err(SaveEntityParseError::Camera) + } +} + +#[test] +fn test_parse_camera() { + { + let line = "camera"; + let parsed = parse_save_camera(line).unwrap(); + let expected = SaveCameraRenderTarget::Default; + assert_eq!(parsed, expected); + } + { + let line = "camera target window"; + let parsed = parse_save_camera(line).unwrap(); + let expected = SaveCameraRenderTarget::Default; + assert_eq!(parsed, expected); + } + { + let line = "camera target"; + let parsed = parse_save_camera(line); + assert!(parsed.is_err()); + } + { + let line = "camera target window 9a1367e0-71e5-4c79-b4ad-02aa44b68af0"; + let target_uuid = Uuid::parse_str("9a1367e0-71e5-4c79-b4ad-02aa44b68af0").unwrap(); + let parsed = parse_save_camera(line).unwrap(); + let expected = SaveCameraRenderTarget::Window(target_uuid); + assert_eq!(parsed, expected); + } + { + let line = "notcamera"; + let parsed = parse_save_camera(line); + 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(Component, PartialEq, Debug, Reflect, Clone)] +#[reflect_value(Component)] +pub(crate) struct SaveParent(String); + +/// Parses a parent entity with this format: +/// ```text +/// parent some_other_file.entity +/// ``` +pub(crate) fn parse_save_parent(line: &str) -> Result { + todo!() +} + +#[test] +fn test_parse_parent() { + let line = "parent some_other_file.entity"; + let parsed = parse_save_parent(line).unwrap(); + let expected = SaveParent("some_other_file.entity".into()); + assert_eq!(parsed, expected); +} + +/// +/// Parse the Window component (which is very big!) +/// We only sparsely define the bits that we want to edit (for now) +/// +pub(crate) fn parse_save_window(line: &str) -> Result { + todo!() +} + +#[test] +fn test_parse_window() { + let line = "window \"Editor\" visible false"; + let parsed = parse_save_window(line).unwrap(); + let expected = Window { visible: false, title: "Editor".into(), ..default() }; + assert_eq!(parsed.visible, expected.visible); + assert_eq!(parsed.title, expected.title); +} + +/// +/// The UI Text bundle specified as a sparse subset of a bundle +/// +pub(crate) fn parse_save_ui_text(line: &str) -> Result { + todo!() +} + +#[test] +fn test_save_ui_text() { + todo!() +} + +/// +/// Returns a parser function for a basic stand-alone tag +/// e.g., "editor_tag" or "ui_node" +pub(crate) fn parse_save_tag(tag: &str) -> impl FnOnce(&str) -> Result { + move |line: &str| -> Result { + todo!() + } +} + +#[test] +fn test_save_tag() { + todo!() +} + +#[derive(Component, Clone, Debug, Reflect, PartialEq)] +#[reflect(Component)] +pub(crate) struct SaveTargetCamera(String); + +/// Parses the a SaveTargetCamera which at runtime is converted to a TargetCamera +pub(crate) fn parse_save_target_camera(line: &str) -> Result { + todo!() +} + +#[test] +fn test_target_camera() { + todo!() +} \ No newline at end of file diff --git a/src/prelude.rs b/src/prelude.rs index 74cc729..86c693e 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -20,4 +20,4 @@ pub(crate) use nom::{ pub(crate) use uuid::Uuid; pub(crate) use thiserror::Error; -pub(crate) use crate::conditions::*; +pub(crate) use crate::{conditions::*, parser::*}; diff --git a/src/save.rs b/src/save.rs index d59816d..b765af5 100644 --- a/src/save.rs +++ b/src/save.rs @@ -6,8 +6,8 @@ pub(crate) struct SavePlugin; impl Plugin for SavePlugin { fn build(&self, app: &mut App) { app - .register_type::() - .register_type::() + .register_type::() + .register_type::() .register_type::() .init_asset::() .init_asset_loader::() @@ -34,22 +34,30 @@ impl SaveEntity { fn parse( text: &str, load_context: &mut LoadContext, - ) -> Result { + ) -> Result { let lines = text.split('\n'); let mut entity = SaveEntity { ..default() }; lines.into_iter().for_each(|line| { - if let Ok(name) = parse::parse_save_name(line) { + if let Ok(name) = parse_save_name(line) { entity.components.push(name.clone_value()); - } else if let Ok(transform) = parse::parse_save_transform(line) { + } else if let Ok(transform) = parse_save_transform(line) { entity.components.push(transform.clone_value()); - } else if let Ok(uuid) = parse::parse_save_uuid(line) { + } else if let Ok(uuid) = parse_save_uuid(line) { entity.components.push(uuid.clone_value()); - } else if let Ok((gltf_file, scene_name)) = parse::parse_save_model(line) { + } else if let Ok((gltf_file, scene_name)) = parse_save_model(line) { let gltf_scene = GltfScene { gltf: load_context.load(gltf_file), name: scene_name }; entity.components.push(gltf_scene.clone_value()); - } else if let Ok(target) = parse::parse_save_camera(line) { + } else if let Ok(target) = parse_save_camera(line) { entity.components.push(target.clone_value()); + } else if let Ok(parent) = parse_save_parent(line) { + entity.components.push(parent.clone_value()); + } else if let Ok(window) = parse_save_window(line) { + entity.components.push(window.clone_value()); + // } else if let Ok(ui_text_bundle) = parse_save_ui_text(line) { + // entity.components.push(ui_text_bundle.clone_value()); + } else if let Ok(target_camera) = parse_save_target_camera(line) { + entity.components.push(target_camera.clone_value()); } else { error!("Failed to parse line component `{:?}`", line); } @@ -65,259 +73,6 @@ struct GltfScene { name: String, } - -mod parse { - use super::*; - - #[derive(Error, Debug)] - pub(crate) enum SaveEntityParseError { - #[error("Failed to parse Uuid: {0}")] - Uuid(#[from] uuid::Error), - #[error("Failed to parse name")] - Name, - #[error("Failed to parse Transform")] - Transform, - #[error("Failed to parse Entity")] - Nom(nom::Err>>), - #[error("Failed to parse camera")] - Camera, - } - - // Convert Nom error to parse error - // https://stackoverflow.com/a/77974858/3096574 - impl From>> for SaveEntityParseError { - fn from(err: nom::Err>) -> Self { - SaveEntityParseError::Nom(err.map_input(|input| input.into())) - } - } - - fn parse_word<'a>(w: &'a str) -> impl Fn(&'a str) -> IResult<&'a str, &'a str> { - move |i: &'a str| { - tag(w)(i) - } - } - - fn parse_xyz(i: &str) -> IResult<&str, (f32, f32, f32)> { - tuple((float, space1, float, space1, float))(i) - .map(|(s, (x, _, y, _, z))| (s, (x, y, z))) - } - - fn parse_wxyz(i: &str) -> IResult<&str, (f32, f32, f32, f32)> { - tuple(( - float, - space1, - float, - space1, - float, - space1, - float, - ))(i) - .map(|(s, (w, _, x, _, y, _, z))| (s, (w, x, y, z))) - } - - fn parse_string(i: &str) -> IResult<&str, &str> { - let (rem, (_, out, _)) = tuple((tag("\""), take_until1("\""), tag("\"")))(i)?; - Ok((rem, out)) - } - - /// - /// - pub(crate) fn parse_save_transform(line: &str) -> Result { - let (rem, _) = tag("transform")(line)?; - - let mut transform = Transform::default(); - - let mut curr = rem.trim_start(); - for _ in 0..3 { - if let Ok((rem, (_, _, (x, y, z)))) = tuple((parse_word("translation"), space1, parse_xyz))(curr.trim_start()) { - transform.translation = Vec3::new(x, y, z); - curr = rem.trim_start(); - } else if let Ok((rem, (_, _, (x, y, z, w)))) = tuple((parse_word("rotation"), space1, parse_wxyz))(curr.trim_start()) { - transform.rotation = Quat::from_xyzw(x, y, z, w); - curr = rem.trim_start(); - } else if let Ok((rem, (_, _, (x, y, z)))) = tuple((parse_word("scale"), space1, parse_xyz))(curr.trim_start()) { - transform.scale = Vec3::new(x, y, z); - curr = rem.trim_start(); - } else { - return Err(SaveEntityParseError::Transform); - } - } - - // Assert there are no trailing characters on the line - debug_assert_eq!(curr, ""); - - Ok(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 parsed = parse_save_transform(line).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_eq!(parsed, expected); - } - - /// - /// - pub(crate) fn parse_save_name(line: &str) -> Result { - let (remainder, _) = tag("name")(line)?; - let n = remainder.trim().to_string(); - if n.is_empty() { - Err(SaveEntityParseError::Name) - } else { - let name = Name::new(n); - Ok(name) - } - } - - #[test] - fn test_parse_name() { - { - let line = "name Van"; - let parsed = parse_save_name(line).unwrap(); - let expected = Name::new("Van"); - - assert_eq!(parsed, expected); - } - { - let line = "name Big Mike"; - let parsed = parse_save_name(line).unwrap(); - let expected = Name::new("Big Mike"); - - assert_eq!(parsed, expected); - } - } - - - #[derive(Component, Clone, Debug, Reflect, PartialEq)] - #[reflect(Component)] - pub(crate) struct EntityUuid { - id: String - } - - /// - /// - pub(crate) fn parse_save_uuid(line: &str) -> Result { - let (remainder, _) = tag("uuid")(line)?; - let id = remainder.trim().into(); - Ok(EntityUuid { id }) - } - - #[test] - fn test_parse_uuid() { - let line = "uuid 1c16ab9a-5f79-4340-8469-4086f69c64f2"; - let parsed = parse_save_uuid(line).unwrap(); - let expected = EntityUuid { id: "1c16ab9a-5f79-4340-8469-4086f69c64f2".into() }; - - assert_eq!(parsed, expected); - } - - /// - /// - pub(crate) fn parse_save_model(line: &str) -> Result<(String, String), SaveEntityParseError> { - let (rem, (_, _, gltf_path, _, scene_name)) = tuple(( - tag("model"), - space1, - parse_string, - space1, - parse_string, - ))(line)?; - - debug_assert!(rem == ""); - - Ok((gltf_path.into(), scene_name.into())) - } - - #[test] - fn test_parse_model() { - let line = "model \"models/foo.glb\" \"My Scene\""; - let parsed = parse_save_model(line).unwrap(); - let expected = (String::from("models/foo.glb"), String::from("My Scene")); - - assert_eq!(parsed, expected); - } - - #[derive(Component, Debug, Default, PartialEq, Reflect, Clone)] - #[reflect_value(Component, Default)] - pub(crate) enum SaveCameraRenderTarget { - #[default] - Default, - Window(Uuid), - } - - pub(crate) fn parse_save_camera(line: &str) -> Result { - if let Ok((rem, _camera)) = tag::<&str, &str, ()>("camera")(line) { - if let Ok((rem, (_, _target))) = tuple((space1::<&str, ()>, tag("target")))(rem) { - if let Ok((rem, (_, _window))) = tuple((space1::<&str, ()>, tag("window")))(rem) { - // Camera + target + window + UUID - if let Ok((rem, (_, uuid))) = tuple((space1::<&str, ()>, take(36usize)))(rem) { - debug_assert!(rem == ""); - - let parsed_uuid = Uuid::parse_str(uuid)?; - Ok(SaveCameraRenderTarget::Window(parsed_uuid)) - // Camera + target + widow - } else { - debug_assert!(rem == ""); - - Ok(SaveCameraRenderTarget::Default) - } - // camera + target - } else { - debug_assert!(rem == ""); - - Err(SaveEntityParseError::Camera) - } - // Just camera - } else { - debug_assert!(rem == ""); - - Ok(SaveCameraRenderTarget::Default) - } - // Nothing parsed well - } else { - Err(SaveEntityParseError::Camera) - } - } - - #[test] - fn test_parse_camera() { - { - let line = "camera"; - let parsed = parse_save_camera(line).unwrap(); - let expected = SaveCameraRenderTarget::Default; - assert_eq!(parsed, expected); - } - { - let line = "camera target window"; - let parsed = parse_save_camera(line).unwrap(); - let expected = SaveCameraRenderTarget::Default; - assert_eq!(parsed, expected); - } - { - let line = "camera target"; - let parsed = parse_save_camera(line); - assert!(parsed.is_err()); - } - { - let line = "camera target window 9a1367e0-71e5-4c79-b4ad-02aa44b68af0"; - let target_uuid = Uuid::parse_str("9a1367e0-71e5-4c79-b4ad-02aa44b68af0").unwrap(); - let parsed = parse_save_camera(line).unwrap(); - let expected = SaveCameraRenderTarget::Window(target_uuid); - assert_eq!(parsed, expected); - } - { - let line = "notcamera"; - let parsed = parse_save_camera(line); - assert!(parsed.is_err()); - } - } -} - #[derive(Default)] struct SaveEntityLoader; @@ -326,7 +81,7 @@ enum SaveEntityLoaderError { #[error("Could not load asset: {0}")] Io(#[from] std::io::Error), #[error("Could not parse entity: {0}")] - Parse(#[from] parse::SaveEntityParseError), + Parse(#[from] SaveEntityParseError), } impl AssetLoader for SaveEntityLoader {