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}