From 6619f067b15ddde2ba5785509378a9d705be2ba5 Mon Sep 17 00:00:00 2001
From: Jerome Humbert <djeedai@gmail.com>
Date: Sat, 19 Mar 2022 12:10:28 +0000
Subject: [PATCH] Tighter dependencies with features (#9)

Enable minimal dependencies with new features `bevu_sprite` and
`bevy_ui`, removing the `bevy/render` mandatory dependency. Those new
features are enabled by default, and enable the built-in lenses for the
related Bevy crates. The core `bevy_tweening` crate itself does not take
any optional Bevy dependency anymore, allowing for a slim build with
only the core Bevy functionalities.
---
 .github/workflows/ci.yaml | 18 ++++++--
 CHANGELOG.md              |  6 +++
 Cargo.toml                | 17 ++++---
 src/lens.rs               | 12 +++++
 src/lib.rs                | 95 ++++++++++++++++++++-------------------
 src/plugin.rs             | 12 +++--
 6 files changed, 103 insertions(+), 57 deletions(-)

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 5d415a4..2aba7b6 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -44,10 +44,22 @@ jobs:
           sudo apt-get update
           sudo apt install -y xvfb libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers
         if: runner.os == 'linux'
-      - name: Build & run tests
-        run: cargo test
+      - name: Build & run tests (slim)
+        run: cargo test --no-default-features
         env:
           CARGO_INCREMENTAL: 0
+      - name: Build & run tests (ui)
+        run: cargo test --no-default-features --features="bevy_ui"
+        env:
+          CARGO_INCREMENTAL: 1
+      - name: Build & run tests (sprite)
+        run: cargo test --no-default-features --features="bevy_sprite"
+        env:
+          CARGO_INCREMENTAL: 1
+      - name: Build & run tests (all)
+        run: cargo test --all-features
+        env:
+          CARGO_INCREMENTAL: 1
 
   coverage:
     name: Coverage
@@ -87,7 +99,7 @@ jobs:
           RUST_BACKTRACE=1 cargo install --version 0.19.1 cargo-tarpaulin
       - name: Generate code coverage
         run: |
-          RUST_BACKTRACE=1 cargo tarpaulin --verbose --timeout 120 --out Lcov --workspace
+          RUST_BACKTRACE=1 cargo tarpaulin --all-features --verbose --timeout 120 --out Lcov --workspace
           ls -la
       - name: Upload code coverage
         uses: coverallsapp/github-action@master
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1237b92..5c81f08 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,12 @@
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [Unreleased]
+
+### Changed
+
+- Better dependencies: Introduced features `bevy_sprite` and `bevy_ui` taking a dependency on the same-named crates of Bevy, and removed the forced dependency on `bevy/render`. The new features are enabled by default, for discoverability, and only impact the prebuilt lenses. The library now builds without any Bevy optional feature.
+
 ## [0.3.3] - 2022-03-05
 
 ### Added
diff --git a/Cargo.toml b/Cargo.toml
index 8645e90..ed26c9c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,9 +12,16 @@ license = "MIT OR Apache-2.0"
 readme = "README.md"
 exclude = ["examples/*.gif", ".github", "release.md"]
 
+[features]
+default = ["bevy_sprite", "bevy_ui"]
+# Enable built-in lenses for Bevy sprites
+bevy_sprite = ["bevy/bevy_sprite", "bevy/bevy_render"]
+# Enable built-in lenses for Bevy UI
+bevy_ui = ["bevy/bevy_ui", "bevy/bevy_text", "bevy/bevy_render"]
+
 [dependencies]
 interpolation = "0.2"
-bevy = { version = "0.6", default-features = false, features = [ "render" ] }
+bevy = { version = "0.6", default-features = false }
 
 [dev-dependencies]
 bevy-inspector-egui = "0.8"
@@ -25,11 +32,11 @@ required-features = [ "bevy/bevy_winit" ]
 
 [[example]]
 name = "colormaterial_color"
-required-features = [ "bevy/bevy_winit" ]
+required-features = [ "bevy_sprite", "bevy/bevy_winit" ]
 
 [[example]]
 name = "sprite_color"
-required-features = [ "bevy/bevy_winit" ]
+required-features = [ "bevy_sprite", "bevy/bevy_winit" ]
 
 [[example]]
 name = "transform_translation"
@@ -41,11 +48,11 @@ required-features = [ "bevy/bevy_winit" ]
 
 [[example]]
 name = "ui_position"
-required-features = [ "bevy/bevy_winit" ]
+required-features = [ "bevy_ui", "bevy/bevy_winit" ]
 
 [[example]]
 name = "text_color"
-required-features = [ "bevy/bevy_winit" ]
+required-features = [ "bevy_ui", "bevy/bevy_winit" ]
 
 [[example]]
 name = "sequence"
diff --git a/src/lens.rs b/src/lens.rs
index 7e9a908..d59a84e 100644
--- a/src/lens.rs
+++ b/src/lens.rs
@@ -76,6 +76,7 @@ pub trait Lens<T> {
 ///
 /// [`color`]: https://docs.rs/bevy/0.6.1/bevy/text/struct.TextStyle.html#structfield.color
 /// [`Text`]: https://docs.rs/bevy/0.6.1/bevy/text/struct.Text.html
+#[cfg(feature = "bevy_ui")]
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub struct TextColorLens {
     /// Start color.
@@ -86,6 +87,7 @@ pub struct TextColorLens {
     pub section: usize,
 }
 
+#[cfg(feature = "bevy_ui")]
 impl Lens<Text> for TextColorLens {
     fn lerp(&mut self, target: &mut Text, ratio: f32) {
         // Note: Add<f32> for Color affects alpha, but not Mul<f32>. So use Vec4 for consistency.
@@ -277,6 +279,7 @@ impl Lens<Transform> for TransformScaleLens {
 ///
 /// [`position`]: https://docs.rs/bevy/0.6.1/bevy/ui/struct.Style.html#structfield.position
 /// [`Style`]: https://docs.rs/bevy/0.6.1/bevy/ui/struct.Style.html
+#[cfg(feature = "bevy_ui")]
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub struct UiPositionLens {
     /// Start position.
@@ -285,6 +288,7 @@ pub struct UiPositionLens {
     pub end: Rect<Val>,
 }
 
+#[cfg(feature = "bevy_ui")]
 fn lerp_val(start: &Val, end: &Val, ratio: f32) -> Val {
     match (start, end) {
         (Val::Percent(start), Val::Percent(end)) => Val::Percent(start + (end - start) * ratio),
@@ -293,6 +297,7 @@ fn lerp_val(start: &Val, end: &Val, ratio: f32) -> Val {
     }
 }
 
+#[cfg(feature = "bevy_ui")]
 impl Lens<Style> for UiPositionLens {
     fn lerp(&mut self, target: &mut Style, ratio: f32) {
         target.position = Rect {
@@ -308,6 +313,7 @@ impl Lens<Style> for UiPositionLens {
 ///
 /// [`color`]: https://docs.rs/bevy/0.6.1/bevy/sprite/struct.ColorMaterial.html#structfield.color
 /// [`ColorMaterial`]: https://docs.rs/bevy/0.6.1/bevy/sprite/struct.ColorMaterial.html
+#[cfg(feature = "bevy_sprite")]
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub struct ColorMaterialColorLens {
     /// Start color.
@@ -316,6 +322,7 @@ pub struct ColorMaterialColorLens {
     pub end: Color,
 }
 
+#[cfg(feature = "bevy_sprite")]
 impl Lens<ColorMaterial> for ColorMaterialColorLens {
     fn lerp(&mut self, target: &mut ColorMaterial, ratio: f32) {
         // Note: Add<f32> for Color affects alpha, but not Mul<f32>. So use Vec4 for consistency.
@@ -330,6 +337,7 @@ impl Lens<ColorMaterial> for ColorMaterialColorLens {
 ///
 /// [`color`]: https://docs.rs/bevy/0.6.1/bevy/sprite/struct.Sprite.html#structfield.color
 /// [`Sprite`]: https://docs.rs/bevy/0.6.1/bevy/sprite/struct.Sprite.html
+#[cfg(feature = "bevy_sprite")]
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub struct SpriteColorLens {
     /// Start color.
@@ -338,6 +346,7 @@ pub struct SpriteColorLens {
     pub end: Color,
 }
 
+#[cfg(feature = "bevy_sprite")]
 impl Lens<Sprite> for SpriteColorLens {
     fn lerp(&mut self, target: &mut Sprite, ratio: f32) {
         // Note: Add<f32> for Color affects alpha, but not Mul<f32>. So use Vec4 for consistency.
@@ -353,6 +362,7 @@ mod tests {
     use super::*;
     use std::f32::consts::TAU;
 
+    #[cfg(feature = "bevy_ui")]
     #[test]
     fn text_color() {
         let mut lens = TextColorLens {
@@ -582,6 +592,7 @@ mod tests {
         assert!(transform.scale.abs_diff_eq(Vec3::new(0.3, 0.6, -1.2), 1e-5));
     }
 
+    #[cfg(feature = "bevy_sprite")]
     #[test]
     fn colormaterial_color() {
         let mut lens = ColorMaterialColorLens {
@@ -603,6 +614,7 @@ mod tests {
         assert_eq!(mat.color, Color::rgba(0.7, 0., 0.3, 1.0));
     }
 
+    #[cfg(feature = "bevy_sprite")]
     #[test]
     fn sprite_color() {
         let mut lens = SpriteColorLens {
diff --git a/src/lib.rs b/src/lib.rs
index a98631a..6148b6e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -525,6 +525,35 @@ impl<T: Asset> AssetAnimator<T> {
 #[cfg(test)]
 mod tests {
     use super::{lens::*, *};
+    use bevy::reflect::TypeUuid;
+
+    struct DummyLens {
+        start: f32,
+        end: f32,
+    }
+
+    #[derive(Component)]
+    struct DummyComponent {
+        value: f32,
+    }
+
+    #[derive(Reflect, TypeUuid)]
+    #[uuid = "a33abc11-264e-4bbb-82e8-b87226bb4383"]
+    struct DummyAsset {
+        value: f32,
+    }
+
+    impl Lens<DummyComponent> for DummyLens {
+        fn lerp(&mut self, target: &mut DummyComponent, ratio: f32) {
+            target.value = self.start.lerp(&self.end, &ratio);
+        }
+    }
+
+    impl Lens<DummyAsset> for DummyLens {
+        fn lerp(&mut self, target: &mut DummyAsset, ratio: f32) {
+            target.value = self.start.lerp(&self.end, &ratio);
+        }
+    }
 
     #[test]
     fn tweening_type() {
@@ -581,12 +610,9 @@ mod tests {
             EaseFunction::QuadraticInOut,
             TweeningType::PingPong,
             std::time::Duration::from_secs(1),
-            TransformRotationLens {
-                start: Quat::IDENTITY,
-                end: Quat::from_axis_angle(Vec3::Z, std::f32::consts::PI / 2.),
-            },
+            DummyLens { start: 0., end: 1. },
         );
-        let animator = Animator::new(tween);
+        let animator = Animator::<DummyComponent>::new(tween);
         assert_eq!(animator.state, AnimatorState::default());
         let tween = animator.tweenable().unwrap();
         assert_eq!(tween.progress(), 0.);
@@ -596,14 +622,11 @@ mod tests {
     #[test]
     fn animator_with_state() {
         for state in [AnimatorState::Playing, AnimatorState::Paused] {
-            let tween = Tween::new(
+            let tween = Tween::<DummyComponent>::new(
                 EaseFunction::QuadraticInOut,
                 TweeningType::PingPong,
                 std::time::Duration::from_secs(1),
-                TransformRotationLens {
-                    start: Quat::IDENTITY,
-                    end: Quat::from_axis_angle(Vec3::Z, std::f32::consts::PI / 2.),
-                },
+                DummyLens { start: 0., end: 1. },
             );
             let animator = Animator::new(tween).with_state(state);
             assert_eq!(animator.state, state);
@@ -613,18 +636,15 @@ mod tests {
     /// Animator::default() + Animator::set_tweenable()
     #[test]
     fn animator_default() {
-        let mut animator = Animator::<Transform>::default();
+        let mut animator = Animator::<DummyComponent>::default();
         assert!(animator.tweenable().is_none());
         assert!(animator.tweenable_mut().is_none());
 
-        let tween = Tween::new(
+        let tween = Tween::<DummyComponent>::new(
             EaseFunction::QuadraticInOut,
             TweeningType::PingPong,
             std::time::Duration::from_secs(1),
-            TransformRotationLens {
-                start: Quat::IDENTITY,
-                end: Quat::from_axis_angle(Vec3::Z, std::f32::consts::PI / 2.),
-            },
+            DummyLens { start: 0., end: 1. },
         );
         animator.set_tweenable(tween);
         assert!(animator.tweenable().is_some());
@@ -634,14 +654,11 @@ mod tests {
     /// Animator control playback
     #[test]
     fn animator_controls() {
-        let tween = Tween::new(
+        let tween = Tween::<DummyComponent>::new(
             EaseFunction::QuadraticInOut,
             TweeningType::PingPong,
             std::time::Duration::from_secs(1),
-            TransformRotationLens {
-                start: Quat::IDENTITY,
-                end: Quat::from_axis_angle(Vec3::Z, std::f32::consts::PI / 2.),
-            },
+            DummyLens { start: 0., end: 1. },
         );
         let mut animator = Animator::new(tween);
         assert_eq!(animator.state, AnimatorState::Playing);
@@ -676,16 +693,13 @@ mod tests {
     /// AssetAnimator::new()
     #[test]
     fn asset_animator_new() {
-        let tween = Tween::new(
+        let tween = Tween::<DummyAsset>::new(
             EaseFunction::QuadraticInOut,
             TweeningType::PingPong,
             std::time::Duration::from_secs(1),
-            ColorMaterialColorLens {
-                start: Color::RED,
-                end: Color::BLUE,
-            },
+            DummyLens { start: 0., end: 1. },
         );
-        let animator = AssetAnimator::new(Handle::<ColorMaterial>::default(), tween);
+        let animator = AssetAnimator::new(Handle::<DummyAsset>::default(), tween);
         assert_eq!(animator.state, AnimatorState::default());
         let tween = animator.tweenable().unwrap();
         assert_eq!(tween.progress(), 0.);
@@ -695,17 +709,14 @@ mod tests {
     #[test]
     fn asset_animator_with_state() {
         for state in [AnimatorState::Playing, AnimatorState::Paused] {
-            let tween = Tween::new(
+            let tween = Tween::<DummyAsset>::new(
                 EaseFunction::QuadraticInOut,
                 TweeningType::PingPong,
                 std::time::Duration::from_secs(1),
-                ColorMaterialColorLens {
-                    start: Color::RED,
-                    end: Color::BLUE,
-                },
+                DummyLens { start: 0., end: 1. },
             );
             let animator =
-                AssetAnimator::new(Handle::<ColorMaterial>::default(), tween).with_state(state);
+                AssetAnimator::new(Handle::<DummyAsset>::default(), tween).with_state(state);
             assert_eq!(animator.state, state);
         }
     }
@@ -713,24 +724,21 @@ mod tests {
     /// AssetAnimator::default() + AssetAnimator::set_tweenable()
     #[test]
     fn asset_animator_default() {
-        let mut animator = AssetAnimator::<ColorMaterial>::default();
+        let mut animator = AssetAnimator::<DummyAsset>::default();
         assert!(animator.tweenable().is_none());
         assert!(animator.tweenable_mut().is_none());
-        assert_eq!(animator.handle(), Handle::<ColorMaterial>::default());
+        assert_eq!(animator.handle(), Handle::<DummyAsset>::default());
 
         let tween = Tween::new(
             EaseFunction::QuadraticInOut,
             TweeningType::PingPong,
             std::time::Duration::from_secs(1),
-            ColorMaterialColorLens {
-                start: Color::RED,
-                end: Color::BLUE,
-            },
+            DummyLens { start: 0., end: 1. },
         );
         animator.set_tweenable(tween);
         assert!(animator.tweenable().is_some());
         assert!(animator.tweenable_mut().is_some());
-        assert_eq!(animator.handle(), Handle::<ColorMaterial>::default());
+        assert_eq!(animator.handle(), Handle::<DummyAsset>::default());
     }
 
     /// AssetAnimator control playback
@@ -740,12 +748,9 @@ mod tests {
             EaseFunction::QuadraticInOut,
             TweeningType::PingPong,
             std::time::Duration::from_secs(1),
-            ColorMaterialColorLens {
-                start: Color::RED,
-                end: Color::BLUE,
-            },
+            DummyLens { start: 0., end: 1. },
         );
-        let mut animator = AssetAnimator::new(Handle::<ColorMaterial>::default(), tween);
+        let mut animator = AssetAnimator::new(Handle::<DummyAsset>::default(), tween);
         assert_eq!(animator.state, AnimatorState::Playing);
         assert!(animator.progress().abs() <= 1e-5);
 
diff --git a/src/plugin.rs b/src/plugin.rs
index 9fe526e..7d0b70c 100644
--- a/src/plugin.rs
+++ b/src/plugin.rs
@@ -34,10 +34,14 @@ pub struct TweeningPlugin;
 impl Plugin for TweeningPlugin {
     fn build(&self, app: &mut App) {
         app.add_event::<TweenCompleted>()
-            .add_system(component_animator_system::<Transform>)
-            .add_system(component_animator_system::<Text>)
-            .add_system(component_animator_system::<Style>)
-            .add_system(component_animator_system::<Sprite>)
+            .add_system(component_animator_system::<Transform>);
+
+        #[cfg(eature = "bevy_ui")]
+        app.add_system(component_animator_system::<Text>)
+            .add_system(component_animator_system::<Style>);
+
+        #[cfg(eature = "bevy_sprite")]
+        app.add_system(component_animator_system::<Sprite>)
             .add_system(asset_animator_system::<ColorMaterial>);
     }
 }
-- 
GitLab