plexus_substrate/activations/mustache/
activation.rs1use super::storage::{MustacheStorage, MustacheStorageConfig};
8use super::types::MustacheEvent;
9use async_stream::stream;
10use futures::Stream;
11use serde_json::Value;
12use std::sync::Arc;
13use uuid::Uuid;
14
15pub struct Mustache {
17 storage: Arc<MustacheStorage>,
18}
19
20impl Mustache {
21 pub async fn new(config: MustacheStorageConfig) -> Result<Self, String> {
23 let storage = MustacheStorage::new(config)
24 .await
25 .map_err(|e| format!("Failed to initialize mustache storage: {}", e))?;
26
27 Ok(Self {
28 storage: Arc::new(storage),
29 })
30 }
31
32 pub async fn with_defaults() -> Result<Self, String> {
34 Self::new(MustacheStorageConfig::default()).await
35 }
36
37 pub fn storage(&self) -> &MustacheStorage {
39 &self.storage
40 }
41
42 pub async fn register_template_direct(
47 &self,
48 plugin_id: Uuid,
49 method: &str,
50 name: &str,
51 template: &str,
52 ) -> Result<(), String> {
53 if let Err(e) = mustache::compile_str(template) {
55 return Err(format!("Invalid template: {}", e));
56 }
57
58 self.storage
59 .set_template(&plugin_id, method, name, template)
60 .await
61 .map_err(|e| format!("Failed to register template: {}", e))?;
62
63 Ok(())
64 }
65
66 pub async fn register_templates(
71 &self,
72 plugin_id: Uuid,
73 templates: &[(&str, &str, &str)],
74 ) -> Result<(), String> {
75 for (method, name, template) in templates {
76 self.register_template_direct(plugin_id, method, name, template).await?;
77 }
78 Ok(())
79 }
80}
81
82impl Clone for Mustache {
83 fn clone(&self) -> Self {
84 Self {
85 storage: Arc::clone(&self.storage),
86 }
87 }
88}
89
90#[plexus_macros::hub_methods(
92 namespace = "mustache",
93 version = "1.0.0",
94 description = "Mustache template rendering for handle values"
95)]
96impl Mustache {
97 #[plexus_macros::hub_method(
103 description = "Render a value using a registered mustache template",
104 params(
105 plugin_id = "UUID of the plugin that owns the template",
106 method = "Method name the template is for",
107 template_name = "Template name (defaults to 'default' if not specified)",
108 value = "JSON value to render with the template"
109 )
110 )]
111 async fn render(
112 &self,
113 plugin_id: Uuid,
114 method: String,
115 template_name: Option<String>,
116 value: Value,
117 ) -> impl Stream<Item = MustacheEvent> + Send + 'static {
118 let storage = Arc::clone(&self.storage);
119 let name = template_name.unwrap_or_else(|| "default".to_string());
120
121 stream! {
122 match storage.get_template(&plugin_id, &method, &name).await {
124 Ok(Some(template_str)) => {
125 match mustache::compile_str(&template_str) {
127 Ok(template) => {
128 let mut output = Vec::new();
129 match template.render(&mut output, &value) {
130 Ok(()) => {
131 match String::from_utf8(output) {
132 Ok(rendered) => {
133 yield MustacheEvent::Rendered { output: rendered };
134 }
135 Err(e) => {
136 yield MustacheEvent::Error {
137 message: format!("UTF-8 conversion error: {}", e),
138 };
139 }
140 }
141 }
142 Err(e) => {
143 yield MustacheEvent::Error {
144 message: format!("Template render error: {}", e),
145 };
146 }
147 }
148 }
149 Err(e) => {
150 yield MustacheEvent::Error {
151 message: format!("Template compile error: {}", e),
152 };
153 }
154 }
155 }
156 Ok(None) => {
157 yield MustacheEvent::NotFound {
158 message: format!(
159 "Template not found: plugin={}, method={}, name={}",
160 plugin_id, method, name
161 ),
162 };
163 }
164 Err(e) => {
165 yield MustacheEvent::Error {
166 message: format!("Storage error: {}", e),
167 };
168 }
169 }
170 }
171 }
172
173 #[plexus_macros::hub_method(
178 description = "Register a mustache template for a plugin method",
179 params(
180 plugin_id = "UUID of the plugin registering the template",
181 method = "Method name this template is for",
182 name = "Template name (e.g., 'default', 'compact', 'verbose')",
183 template = "Mustache template content"
184 )
185 )]
186 async fn register_template(
187 &self,
188 plugin_id: Uuid,
189 method: String,
190 name: String,
191 template: String,
192 ) -> impl Stream<Item = MustacheEvent> + Send + 'static {
193 let storage = Arc::clone(&self.storage);
194
195 stream! {
196 if let Err(e) = mustache::compile_str(&template) {
198 yield MustacheEvent::Error {
199 message: format!("Invalid template: {}", e),
200 };
201 return;
202 }
203
204 match storage.set_template(&plugin_id, &method, &name, &template).await {
205 Ok(info) => {
206 yield MustacheEvent::Registered { template: info };
207 }
208 Err(e) => {
209 yield MustacheEvent::Error {
210 message: format!("Failed to register template: {}", e),
211 };
212 }
213 }
214 }
215 }
216
217 #[plexus_macros::hub_method(
219 description = "List all templates registered for a plugin",
220 params(plugin_id = "UUID of the plugin to list templates for")
221 )]
222 async fn list_templates(
223 &self,
224 plugin_id: Uuid,
225 ) -> impl Stream<Item = MustacheEvent> + Send + 'static {
226 let storage = Arc::clone(&self.storage);
227
228 stream! {
229 match storage.list_templates(&plugin_id).await {
230 Ok(templates) => {
231 yield MustacheEvent::Templates { templates };
232 }
233 Err(e) => {
234 yield MustacheEvent::Error {
235 message: format!("Failed to list templates: {}", e),
236 };
237 }
238 }
239 }
240 }
241
242 #[plexus_macros::hub_method(
244 description = "Get a specific template by plugin, method, and name",
245 params(
246 plugin_id = "UUID of the plugin that owns the template",
247 method = "Method name the template is for",
248 name = "Template name"
249 )
250 )]
251 async fn get_template(
252 &self,
253 plugin_id: Uuid,
254 method: String,
255 name: String,
256 ) -> impl Stream<Item = MustacheEvent> + Send + 'static {
257 let storage = Arc::clone(&self.storage);
258
259 stream! {
260 match storage.get_template(&plugin_id, &method, &name).await {
261 Ok(Some(template)) => {
262 yield MustacheEvent::Template { template };
263 }
264 Ok(None) => {
265 yield MustacheEvent::NotFound {
266 message: format!(
267 "Template not found: plugin={}, method={}, name={}",
268 plugin_id, method, name
269 ),
270 };
271 }
272 Err(e) => {
273 yield MustacheEvent::Error {
274 message: format!("Failed to get template: {}", e),
275 };
276 }
277 }
278 }
279 }
280
281 #[plexus_macros::hub_method(
283 description = "Delete a specific template",
284 params(
285 plugin_id = "UUID of the plugin that owns the template",
286 method = "Method name the template is for",
287 name = "Template name"
288 )
289 )]
290 async fn delete_template(
291 &self,
292 plugin_id: Uuid,
293 method: String,
294 name: String,
295 ) -> impl Stream<Item = MustacheEvent> + Send + 'static {
296 let storage = Arc::clone(&self.storage);
297
298 stream! {
299 match storage.delete_template(&plugin_id, &method, &name).await {
300 Ok(deleted) => {
301 yield MustacheEvent::Deleted {
302 count: if deleted { 1 } else { 0 },
303 };
304 }
305 Err(e) => {
306 yield MustacheEvent::Error {
307 message: format!("Failed to delete template: {}", e),
308 };
309 }
310 }
311 }
312 }
313}