we got tests passing!

Adding components to save files is a pain, but hopefully a one-time cost.
attempt/001
Elijah Voigt 1 year ago
parent e8b15c80c1
commit 7947be5cfb

@ -21,6 +21,7 @@ mkShell rec {
udev # Audio? udev # Audio?
vulkan-loader # Rendering vulkan-loader # Rendering
xorg.libX11 xorg.libXcursor xorg.libXi xorg.libXrandr # To use the x11 feature 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; LD_LIBRARY_PATH = lib.makeLibraryPath buildInputs;

@ -1,252 +1,254 @@
use nom::character::complete::hex_digit1;
use crate::prelude::*; use crate::prelude::*;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub(crate) enum SaveEntityParseError { pub(crate) enum SaveEntityParseError {
#[error("Failed to parse Uuid: {0}")] #[error("Failed to parse Uuid: {0}")]
Uuid(#[from] uuid::Error), Uuid(#[from] uuid::Error),
#[error("Failed to parse name")] #[error("Failed to parse name")]
Name, Name,
#[error("Failed to parse Transform")] #[error("Failed to parse Transform")]
Transform, Transform,
#[error("Failed to parse Entity")] #[error("Failed to parse Entity")]
Nom(nom::Err<nom::error::Error<Box<str>>>), Nom(nom::Err<nom::error::Error<Box<str>>>),
#[error("Failed to parse camera")] #[error("Failed to parse camera")]
Camera, Camera,
#[error("Failed to parse marker component")]
Marker,
} }
// Convert Nom error to parse error // Convert Nom error to parse error
// https://stackoverflow.com/a/77974858/3096574 // https://stackoverflow.com/a/77974858/3096574
impl From<nom::Err<nom::error::Error<&str>>> for SaveEntityParseError { impl From<nom::Err<nom::error::Error<&str>>> for SaveEntityParseError {
fn from(err: nom::Err<nom::error::Error<&str>>) -> Self { fn from(err: nom::Err<nom::error::Error<&str>>) -> Self {
SaveEntityParseError::Nom(err.map_input(|input| input.into())) SaveEntityParseError::Nom(err.map_input(|input| input.into()))
} }
} }
fn parse_word<'a>(w: &'a str) -> impl Fn(&'a str) -> IResult<&'a str, &'a str> { fn parse_bool(i: &str) -> IResult<&str, bool> {
move |i: &'a str| tag(w)(i) 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)> { fn parse_xyz(i: &str) -> IResult<&str, (f32, f32, f32)> {
tuple((float, space1, float, space1, float))(i) tuple((float, space1, float, space1, float))(i).map(|(s, (x, _, y, _, z))| (s, (x, y, z)))
.map(|(s, (x, _, y, _, z))| (s, (x, y, z)))
} }
fn parse_wxyz(i: &str) -> IResult<&str, (f32, f32, f32, f32)> { fn parse_wxyz(i: &str) -> IResult<&str, (f32, f32, f32, f32)> {
tuple(( tuple((float, space1, float, space1, float, space1, float))(i)
float, .map(|(s, (w, _, x, _, y, _, z))| (s, (w, x, y, z)))
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> { fn parse_string(i: &str) -> IResult<&str, &str> {
let (rem, (_, out, _)) = tuple((tag("\""), take_until1("\""), tag("\"")))(i)?; let (rem, (_, out, _)) = tuple((tag("\""), take_until1("\""), tag("\"")))(i)?;
Ok((rem, out)) Ok((rem, out))
} }
/// ///
/// ///
pub(crate) fn parse_save_transform(line: &str) -> Result<Transform, SaveEntityParseError> { pub(crate) fn parse_save_transform(line: &str) -> Result<Transform, SaveEntityParseError> {
let (rem, _) = tag("transform")(line)?; let (rem, _) = tag("transform")(line)?;
let mut transform = Transform::default(); let mut transform = Transform::default();
let mut curr = rem.trim_start(); let mut curr = rem.trim_start();
for _ in 0..3 { for _ in 0..3 {
if let Ok((rem, (_, _, (x, y, z)))) = tuple((parse_word("translation"), space1, parse_xyz))(curr.trim_start()) { if let Ok((rem, (_, _, (x, y, z)))) =
transform.translation = Vec3::new(x, y, z); tuple((tag("translation"), space1, parse_xyz))(curr.trim_start())
curr = rem.trim_start(); {
} else if let Ok((rem, (_, _, (x, y, z, w)))) = tuple((parse_word("rotation"), space1, parse_wxyz))(curr.trim_start()) { transform.translation = Vec3::new(x, y, z);
transform.rotation = Quat::from_xyzw(x, y, z, w); curr = rem.trim_start();
curr = rem.trim_start(); } else if let Ok((rem, (_, _, (x, y, z, w)))) =
} else if let Ok((rem, (_, _, (x, y, z)))) = tuple((parse_word("scale"), space1, parse_xyz))(curr.trim_start()) { tuple((tag("rotation"), space1, parse_wxyz))(curr.trim_start())
transform.scale = Vec3::new(x, y, z); {
curr = rem.trim_start(); transform.rotation = Quat::from_xyzw(x, y, z, w);
} else { curr = rem.trim_start();
return Err(SaveEntityParseError::Transform); } else if let Ok((rem, (_, _, (x, y, z)))) =
} tuple((tag("scale"), space1, parse_xyz))(curr.trim_start())
} {
transform.scale = Vec3::new(x, y, z);
// Assert there are no trailing characters on the line curr = rem.trim_start();
debug_assert_eq!(curr, ""); } else {
return Err(SaveEntityParseError::Transform);
Ok(transform) }
}
// Assert there are no trailing characters on the line
debug_assert_eq!(curr, "");
Ok(transform)
} }
#[test] #[test]
fn test_parse_transform() { 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 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 parsed = parse_save_transform(line).unwrap();
let expected = Transform { let expected = Transform {
translation: Vec3::new(1.0, 2.0, 3.0), translation: Vec3::new(1.0, 2.0, 3.0),
rotation: Quat::from_xyzw(0.1, 0.2, 0.3, 1.0), rotation: Quat::from_xyzw(0.1, 0.2, 0.3, 1.0),
scale: Vec3::new(1.1, 1.2, 1.3), scale: Vec3::new(1.1, 1.2, 1.3),
}; };
assert_eq!(parsed, expected); assert_eq!(parsed, expected);
} }
/// ///
/// ///
pub(crate) fn parse_save_name(line: &str) -> Result<Name, SaveEntityParseError> { pub(crate) fn parse_save_name(line: &str) -> Result<Name, SaveEntityParseError> {
let (remainder, _) = tag("name")(line)?; let (remainder, _) = tag("name")(line)?;
let n = remainder.trim().to_string(); let n = remainder.trim().to_string();
if n.is_empty() { if n.is_empty() {
Err(SaveEntityParseError::Name) Err(SaveEntityParseError::Name)
} else { } else {
let name = Name::new(n); let name = Name::new(n);
Ok(name) Ok(name)
} }
} }
#[test] #[test]
fn test_parse_name() { fn test_parse_name() {
{ {
let line = "name Van"; let line = "name Van";
let parsed = parse_save_name(line).unwrap(); let parsed = parse_save_name(line).unwrap();
let expected = Name::new("Van"); let expected = Name::new("Van");
assert_eq!(parsed, expected); assert_eq!(parsed, expected);
} }
{ {
let line = "name Big Mike"; let line = "name Big Mike";
let parsed = parse_save_name(line).unwrap(); let parsed = parse_save_name(line).unwrap();
let expected = Name::new("Big Mike"); let expected = Name::new("Big Mike");
assert_eq!(parsed, expected); assert_eq!(parsed, expected);
} }
} }
#[derive(Component, Clone, Debug, Reflect, PartialEq)] #[derive(Component, Clone, Debug, Reflect, PartialEq)]
#[reflect(Component)] #[reflect(Component)]
pub(crate) struct EntityUuid { pub(crate) struct EntityUuid {
id: String id: String,
} }
/// ///
/// ///
pub(crate) fn parse_save_uuid(line: &str) -> Result<EntityUuid, SaveEntityParseError> { pub(crate) fn parse_save_uuid(line: &str) -> Result<EntityUuid, SaveEntityParseError> {
let (remainder, _) = tag("uuid")(line)?; let (remainder, _) = tag("uuid")(line)?;
let id = remainder.trim().into(); let id = remainder.trim().into();
Ok(EntityUuid { id }) Ok(EntityUuid { id })
} }
#[test] #[test]
fn test_parse_uuid() { fn test_parse_uuid() {
let line = "uuid 1c16ab9a-5f79-4340-8469-4086f69c64f2"; let line = "uuid 1c16ab9a-5f79-4340-8469-4086f69c64f2";
let parsed = parse_save_uuid(line).unwrap(); let parsed = parse_save_uuid(line).unwrap();
let expected = EntityUuid { id: "1c16ab9a-5f79-4340-8469-4086f69c64f2".into() }; 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> { pub(crate) fn parse_save_model(line: &str) -> Result<(String, String), SaveEntityParseError> {
let (rem, (_, _, gltf_path, _, scene_name)) = tuple(( let (rem, (_, _, gltf_path, _, scene_name)) =
tag("model"), tuple((tag("model"), space1, parse_string, space1, parse_string))(line)?;
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] #[test]
fn test_parse_model() { fn test_parse_model() {
let line = "model \"models/foo.glb\" \"My Scene\""; let line = "model \"models/foo.glb\" \"My Scene\"";
let parsed = parse_save_model(line).unwrap(); let parsed = parse_save_model(line).unwrap();
let expected = (String::from("models/foo.glb"), String::from("My Scene")); 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)] #[derive(Component, Debug, Default, PartialEq, Reflect, Clone)]
#[reflect_value(Component, Default)] #[reflect_value(Component, Default)]
pub(crate) enum SaveCameraRenderTarget { pub(crate) enum SaveCameraRenderTarget {
#[default] #[default]
Default, Default,
Window(Uuid), Window(Uuid),
} }
pub(crate) fn parse_save_camera(line: &str) -> Result<SaveCameraRenderTarget, SaveEntityParseError> { pub(crate) fn parse_save_camera(
if let Ok((rem, _camera)) = tag::<&str, &str, ()>("camera")(line) { line: &str,
if let Ok((rem, (_, _target))) = tuple((space1::<&str, ()>, tag("target")))(rem) { ) -> Result<SaveCameraRenderTarget, SaveEntityParseError> {
if let Ok((rem, (_, _window))) = tuple((space1::<&str, ()>, tag("window")))(rem) { if let Ok((rem, _camera)) = tag::<&str, &str, ()>("camera")(line) {
// Camera + target + window + UUID if let Ok((rem, (_, _target))) = tuple((space1::<&str, ()>, tag("target")))(rem) {
if let Ok((rem, (_, uuid))) = tuple((space1::<&str, ()>, take(36usize)))(rem) { if let Ok((rem, (_, _window))) = tuple((space1::<&str, ()>, tag("window")))(rem) {
debug_assert!(rem == ""); // Camera + target + window + UUID
if let Ok((rem, (_, uuid))) = tuple((space1::<&str, ()>, take(36usize)))(rem) {
let parsed_uuid = Uuid::parse_str(uuid)?; debug_assert!(rem == "");
Ok(SaveCameraRenderTarget::Window(parsed_uuid))
// Camera + target + widow let parsed_uuid = Uuid::parse_str(uuid)?;
} else { Ok(SaveCameraRenderTarget::Window(parsed_uuid))
debug_assert!(rem == ""); // Camera + target + widow
} else {
Ok(SaveCameraRenderTarget::Default) debug_assert!(rem == "");
}
// camera + target Ok(SaveCameraRenderTarget::Default)
} else { }
debug_assert!(rem == ""); // camera + target
} else {
Err(SaveEntityParseError::Camera) debug_assert!(rem == "");
}
// Just camera Err(SaveEntityParseError::Camera)
} else { }
debug_assert!(rem == ""); // Just camera
} else {
Ok(SaveCameraRenderTarget::Default) debug_assert!(rem == "");
}
// Nothing parsed well Ok(SaveCameraRenderTarget::Default)
} else { }
Err(SaveEntityParseError::Camera) // Nothing parsed well
} } else {
Err(SaveEntityParseError::Camera)
}
} }
#[test] #[test]
fn test_parse_camera() { fn test_parse_camera() {
{ {
let line = "camera"; let line = "camera";
let parsed = parse_save_camera(line).unwrap(); let parsed = parse_save_camera(line).unwrap();
let expected = SaveCameraRenderTarget::Default; let expected = SaveCameraRenderTarget::Default;
assert_eq!(parsed, expected); assert_eq!(parsed, expected);
} }
{ {
let line = "camera target window"; let line = "camera target window";
let parsed = parse_save_camera(line).unwrap(); let parsed = parse_save_camera(line).unwrap();
let expected = SaveCameraRenderTarget::Default; let expected = SaveCameraRenderTarget::Default;
assert_eq!(parsed, expected); assert_eq!(parsed, expected);
} }
{ {
let line = "camera target"; let line = "camera target";
let parsed = parse_save_camera(line); let parsed = parse_save_camera(line);
assert!(parsed.is_err()); assert!(parsed.is_err());
} }
{ {
let line = "camera target window 9a1367e0-71e5-4c79-b4ad-02aa44b68af0"; let line = "camera target window 9a1367e0-71e5-4c79-b4ad-02aa44b68af0";
let target_uuid = Uuid::parse_str("9a1367e0-71e5-4c79-b4ad-02aa44b68af0").unwrap(); let target_uuid = Uuid::parse_str("9a1367e0-71e5-4c79-b4ad-02aa44b68af0").unwrap();
let parsed = parse_save_camera(line).unwrap(); let parsed = parse_save_camera(line).unwrap();
let expected = SaveCameraRenderTarget::Window(target_uuid); let expected = SaveCameraRenderTarget::Window(target_uuid);
assert_eq!(parsed, expected); assert_eq!(parsed, expected);
} }
{ {
let line = "notcamera"; let line = "notcamera";
let parsed = parse_save_camera(line); let parsed = parse_save_camera(line);
assert!(parsed.is_err()); assert!(parsed.is_err());
} }
} }
/// SaveParent entity which is a reference to which entity this is a child of /// 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 /// A run-time system converts this to a bevy Parent component
#[derive(Component, PartialEq, Debug, Reflect, Clone)] #[derive(Component, PartialEq, Debug, Reflect, Clone)]
@ -258,58 +260,121 @@ pub(crate) struct SaveParent(String);
/// parent some_other_file.entity /// parent some_other_file.entity
/// ``` /// ```
pub(crate) fn parse_save_parent(line: &str) -> Result<SaveParent, SaveEntityParseError> { pub(crate) fn parse_save_parent(line: &str) -> Result<SaveParent, SaveEntityParseError> {
todo!() let (rem, (_, _, parent_file)) = tuple((tag("parent"), space1, parse_string))(line)?;
debug_assert!(rem.is_empty());
Ok(SaveParent(parent_file.into()))
} }
#[test] #[test]
fn test_parse_parent() { fn test_parse_parent() {
let line = "parent some_other_file.entity"; let line = "parent \"some_other_file.entity\"";
let parsed = parse_save_parent(line).unwrap(); let parsed = parse_save_parent(line).unwrap();
let expected = SaveParent("some_other_file.entity".into()); let expected = SaveParent("some_other_file.entity".into());
assert_eq!(parsed, expected); assert_eq!(parsed, expected);
} }
/// ///
/// Parse the Window component (which is very big!) /// Parse the Window component (which is very big!)
/// We only sparsely define the bits that we want to edit (for now) /// We only sparsely define the bits that we want to edit (for now)
/// ///
pub(crate) fn parse_save_window(line: &str) -> Result<Window, SaveEntityParseError> { pub(crate) fn parse_save_window(line: &str) -> Result<Window, SaveEntityParseError> {
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] #[test]
fn test_parse_window() { fn test_parse_window() {
let line = "window \"Editor\" visible false"; let line = "window \"Editor\" visible false";
let parsed = parse_save_window(line).unwrap(); let parsed = parse_save_window(line).unwrap();
let expected = Window { visible: false, title: "Editor".into(), ..default() }; let expected = Window {
assert_eq!(parsed.visible, expected.visible); visible: false,
assert_eq!(parsed.title, expected.title); 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 /// 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<TextBundle, SaveEntityParseError> { pub(crate) fn parse_save_ui_text(line: &str) -> Result<TextBundle, SaveEntityParseError> {
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] #[test]
fn test_save_ui_text() { 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 /// Returns a parser function for a basic stand-alone tag
/// e.g., "editor_tag" or "ui_node" /// e.g., "editor_tag" or "ui_node"
pub(crate) fn parse_save_tag<T: Component + Reflect>(tag: &str) -> impl FnOnce(&str) -> Result<T, SaveEntityParseError> { pub(crate) fn parse_save_tag<T: Component + Reflect + Default>(
move |line: &str| -> Result<T, SaveEntityParseError> { t: &str,
todo!() ) -> impl FnOnce(&str) -> Result<T, SaveEntityParseError> + '_ {
} move |line: &str| -> Result<T, SaveEntityParseError> { Ok(tag(t)(line).map(|_| T::default())?) }
} }
#[derive(Component, Reflect, PartialEq, Debug, Default)]
#[reflect(Component)]
struct TestingTag;
#[test] #[test]
fn test_save_tag() { fn test_save_tag() {
todo!() let line = "testing";
let parsed = parse_save_tag::<TestingTag>("testing")(line).unwrap();
let expected = TestingTag;
assert_eq!(parsed, expected);
} }
#[derive(Component, Clone, Debug, Reflect, PartialEq)] #[derive(Component, Clone, Debug, Reflect, PartialEq)]
@ -317,11 +382,25 @@ fn test_save_tag() {
pub(crate) struct SaveTargetCamera(String); pub(crate) struct SaveTargetCamera(String);
/// Parses the a SaveTargetCamera which at runtime is converted to a TargetCamera /// Parses the a SaveTargetCamera which at runtime is converted to a TargetCamera
pub(crate) fn parse_save_target_camera(line: &str) -> Result<SaveTargetCamera, SaveEntityParseError> { /// ```text
todo!() /// target-camera "./level-camera.entity"
/// ```
pub(crate) fn parse_save_target_camera(
line: &str,
) -> Result<SaveTargetCamera, SaveEntityParseError> {
Ok(
tuple((tag("target-camera"), space1, parse_string))(line).map(
|(_, (_, _, target_camera_entity_file))| {
SaveTargetCamera(target_camera_entity_file.into())
},
)?,
)
} }
#[test] #[test]
fn test_target_camera() { fn test_target_camera() {
todo!() 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);
}

@ -1,23 +1,24 @@
pub(crate) use bevy::ecs::reflect::ReflectCommandExt;
pub(crate) use bevy::{ pub(crate) use bevy::{
app::AppExit, app::AppExit,
asset::{io::Reader, AssetLoader, LoadContext, AsyncReadExt}, asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext},
color::palettes::css::{GRAY, RED, DARK_GREEN, BLUE}, color::palettes::css::{BLUE, DARK_GREEN, GRAY, RED},
gltf::Gltf, gltf::Gltf,
input::common_conditions::input_just_pressed, input::common_conditions::input_just_pressed,
prelude::*, prelude::*,
render::camera::RenderTarget, render::camera::RenderTarget,
window::{WindowRef, WindowCloseRequested, PrimaryWindow}, window::{PrimaryWindow, WindowCloseRequested, WindowRef},
}; };
pub(crate) use bevy::ecs::reflect::ReflectCommandExt;
pub(crate) use nom::bytes::complete::take; pub(crate) use nom::bytes::complete::take;
pub(crate) use nom::{ pub(crate) use nom::{
IResult, branch::{alt, permutation},
bytes::complete::{tag, take_until1}, bytes::complete::{tag, take_until1},
character::complete::space1, character::complete::{char, hex_digit1, space1},
number::complete::float, number::complete::float,
sequence::tuple, sequence::tuple,
IResult,
}; };
pub(crate) use uuid::Uuid;
pub(crate) use thiserror::Error; pub(crate) use thiserror::Error;
pub(crate) use uuid::Uuid;
pub(crate) use crate::{conditions::*, parser::*}; pub(crate) use crate::{conditions::*, parser::*};

Loading…
Cancel
Save