1use serde::{Deserialize, Serialize};
2use std::sync::Arc;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Module {
7 pub name: String,
9
10 pub actions: Vec<String>,
12
13 pub state_keys: Vec<String>,
15
16 pub persist: bool,
18
19 pub version: Option<u32>,
21}
22
23impl Module {
24 pub fn new(name: impl Into<String>) -> Self {
25 Self {
26 name: name.into(),
27 actions: Vec::new(),
28 state_keys: Vec::new(),
29 persist: false,
30 version: None,
31 }
32 }
33
34 pub fn with_actions(mut self, actions: Vec<String>) -> Self {
35 self.actions = actions;
36 self
37 }
38
39 pub fn with_state_keys(mut self, state_keys: Vec<String>) -> Self {
40 self.state_keys = state_keys;
41 self
42 }
43
44 pub fn with_persist(mut self, persist: bool) -> Self {
45 self.persist = persist;
46 self
47 }
48
49 pub fn with_version(mut self, version: u32) -> Self {
50 self.version = Some(version);
51 self
52 }
53}
54
55pub trait ModuleLifecycle {
57 fn on_created(&mut self);
59
60 fn on_destroyed(&mut self);
62
63 fn on_state_changed(&mut self, state: serde_json::Value);
65}
66
67pub type LifecycleCallback = Box<dyn Fn() + Send + Sync>;
69
70pub struct ModuleInstance {
76 pub module: Module,
78
79 state: Arc<serde_json::Value>,
81
82 pub mounted: bool,
84
85 on_created: Option<LifecycleCallback>,
87
88 on_destroyed: Option<LifecycleCallback>,
90}
91
92impl ModuleInstance {
93 pub fn new(module: Module, initial_state: serde_json::Value) -> Self {
94 Self {
95 module,
96 state: Arc::new(initial_state),
97 mounted: false,
98 on_created: None,
99 on_destroyed: None,
100 }
101 }
102
103 pub fn set_on_created<F>(&mut self, callback: F)
105 where
106 F: Fn() + Send + Sync + 'static,
107 {
108 self.on_created = Some(Box::new(callback));
109 }
110
111 pub fn set_on_destroyed<F>(&mut self, callback: F)
113 where
114 F: Fn() + Send + Sync + 'static,
115 {
116 self.on_destroyed = Some(Box::new(callback));
117 }
118
119 pub fn mount(&mut self) {
121 if !self.mounted {
122 self.mounted = true;
123 if let Some(ref callback) = self.on_created {
125 callback();
126 }
127 }
128 }
129
130 pub fn unmount(&mut self) {
132 if self.mounted {
133 if let Some(ref callback) = self.on_destroyed {
135 callback();
136 }
137 self.mounted = false;
138 }
139 }
140
141 pub fn update_state(&mut self, patch: serde_json::Value) {
146 let state = Arc::make_mut(&mut self.state);
148 merge_json(state, patch);
149 }
150
151 pub fn update_state_sparse(&mut self, paths: &[String], values: &serde_json::Value) {
156 if let serde_json::Value::Object(map) = values {
158 if paths.iter().any(|p| map.contains_key(p)) {
160 let state = Arc::make_mut(&mut self.state);
161 for path in paths {
162 if let Some(new_value) = map.get(path) {
163 set_value_at_path(state, path, new_value.clone());
164 }
165 }
166 }
167 }
168 }
169
170 pub fn get_state(&self) -> &serde_json::Value {
175 &self.state
176 }
177
178 pub fn get_state_shared(&self) -> Arc<serde_json::Value> {
183 Arc::clone(&self.state)
184 }
185}
186
187fn merge_json(target: &mut serde_json::Value, source: serde_json::Value) {
189 use serde_json::Value;
190
191 match (target, source) {
192 (Value::Object(target_map), Value::Object(source_map)) => {
193 for (key, value) in source_map {
194 if let Some(target_value) = target_map.get_mut(&key) {
195 merge_json(target_value, value);
196 } else {
197 target_map.insert(key, value);
198 }
199 }
200 }
201 (target, source) => {
202 *target = source;
203 }
204 }
205}
206
207fn set_value_at_path(target: &mut serde_json::Value, path: &str, value: serde_json::Value) {
210 use serde_json::Value;
211
212 let parts: Vec<&str> = path.split('.').collect();
213 if parts.is_empty() {
214 return;
215 }
216
217 let mut current = target;
218
219 for part in &parts[..parts.len() - 1] {
221 if let Ok(index) = part.parse::<usize>() {
223 if let Value::Array(arr) = current {
224 while arr.len() <= index {
226 arr.push(Value::Null);
227 }
228 current = &mut arr[index];
229 continue;
230 }
231 }
232
233 if !current.is_object() {
235 *current = Value::Object(serde_json::Map::new());
236 }
237
238 if let Value::Object(map) = current {
239 if !map.contains_key(*part) {
240 map.insert(part.to_string(), Value::Object(serde_json::Map::new()));
241 }
242 current = map.get_mut(*part).unwrap();
243 }
244 }
245
246 let final_key = parts[parts.len() - 1];
248
249 if let Ok(index) = final_key.parse::<usize>() {
251 if let Value::Array(arr) = current {
252 while arr.len() <= index {
253 arr.push(Value::Null);
254 }
255 arr[index] = value;
256 return;
257 }
258 }
259
260 if !current.is_object() {
262 *current = Value::Object(serde_json::Map::new());
263 }
264
265 if let Value::Object(map) = current {
266 map.insert(final_key.to_string(), value);
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273 use serde_json::json;
274
275 #[test]
276 fn test_merge_json() {
277 let mut target = json!({
278 "user": {
279 "name": "Alice",
280 "age": 30
281 }
282 });
283
284 let patch = json!({
285 "user": {
286 "age": 31
287 }
288 });
289
290 merge_json(&mut target, patch);
291
292 assert_eq!(target["user"]["name"], "Alice");
293 assert_eq!(target["user"]["age"], 31);
294 }
295
296 #[test]
297 fn test_lifecycle_on_created_callback() {
298 use std::sync::{Arc, Mutex};
299
300 let module = Module::new("TestModule");
301 let initial_state = json!({});
302 let mut instance = ModuleInstance::new(module, initial_state);
303
304 let called = Arc::new(Mutex::new(false));
306 let called_clone = called.clone();
307
308 instance.set_on_created(move || {
309 *called_clone.lock().unwrap() = true;
310 });
311
312 assert!(!*called.lock().unwrap());
314 instance.mount();
315 assert!(*called.lock().unwrap());
316 assert!(instance.mounted);
317 }
318
319 #[test]
320 fn test_lifecycle_on_destroyed_callback() {
321 use std::sync::{Arc, Mutex};
322
323 let module = Module::new("TestModule");
324 let initial_state = json!({});
325 let mut instance = ModuleInstance::new(module, initial_state);
326
327 let called = Arc::new(Mutex::new(false));
328 let called_clone = called.clone();
329
330 instance.set_on_destroyed(move || {
331 *called_clone.lock().unwrap() = true;
332 });
333
334 instance.mount();
336 assert!(instance.mounted);
337
338 assert!(!*called.lock().unwrap());
340 instance.unmount();
341 assert!(*called.lock().unwrap());
342 assert!(!instance.mounted);
343 }
344
345 #[test]
346 fn test_lifecycle_callbacks_not_called_when_not_set() {
347 let module = Module::new("TestModule");
348 let initial_state = json!({});
349 let mut instance = ModuleInstance::new(module, initial_state);
350
351 instance.mount();
353 assert!(instance.mounted);
354
355 instance.unmount();
356 assert!(!instance.mounted);
357 }
358
359 #[test]
360 fn test_lifecycle_mount_idempotent() {
361 use std::sync::{Arc, Mutex};
362
363 let module = Module::new("TestModule");
364 let initial_state = json!({});
365 let mut instance = ModuleInstance::new(module, initial_state);
366
367 let call_count = Arc::new(Mutex::new(0));
368 let call_count_clone = call_count.clone();
369
370 instance.set_on_created(move || {
371 *call_count_clone.lock().unwrap() += 1;
372 });
373
374 instance.mount();
376 assert_eq!(*call_count.lock().unwrap(), 1);
377
378 instance.mount();
380 assert_eq!(*call_count.lock().unwrap(), 1);
381 }
382
383 #[test]
384 fn test_lifecycle_unmount_idempotent() {
385 use std::sync::{Arc, Mutex};
386
387 let module = Module::new("TestModule");
388 let initial_state = json!({});
389 let mut instance = ModuleInstance::new(module, initial_state);
390
391 let call_count = Arc::new(Mutex::new(0));
392 let call_count_clone = call_count.clone();
393
394 instance.set_on_destroyed(move || {
395 *call_count_clone.lock().unwrap() += 1;
396 });
397
398 instance.mount();
400
401 instance.unmount();
403 assert_eq!(*call_count.lock().unwrap(), 1);
404
405 instance.unmount();
407 assert_eq!(*call_count.lock().unwrap(), 1);
408 }
409
410 #[test]
411 fn test_lifecycle_full_cycle() {
412 use std::sync::{Arc, Mutex};
413
414 let module = Module::new("TestModule");
415 let initial_state = json!({});
416 let mut instance = ModuleInstance::new(module, initial_state);
417
418 let events = Arc::new(Mutex::new(Vec::new()));
419 let events_created = events.clone();
420 let events_destroyed = events.clone();
421
422 instance.set_on_created(move || {
423 events_created.lock().unwrap().push("created");
424 });
425
426 instance.set_on_destroyed(move || {
427 events_destroyed.lock().unwrap().push("destroyed");
428 });
429
430 instance.mount();
432 instance.unmount();
433 instance.mount();
434 instance.unmount();
435
436 let events = events.lock().unwrap();
437 assert_eq!(events.len(), 4);
438 assert_eq!(events[0], "created");
439 assert_eq!(events[1], "destroyed");
440 assert_eq!(events[2], "created");
441 assert_eq!(events[3], "destroyed");
442 }
443
444 #[test]
445 fn test_set_value_at_path_simple() {
446 let mut state = json!({
447 "count": 0
448 });
449
450 set_value_at_path(&mut state, "count", json!(42));
451 assert_eq!(state["count"], 42);
452 }
453
454 #[test]
455 fn test_set_value_at_path_nested() {
456 let mut state = json!({
457 "user": {
458 "name": "Alice",
459 "profile": {
460 "bio": "Developer"
461 }
462 }
463 });
464
465 set_value_at_path(&mut state, "user.profile.bio", json!("Engineer"));
466 assert_eq!(state["user"]["profile"]["bio"], "Engineer");
467 assert_eq!(state["user"]["name"], "Alice");
469 }
470
471 #[test]
472 fn test_set_value_at_path_creates_intermediate() {
473 let mut state = json!({});
474
475 set_value_at_path(&mut state, "user.profile.name", json!("Bob"));
476 assert_eq!(state["user"]["profile"]["name"], "Bob");
477 }
478
479 #[test]
480 fn test_set_value_at_path_array_index() {
481 let mut state = json!({
482 "items": ["a", "b", "c"]
483 });
484
485 set_value_at_path(&mut state, "items.1", json!("modified"));
486 assert_eq!(state["items"][1], "modified");
487 assert_eq!(state["items"][0], "a");
488 assert_eq!(state["items"][2], "c");
489 }
490
491 #[test]
492 fn test_update_state_sparse_single_path() {
493 let module = Module::new("TestModule");
494 let initial_state = json!({
495 "count": 0,
496 "name": "Alice"
497 });
498 let mut instance = ModuleInstance::new(module, initial_state);
499
500 let paths = vec!["count".to_string()];
501 let values = json!({
502 "count": 42
503 });
504
505 instance.update_state_sparse(&paths, &values);
506
507 assert_eq!(instance.get_state()["count"], 42);
508 assert_eq!(instance.get_state()["name"], "Alice"); }
510
511 #[test]
512 fn test_update_state_sparse_nested_path() {
513 let module = Module::new("TestModule");
514 let initial_state = json!({
515 "user": {
516 "name": "Alice",
517 "age": 30
518 },
519 "settings": {
520 "theme": "dark"
521 }
522 });
523 let mut instance = ModuleInstance::new(module, initial_state);
524
525 let paths = vec!["user.age".to_string()];
526 let values = json!({
527 "user.age": 31
528 });
529
530 instance.update_state_sparse(&paths, &values);
531
532 assert_eq!(instance.get_state()["user"]["age"], 31);
533 assert_eq!(instance.get_state()["user"]["name"], "Alice"); assert_eq!(instance.get_state()["settings"]["theme"], "dark"); }
536
537 #[test]
538 fn test_update_state_sparse_multiple_paths() {
539 let module = Module::new("TestModule");
540 let initial_state = json!({
541 "count": 0,
542 "user": {
543 "name": "Alice"
544 }
545 });
546 let mut instance = ModuleInstance::new(module, initial_state);
547
548 let paths = vec!["count".to_string(), "user.name".to_string()];
549 let values = json!({
550 "count": 100,
551 "user.name": "Bob"
552 });
553
554 instance.update_state_sparse(&paths, &values);
555
556 assert_eq!(instance.get_state()["count"], 100);
557 assert_eq!(instance.get_state()["user"]["name"], "Bob");
558 }
559
560 #[test]
563 fn test_set_value_at_path_empty_path() {
564 let mut state = json!({"count": 0});
565 set_value_at_path(&mut state, "", json!(42));
567 assert_eq!(state["count"], 0);
568 }
569
570 #[test]
571 fn test_set_value_at_path_null_value() {
572 let mut state = json!({
573 "user": {
574 "name": "Alice",
575 "email": "alice@example.com"
576 }
577 });
578
579 set_value_at_path(&mut state, "user.email", json!(null));
580 assert_eq!(state["user"]["email"], serde_json::Value::Null);
581 assert_eq!(state["user"]["name"], "Alice"); }
583
584 #[test]
585 fn test_set_value_at_path_type_change() {
586 let mut state = json!({
587 "data": "string value"
588 });
589
590 set_value_at_path(&mut state, "data", json!({"nested": true}));
592 assert_eq!(state["data"]["nested"], true);
593
594 set_value_at_path(&mut state, "data", json!([1, 2, 3]));
596 assert_eq!(state["data"][0], 1);
597
598 set_value_at_path(&mut state, "data", json!(42));
600 assert_eq!(state["data"], 42);
601 }
602
603 #[test]
604 fn test_set_value_at_path_deeply_nested() {
605 let mut state = json!({});
606
607 set_value_at_path(&mut state, "a.b.c.d.e.f", json!("deep value"));
609 assert_eq!(state["a"]["b"]["c"]["d"]["e"]["f"], "deep value");
610 }
611
612 #[test]
613 fn test_set_value_at_path_nested_array_object() {
614 let mut state = json!({
615 "users": [
616 {"name": "Alice", "tags": ["admin"]},
617 {"name": "Bob", "tags": ["user"]}
618 ]
619 });
620
621 set_value_at_path(&mut state, "users.1.name", json!("Robert"));
623 assert_eq!(state["users"][1]["name"], "Robert");
624 assert_eq!(state["users"][0]["name"], "Alice"); set_value_at_path(&mut state, "users.0.tags.0", json!("superadmin"));
628 assert_eq!(state["users"][0]["tags"][0], "superadmin");
629 }
630
631 #[test]
632 fn test_set_value_at_path_extend_array() {
633 let mut state = json!({
634 "items": ["a", "b"]
635 });
636
637 set_value_at_path(&mut state, "items.5", json!("extended"));
639 assert_eq!(state["items"].as_array().unwrap().len(), 6);
640 assert_eq!(state["items"][5], "extended");
641 assert_eq!(state["items"][2], serde_json::Value::Null);
642 assert_eq!(state["items"][3], serde_json::Value::Null);
643 assert_eq!(state["items"][4], serde_json::Value::Null);
644 }
645
646 #[test]
647 fn test_set_value_at_path_overwrite_primitive_with_nested() {
648 let mut state = json!({
649 "config": 42
650 });
651
652 set_value_at_path(&mut state, "config.nested.value", json!("test"));
655 assert_eq!(state["config"]["nested"]["value"], "test");
656 }
657
658 #[test]
659 fn test_set_value_at_path_boolean_values() {
660 let mut state = json!({
661 "flags": {
662 "enabled": true,
663 "visible": false
664 }
665 });
666
667 set_value_at_path(&mut state, "flags.enabled", json!(false));
668 set_value_at_path(&mut state, "flags.visible", json!(true));
669 assert_eq!(state["flags"]["enabled"], false);
670 assert_eq!(state["flags"]["visible"], true);
671 }
672
673 #[test]
674 fn test_set_value_at_path_float_values() {
675 let mut state = json!({
676 "coordinates": {
677 "lat": 0.0,
678 "lng": 0.0
679 }
680 });
681
682 set_value_at_path(&mut state, "coordinates.lat", json!(37.7749));
683 set_value_at_path(&mut state, "coordinates.lng", json!(-122.4194));
684
685 let lat = state["coordinates"]["lat"].as_f64().unwrap();
687 let lng = state["coordinates"]["lng"].as_f64().unwrap();
688 assert!((lat - 37.7749).abs() < 0.0001);
689 assert!((lng - (-122.4194)).abs() < 0.0001);
690 }
691
692 #[test]
693 fn test_update_state_sparse_empty_paths() {
694 let module = Module::new("TestModule");
695 let initial_state = json!({
696 "count": 0
697 });
698 let mut instance = ModuleInstance::new(module, initial_state);
699
700 let paths: Vec<String> = vec![];
701 let values = json!({});
702
703 instance.update_state_sparse(&paths, &values);
705 assert_eq!(instance.get_state()["count"], 0);
706 }
707
708 #[test]
709 fn test_update_state_sparse_path_not_in_values() {
710 let module = Module::new("TestModule");
711 let initial_state = json!({
712 "count": 0,
713 "name": "Alice"
714 });
715 let mut instance = ModuleInstance::new(module, initial_state);
716
717 let paths = vec!["count".to_string(), "missing".to_string()];
719 let values = json!({
720 "count": 42
721 });
723
724 instance.update_state_sparse(&paths, &values);
725 assert_eq!(instance.get_state()["count"], 42);
726 assert_eq!(instance.get_state()["name"], "Alice");
727 }
728
729 #[test]
730 fn test_update_state_sparse_invalid_values_type() {
731 let module = Module::new("TestModule");
732 let initial_state = json!({
733 "count": 0
734 });
735 let mut instance = ModuleInstance::new(module, initial_state);
736
737 let paths = vec!["count".to_string()];
739 let values = json!("not an object");
740
741 instance.update_state_sparse(&paths, &values);
742 assert_eq!(instance.get_state()["count"], 0);
744 }
745
746 #[test]
747 fn test_update_state_sparse_array_values() {
748 let module = Module::new("TestModule");
749 let initial_state = json!({
750 "items": []
751 });
752 let mut instance = ModuleInstance::new(module, initial_state);
753
754 let paths = vec!["items".to_string()];
755 let values = json!({
756 "items": ["a", "b", "c"]
757 });
758
759 instance.update_state_sparse(&paths, &values);
760 assert_eq!(instance.get_state()["items"], json!(["a", "b", "c"]));
761 }
762
763 #[test]
764 fn test_update_state_sparse_complex_nested_update() {
765 let module = Module::new("TestModule");
766 let initial_state = json!({
767 "app": {
768 "ui": {
769 "theme": "light",
770 "sidebar": {
771 "collapsed": false,
772 "width": 250
773 }
774 },
775 "data": {
776 "users": [],
777 "cache": {}
778 }
779 }
780 });
781 let mut instance = ModuleInstance::new(module, initial_state);
782
783 let paths = vec![
784 "app.ui.theme".to_string(),
785 "app.ui.sidebar.collapsed".to_string(),
786 "app.data.users".to_string(),
787 ];
788 let values = json!({
789 "app.ui.theme": "dark",
790 "app.ui.sidebar.collapsed": true,
791 "app.data.users": [{"id": 1, "name": "Alice"}]
792 });
793
794 instance.update_state_sparse(&paths, &values);
795
796 assert_eq!(instance.get_state()["app"]["ui"]["theme"], "dark");
797 assert_eq!(
798 instance.get_state()["app"]["ui"]["sidebar"]["collapsed"],
799 true
800 );
801 assert_eq!(instance.get_state()["app"]["ui"]["sidebar"]["width"], 250); assert_eq!(
803 instance.get_state()["app"]["data"]["users"][0]["name"],
804 "Alice"
805 );
806 assert!(instance.get_state()["app"]["data"]["cache"].is_object()); }
808
809 #[test]
810 fn test_update_state_sparse_preserves_sibling_keys() {
811 let module = Module::new("TestModule");
812 let initial_state = json!({
813 "user": {
814 "name": "Alice",
815 "email": "alice@example.com",
816 "profile": {
817 "bio": "Developer",
818 "avatar": "alice.png",
819 "social": {
820 "twitter": "@alice",
821 "github": "alice"
822 }
823 }
824 }
825 });
826 let mut instance = ModuleInstance::new(module, initial_state);
827
828 let paths = vec!["user.profile.social.twitter".to_string()];
830 let values = json!({
831 "user.profile.social.twitter": "@alice_new"
832 });
833
834 instance.update_state_sparse(&paths, &values);
835
836 assert_eq!(
838 instance.get_state()["user"]["profile"]["social"]["twitter"],
839 "@alice_new"
840 );
841
842 assert_eq!(instance.get_state()["user"]["name"], "Alice");
844 assert_eq!(instance.get_state()["user"]["email"], "alice@example.com");
845 assert_eq!(instance.get_state()["user"]["profile"]["bio"], "Developer");
846 assert_eq!(
847 instance.get_state()["user"]["profile"]["avatar"],
848 "alice.png"
849 );
850 assert_eq!(
851 instance.get_state()["user"]["profile"]["social"]["github"],
852 "alice"
853 );
854 }
855}