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