revue/plugin/
traits.rs

1//! Plugin trait definition
2
3use super::PluginContext;
4use std::time::Duration;
5
6/// Plugin trait for extending Revue applications
7///
8/// Plugins can hook into the application lifecycle to add functionality
9/// such as logging, analytics, state persistence, or custom behaviors.
10///
11/// # Lifecycle
12///
13/// 1. `on_init` - Called once when the plugin is registered
14/// 2. `on_mount` - Called when the app starts running
15/// 3. `on_tick` - Called on each tick (frame update)
16/// 4. `on_unmount` - Called when the app is shutting down
17///
18/// # Example
19///
20/// ```rust,ignore
21/// use revue::plugin::{Plugin, PluginContext};
22///
23/// struct MyPlugin;
24///
25/// impl Plugin for MyPlugin {
26///     fn name(&self) -> &str { "my-plugin" }
27///
28///     fn on_init(&mut self, ctx: &mut PluginContext) -> revue::Result<()> {
29///         ctx.set_data("initialized", true);
30///         Ok(())
31///     }
32/// }
33/// ```
34pub trait Plugin: Send {
35    /// Plugin name (should be unique)
36    fn name(&self) -> &str;
37
38    /// Called when the plugin is first registered
39    ///
40    /// Use this for one-time initialization like loading configuration
41    /// or setting up resources.
42    fn on_init(&mut self, _ctx: &mut PluginContext) -> crate::Result<()> {
43        Ok(())
44    }
45
46    /// Called when the app starts running
47    ///
48    /// Use this to set up state that depends on the app being ready.
49    fn on_mount(&mut self, _ctx: &mut PluginContext) -> crate::Result<()> {
50        Ok(())
51    }
52
53    /// Called on each tick (frame update)
54    ///
55    /// Use this for periodic tasks like polling, animations, or metrics.
56    /// Keep this lightweight as it runs every frame.
57    fn on_tick(&mut self, _ctx: &mut PluginContext, _delta: Duration) -> crate::Result<()> {
58        Ok(())
59    }
60
61    /// Called when the app is shutting down
62    ///
63    /// Use this for cleanup, saving state, or flushing buffers.
64    fn on_unmount(&mut self, _ctx: &mut PluginContext) -> crate::Result<()> {
65        Ok(())
66    }
67
68    /// Optional CSS styles contributed by this plugin
69    ///
70    /// Return CSS string that will be merged with the app's stylesheet.
71    fn styles(&self) -> Option<&str> {
72        None
73    }
74
75    /// Plugin priority (higher = runs first)
76    ///
77    /// Default is 0. Use positive values for early initialization,
78    /// negative for late cleanup.
79    fn priority(&self) -> i32 {
80        0
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[allow(dead_code)]
89    struct TestPlugin {
90        init_called: bool,
91        mount_called: bool,
92        tick_count: usize,
93    }
94
95    impl Plugin for TestPlugin {
96        fn name(&self) -> &str {
97            "test"
98        }
99
100        fn on_init(&mut self, _ctx: &mut PluginContext) -> crate::Result<()> {
101            self.init_called = true;
102            Ok(())
103        }
104
105        fn on_mount(&mut self, _ctx: &mut PluginContext) -> crate::Result<()> {
106            self.mount_called = true;
107            Ok(())
108        }
109
110        fn on_tick(&mut self, _ctx: &mut PluginContext, _delta: Duration) -> crate::Result<()> {
111            self.tick_count += 1;
112            Ok(())
113        }
114    }
115
116    #[test]
117    fn test_plugin_default_implementations() {
118        struct MinimalPlugin;
119        impl Plugin for MinimalPlugin {
120            fn name(&self) -> &str {
121                "minimal"
122            }
123        }
124
125        let plugin = MinimalPlugin;
126        assert_eq!(plugin.name(), "minimal");
127        assert_eq!(plugin.priority(), 0);
128        assert!(plugin.styles().is_none());
129    }
130}