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>>), } // 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())) } } #[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 { 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, ) -> Result, 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, ) -> Result, 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, ) -> Result, 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, ) -> Result, 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, ) -> Result, 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, ) -> Result, 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, ) -> Result, 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: &str, ) -> impl FnOnce(&Vec) -> Result, SaveEntityParseError> + '_ { move |tokens: &Vec| -> Result, 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::("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, ) -> Result, 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()); }