diff --git a/shell.nix b/shell.nix index cd4ddc5..35cf8b6 100644 --- a/shell.nix +++ b/shell.nix @@ -21,6 +21,7 @@ mkShell rec { udev # Audio? vulkan-loader # Rendering xorg.libX11 xorg.libXcursor xorg.libXi xorg.libXrandr # To use the x11 feature + tmux # Sharing environemnt between editor and terminal ]; LD_LIBRARY_PATH = lib.makeLibraryPath buildInputs; diff --git a/src/parser.rs b/src/parser.rs index dccf05d..b372196 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,252 +1,254 @@ +use nom::character::complete::hex_digit1; + 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, + #[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, + #[error("Failed to parse marker component")] + Marker, } // 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 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_bool(i: &str) -> IResult<&str, bool> { + alt((tag("true"), tag("false")))(i).map(|(s, val)| match val { + "true" => (s, true), + "false" => (s, false), + _ => panic!("Bools must be `true` or `false`!"), + }) } 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))) + 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))) + 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)) + 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) + 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((tag("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((tag("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((tag("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); + 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) - } + 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); - } + { + 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 + id: String, } /// /// pub(crate) fn parse_save_uuid(line: &str) -> Result { - let (remainder, _) = tag("uuid")(line)?; - let id = remainder.trim().into(); - Ok(EntityUuid { id }) + 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() }; + 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); + 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)?; + let (rem, (_, _, gltf_path, _, scene_name)) = + tuple((tag("model"), space1, parse_string, space1, parse_string))(line)?; - debug_assert!(rem == ""); + debug_assert!(rem == ""); - Ok((gltf_path.into(), scene_name.into())) + 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")); + 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); + assert_eq!(parsed, expected); } #[derive(Component, Debug, Default, PartialEq, Reflect, Clone)] #[reflect_value(Component, Default)] pub(crate) enum SaveCameraRenderTarget { - #[default] - Default, - Window(Uuid), + #[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) - } +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()); - } + { + 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)] @@ -258,58 +260,121 @@ pub(crate) struct SaveParent(String); /// parent some_other_file.entity /// ``` pub(crate) fn parse_save_parent(line: &str) -> Result { - todo!() + let (rem, (_, _, parent_file)) = tuple((tag("parent"), space1, parse_string))(line)?; + + debug_assert!(rem.is_empty()); + + Ok(SaveParent(parent_file.into())) } #[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); + 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!() + let (rem, (_, _, window_title)) = tuple((tag("window"), space1, parse_string))(line)?; + + let (rem, (_, _, _, visibility)) = tuple((space1, tag("visible"), space1, parse_bool))(rem)?; + + debug_assert!(rem.is_empty()); + + Ok(Window { + title: window_title.into(), + visible: visibility, + ..default() + }) } #[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); + 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 -/// +/// ```text +/// text "This is the text" color #abc123 size 12.34 +/// ``` pub(crate) fn parse_save_ui_text(line: &str) -> Result { - todo!() + let (rem, (_tag_text, _space1, text)) = tuple((tag("text"), space1, parse_string))(line)?; + + let (rem, (_space, _tag_color, _space1, _hash, hex_color)) = + tuple((space1, tag("color"), space1, char('#'), hex_digit1))(rem)?; + + let (rem, (_space, _tag_size, _space1, font_size)) = + tuple((space1, tag("size"), space1, float))(rem)?; + + debug_assert!(rem.is_empty()); + + let style = TextStyle { + color: Color::Srgba(Srgba::hex(hex_color).unwrap()), + font_size, + ..default() + }; + Ok(TextBundle { + text: Text::from_section(text, style), + ..default() + }) } #[test] fn test_save_ui_text() { - todo!() + let line = "text \"This is the text\" color #caffee size 14.6"; + let parsed = parse_save_ui_text(line).unwrap(); + let expected = Text::from_section( + "This is the text", + TextStyle { + color: Srgba::hex("#caffee").unwrap().into(), + font_size: 14.6, + ..default() + }, + ); + assert_eq!(parsed.text.sections[0].value, expected.sections[0].value); + assert_eq!( + parsed.text.sections[0].style.color, + expected.sections[0].style.color, + ); + assert_eq!( + parsed.text.sections[0].style.font_size, + expected.sections[0].style.font_size, + ); } /// /// 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!() - } +pub(crate) fn parse_save_tag( + t: &str, +) -> impl FnOnce(&str) -> Result + '_ { + move |line: &str| -> Result { Ok(tag(t)(line).map(|_| T::default())?) } } +#[derive(Component, Reflect, PartialEq, Debug, Default)] +#[reflect(Component)] +struct TestingTag; + #[test] fn test_save_tag() { - todo!() + let line = "testing"; + let parsed = parse_save_tag::("testing")(line).unwrap(); + let expected = TestingTag; + assert_eq!(parsed, expected); } #[derive(Component, Clone, Debug, Reflect, PartialEq)] @@ -317,11 +382,25 @@ fn test_save_tag() { 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!() +/// ```text +/// target-camera "./level-camera.entity" +/// ``` +pub(crate) fn parse_save_target_camera( + line: &str, +) -> Result { + Ok( + tuple((tag("target-camera"), space1, parse_string))(line).map( + |(_, (_, _, target_camera_entity_file))| { + SaveTargetCamera(target_camera_entity_file.into()) + }, + )?, + ) } #[test] fn test_target_camera() { - todo!() -} \ No newline at end of file + let line = "target-camera \"./level-camera.entity\""; + let parsed = parse_save_target_camera(line).unwrap(); + let expected = SaveTargetCamera("./level-camera.entity".into()); + assert_eq!(parsed, expected); +} diff --git a/src/prelude.rs b/src/prelude.rs index 86c693e..ab2f5c3 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,23 +1,24 @@ +pub(crate) use bevy::ecs::reflect::ReflectCommandExt; pub(crate) use bevy::{ - app::AppExit, - asset::{io::Reader, AssetLoader, LoadContext, AsyncReadExt}, - color::palettes::css::{GRAY, RED, DARK_GREEN, BLUE}, - gltf::Gltf, - input::common_conditions::input_just_pressed, - prelude::*, - render::camera::RenderTarget, - window::{WindowRef, WindowCloseRequested, PrimaryWindow}, + app::AppExit, + asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext}, + color::palettes::css::{BLUE, DARK_GREEN, GRAY, RED}, + gltf::Gltf, + input::common_conditions::input_just_pressed, + prelude::*, + render::camera::RenderTarget, + window::{PrimaryWindow, WindowCloseRequested, WindowRef}, }; -pub(crate) use bevy::ecs::reflect::ReflectCommandExt; pub(crate) use nom::bytes::complete::take; pub(crate) use nom::{ - IResult, - bytes::complete::{tag, take_until1}, - character::complete::space1, - number::complete::float, - sequence::tuple, + branch::{alt, permutation}, + bytes::complete::{tag, take_until1}, + character::complete::{char, hex_digit1, space1}, + number::complete::float, + sequence::tuple, + IResult, }; -pub(crate) use uuid::Uuid; pub(crate) use thiserror::Error; +pub(crate) use uuid::Uuid; pub(crate) use crate::{conditions::*, parser::*};