From 44b58bc0bf14b0ed97f747e01feeb9c93af36225 Mon Sep 17 00:00:00 2001 From: Jonathan Cornaz Date: Mon, 20 Jun 2022 20:05:30 +0000 Subject: [PATCH] feat(unstable-load-from-file): add from_ron_str and from_ron_bytes for `SpriteSheetAnimation` --- Cargo.toml | 1 + src/animation/parse.rs | 307 +++++++++++++++++++++++++++-------------- src/lib.rs | 6 +- 3 files changed, 209 insertions(+), 105 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e474246..3d77e10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ bevy_asset = { version = "0.7.0", default-features = false } bevy_utils = { version = "0.7.0", default-features = false, optional = true } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } serde_yaml = { version = "0.8.24", default-features = false, optional = true } +ron = { version = "0.7.1", default-features = false, optional = true } anyhow = { version = "1.0", default-features = false, optional = true } [dev-dependencies] diff --git a/src/animation/parse.rs b/src/animation/parse.rs index d285082..8cfd570 100644 --- a/src/animation/parse.rs +++ b/src/animation/parse.rs @@ -133,7 +133,7 @@ impl SpriteSheetAnimation { /// ```yaml /// # The mode can be one of: 'once', 'repeat', 'ping-pong' /// # or 'repeat-from: n' (where 'n' is the frame-index to repeat from) - /// # The default is 'repeat' + /// # The default is 'Repeat' /// mode: PingPong /// frames: /// - index: 0 # index in the sprite sheet for that frame @@ -153,19 +153,18 @@ impl SpriteSheetAnimation { /// # Errors /// /// Returns an error if the content is not a valid yaml representation of an animation - #[cfg(feature = "unstable-load-from-file")] pub fn from_yaml_str(yaml: &str) -> Result { - serde_yaml::from_str(yaml).map_err(AnimationParseError) + Self::from_yaml_bytes(yaml.as_bytes()) } - /// Parse content of a yaml bytes representing the animation + /// Parse content of yaml bytes representing the animation /// /// # Yaml schema /// /// ```yaml /// # The mode can be one of: 'Once', 'Repeat', 'PingPong' /// # or 'RepeatFrom: n' (where 'n' is the frame-index to repeat from) - /// # The default is 'repeat' + /// # The default is 'Repeat' /// mode: PingPong /// frames: /// - index: 0 # index in the sprite sheet for that frame @@ -185,15 +184,76 @@ impl SpriteSheetAnimation { /// # Errors /// /// Returns an error if the content is not a valid yaml representation of an animation - #[cfg(feature = "unstable-load-from-file")] pub fn from_yaml_bytes(yaml: &[u8]) -> Result { - serde_yaml::from_slice(yaml).map_err(AnimationParseError) + serde_yaml::from_slice(yaml).map_err(AnimationParseError::new) + } + + /// Parse content of a ron file represenging the animation + /// + /// # Schema + /// + /// ```ron + /// ( + /// // The mode can be one of: 'Once', 'Repeat', 'PingPong' + /// // or 'RepeatFrom(n)' (where 'n' is the frame-index to repeat from) + /// // The default is 'Repeat' + /// mode: PingPong, + /// frames: [ + /// ( + /// index: 0, //index in the sprite sheet for that frame + /// duration: Some(100), // duration of the frame in milliseconds + /// ), + /// (index: 1, duration: Some(100)), + /// (index: 2, duration: Some(120)), + /// ] + /// ) + /// ``` + /// + /// # Errors + /// + /// Returns an error if the content is not a valid ron representation of an animation + pub fn from_ron_str(ron: &str) -> Result { + Self::from_ron_bytes(ron.as_bytes()) + } + + /// Parse content of a ron file represenging the animation + /// + /// # Schema + /// + /// ```ron + /// ( + /// // The mode can be one of: 'Once', 'Repeat', 'PingPong' + /// // or 'RepeatFrom(n)' (where 'n' is the frame-index to repeat from) + /// // The default is 'Repeat' + /// mode: PingPong, + /// frames: [ + /// ( + /// index: 0, //index in the sprite sheet for that frame + /// duration: Some(100), // duration of the frame in milliseconds + /// ), + /// (index: 1, duration: Some(100)), + /// (index: 2, duration: Some(120)), + /// ] + /// ) + /// ``` + /// + /// # Errors + /// + /// Returns an error if the content is not a valid ron representation of an animation + pub fn from_ron_bytes(ron: &[u8]) -> Result { + ron::de::from_bytes(ron).map_err(AnimationParseError::new) } } #[derive(Debug)] #[non_exhaustive] -pub struct AnimationParseError(pub(super) serde_yaml::Error); +pub struct AnimationParseError(anyhow::Error); + +impl AnimationParseError { + fn new(err: impl Error + Send + Sync + 'static) -> Self { + Self(anyhow::Error::from(err)) + } +} impl Display for AnimationParseError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -207,10 +267,13 @@ impl Error for AnimationParseError {} mod tests { use super::*; - #[test] - fn parse_yaml() { - // given - let content = " + mod yaml { + use super::*; + + #[test] + fn parse() { + // given + let content = " mode: PingPong frames: - index: 0 # index in the sprite sheet for that frame @@ -220,72 +283,72 @@ mod tests { - index: 2 duration: 120"; - // when - let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); - - // then - assert_eq!(animation.mode, Mode::PingPong); - assert_eq!( - animation.frames, - vec![ - Frame::new(0, Duration::from_millis(100)), - Frame::new(1, Duration::from_millis(100)), - Frame::new(2, Duration::from_millis(120)), - ] - ); - } + // when + let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); + + // then + assert_eq!(animation.mode, Mode::PingPong); + assert_eq!( + animation.frames, + vec![ + Frame::new(0, Duration::from_millis(100)), + Frame::new(1, Duration::from_millis(100)), + Frame::new(2, Duration::from_millis(120)), + ] + ); + } - #[test] - fn parse_yaml_default_mode() { - // given - let content = " + #[test] + fn default_mode() { + // given + let content = " frames: - index: 0 duration: 100"; - // when - let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); + // when + let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); - // then - assert_eq!(animation.mode, Mode::RepeatFrom(0)); - } + // then + assert_eq!(animation.mode, Mode::RepeatFrom(0)); + } - #[test] - fn parse_yaml_repeat() { - // given - let content = " + #[test] + fn repeat() { + // given + let content = " mode: Repeat frames: - index: 0 duration: 100"; - // when - let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); + // when + let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); - // then - assert_eq!(animation.mode, Mode::RepeatFrom(0)); - } + // then + assert_eq!(animation.mode, Mode::RepeatFrom(0)); + } - #[test] - fn parse_yaml_once() { - // given - let content = " + #[test] + fn once() { + // given + let content = " mode: Once frames: - index: 0 duration: 100"; - // when - let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); + // when + let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); - // then - assert_eq!(animation.mode, Mode::Once); - } + // then + assert_eq!(animation.mode, Mode::Once); + } - #[test] - fn parse_yaml_repeat_from() { - // given - let content = " + #[test] + fn repeat_from() { + // given + let content = " mode: RepeatFrom: 1 frames: @@ -294,32 +357,32 @@ mod tests { - index: 1 duration: 100"; - // when - let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); + // when + let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); - // then - assert_eq!(animation.mode, Mode::RepeatFrom(1)); - } + // then + assert_eq!(animation.mode, Mode::RepeatFrom(1)); + } - #[test] - fn parse_yaml_zero_duration() { - // given - let content = " + #[test] + fn zero_duration() { + // given + let content = " frames: - index: 0 duration: 0"; - // when - let animation = SpriteSheetAnimation::from_yaml_str(content); + // when + let animation = SpriteSheetAnimation::from_yaml_str(content); - // then - assert!(animation.is_err()); - } + // then + assert!(animation.is_err()); + } - #[test] - fn parse_yaml_same_duraton_for_all_frames() { - // given - let content = " + #[test] + fn same_duration_for_all_frames() { + // given + let content = " frame_duration: 100 frames: - index: 0 @@ -328,39 +391,75 @@ mod tests { duration: 200 "; - // when - let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); - - // then - assert_eq!( - animation.frames, - vec![ - Frame::new(0, Duration::from_millis(100)), - Frame::new(1, Duration::from_millis(100)), - Frame::new(2, Duration::from_millis(200)), - ] - ); - } + // when + let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); + + // then + assert_eq!( + animation.frames, + vec![ + Frame::new(0, Duration::from_millis(100)), + Frame::new(1, Duration::from_millis(100)), + Frame::new(2, Duration::from_millis(200)), + ] + ); + } - #[test] - fn parse_yaml_same_duraton_for_all_frames_short_hand() { - // given - let content = " + #[test] + fn same_duration_for_all_frames_short_hand() { + // given + let content = " frame_duration: 100 frames: [0, 1, 2] "; - // when - let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); - - // then - assert_eq!( - animation.frames, - vec![ - Frame::new(0, Duration::from_millis(100)), - Frame::new(1, Duration::from_millis(100)), - Frame::new(2, Duration::from_millis(100)), - ] - ); + // when + let animation = SpriteSheetAnimation::from_yaml_str(content).unwrap(); + + // then + assert_eq!( + animation.frames, + vec![ + Frame::new(0, Duration::from_millis(100)), + Frame::new(1, Duration::from_millis(100)), + Frame::new(2, Duration::from_millis(100)), + ] + ); + } + } + + mod ron { + use super::*; + + #[test] + fn frames() { + // given + let content = " + ( + mode: RepeatFrom(1), + frames: [ + ( + index: 0, // index in the sprite sheet for that frame + duration: Some(100), // # duration of the frame in milliseconds + ), + (index: 1, duration: Some(100)), + (index: 2, duration: Some(120)), + ] + )"; + + // when + let animation = SpriteSheetAnimation::from_ron_str(content).unwrap(); + + // then + assert_eq!(animation.mode, Mode::RepeatFrom(1)); + assert_eq!( + animation.frames, + vec![ + Frame::new(0, Duration::from_millis(100)), + Frame::new(1, Duration::from_millis(100)), + Frame::new(2, Duration::from_millis(120)), + ] + ); + } } } diff --git a/src/lib.rs b/src/lib.rs index 7ae3094..9303f2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,7 +116,7 @@ //! ```yaml //! # The mode can be one of: 'Once', 'Repeat', 'PingPong' //! # or 'RepeatFrom: n' (where 'n' is the frame-index to repeat from) -//! # The default is 'repeat' +//! # The default is 'Repeat' //! mode: PingPong //! frame_duration: 100 # duration of the frame in milliseconds //! frames: [0, 1, 2] # Sequence of frame index @@ -139,6 +139,10 @@ //! let handle: Handle = asset_server.load("player_run.animation.yml"); //! # } //! ``` +//! +//! It is also possible to use `ron` instead of `yaml`. +//! +//! For more info on the format see: [`SpriteSheetAnimation::from_yaml_str`] and [`SpriteSheetAnimation::from_ron_str`]. #[cfg(test)] #[macro_use]