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 from_config(
140 name: &str,
141 actions: Vec<String>,
142 state_keys: Vec<String>,
143 initial_state: serde_json::Value,
144 ) -> Self {
145 let module = Module::new(name)
146 .with_actions(actions)
147 .with_state_keys(state_keys);
148 Self::new(module, initial_state)
149 }
150
151 pub fn set_on_created<F>(&mut self, callback: F)
153 where
154 F: Fn() + Send + Sync + 'static,
155 {
156 self.on_created = Some(Box::new(callback));
157 }
158
159 pub fn set_on_destroyed<F>(&mut self, callback: F)
161 where
162 F: Fn() + Send + Sync + 'static,
163 {
164 self.on_destroyed = Some(Box::new(callback));
165 }
166
167 pub fn mount(&mut self) {
169 if !self.mounted {
170 self.mounted = true;
171 if let Some(ref callback) = self.on_created {
173 callback();
174 }
175 }
176 }
177
178 pub fn unmount(&mut self) {
180 if self.mounted {
181 if let Some(ref callback) = self.on_destroyed {
183 callback();
184 }
185 self.mounted = false;
186 }
187 }
188
189 pub fn update_state(&mut self, patch: serde_json::Value) {
194 let state = Arc::make_mut(&mut self.state);
196 merge_json(state, patch);
197 }
198
199 pub fn update_state_sparse(&mut self, paths: &[String], values: &serde_json::Value) {
204 if let serde_json::Value::Object(map) = values {
206 if paths.iter().any(|p| map.contains_key(p)) {
208 let state = Arc::make_mut(&mut self.state);
209 for path in paths {
210 if let Some(new_value) = map.get(path) {
211 set_value_at_path(state, path, new_value.clone());
212 }
213 }
214 }
215 }
216 }
217
218 pub fn get_state(&self) -> &serde_json::Value {
223 &self.state
224 }
225
226 pub fn get_state_shared(&self) -> Arc<serde_json::Value> {
231 Arc::clone(&self.state)
232 }
233}
234
235fn merge_json(target: &mut serde_json::Value, source: serde_json::Value) {
237 use serde_json::Value;
238
239 match (target, source) {
240 (Value::Object(target_map), Value::Object(source_map)) => {
241 for (key, value) in source_map {
242 if let Some(target_value) = target_map.get_mut(&key) {
243 merge_json(target_value, value);
244 } else {
245 target_map.insert(key, value);
246 }
247 }
248 }
249 (target, source) => {
250 *target = source;
251 }
252 }
253}
254
255fn set_value_at_path(target: &mut serde_json::Value, path: &str, value: serde_json::Value) {
258 use serde_json::Value;
259
260 let parts: Vec<&str> = path.split('.').collect();
261 if parts.is_empty() {
262 return;
263 }
264
265 let mut current = target;
266
267 for part in &parts[..parts.len() - 1] {
269 if let Ok(index) = part.parse::<usize>() {
271 if let Value::Array(arr) = current {
272 while arr.len() <= index {
274 arr.push(Value::Null);
275 }
276 current = &mut arr[index];
277 continue;
278 }
279 }
280
281 if !current.is_object() {
283 *current = Value::Object(serde_json::Map::new());
284 }
285
286 if let Value::Object(map) = current {
287 if !map.contains_key(*part) {
288 map.insert(part.to_string(), Value::Object(serde_json::Map::new()));
289 }
290 current = map.get_mut(*part).unwrap();
291 }
292 }
293
294 let final_key = parts[parts.len() - 1];
296
297 if let Ok(index) = final_key.parse::<usize>() {
299 if let Value::Array(arr) = current {
300 while arr.len() <= index {
301 arr.push(Value::Null);
302 }
303 arr[index] = value;
304 return;
305 }
306 }
307
308 if !current.is_object() {
310 *current = Value::Object(serde_json::Map::new());
311 }
312
313 if let Value::Object(map) = current {
314 map.insert(final_key.to_string(), value);
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321 use serde_json::json;
322
323 #[test]
324 fn test_merge_json() {
325 let mut target = json!({
326 "user": {
327 "name": "Alice",
328 "age": 30
329 }
330 });
331
332 let patch = json!({
333 "user": {
334 "age": 31
335 }
336 });
337
338 merge_json(&mut target, patch);
339
340 assert_eq!(target["user"]["name"], "Alice");
341 assert_eq!(target["user"]["age"], 31);
342 }
343
344 #[test]
345 fn test_lifecycle_on_created_callback() {
346 use std::sync::{Arc, Mutex};
347
348 let module = Module::new("TestModule");
349 let initial_state = json!({});
350 let mut instance = ModuleInstance::new(module, initial_state);
351
352 let called = Arc::new(Mutex::new(false));
354 let called_clone = called.clone();
355
356 instance.set_on_created(move || {
357 *called_clone.lock().unwrap() = true;
358 });
359
360 assert!(!*called.lock().unwrap());
362 instance.mount();
363 assert!(*called.lock().unwrap());
364 assert!(instance.mounted);
365 }
366
367 #[test]
368 fn test_lifecycle_on_destroyed_callback() {
369 use std::sync::{Arc, Mutex};
370
371 let module = Module::new("TestModule");
372 let initial_state = json!({});
373 let mut instance = ModuleInstance::new(module, initial_state);
374
375 let called = Arc::new(Mutex::new(false));
376 let called_clone = called.clone();
377
378 instance.set_on_destroyed(move || {
379 *called_clone.lock().unwrap() = true;
380 });
381
382 instance.mount();
384 assert!(instance.mounted);
385
386 assert!(!*called.lock().unwrap());
388 instance.unmount();
389 assert!(*called.lock().unwrap());
390 assert!(!instance.mounted);
391 }
392
393 #[test]
394 fn test_lifecycle_callbacks_not_called_when_not_set() {
395 let module = Module::new("TestModule");
396 let initial_state = json!({});
397 let mut instance = ModuleInstance::new(module, initial_state);
398
399 instance.mount();
401 assert!(instance.mounted);
402
403 instance.unmount();
404 assert!(!instance.mounted);
405 }
406
407 #[test]
408 fn test_lifecycle_mount_idempotent() {
409 use std::sync::{Arc, Mutex};
410
411 let module = Module::new("TestModule");
412 let initial_state = json!({});
413 let mut instance = ModuleInstance::new(module, initial_state);
414
415 let call_count = Arc::new(Mutex::new(0));
416 let call_count_clone = call_count.clone();
417
418 instance.set_on_created(move || {
419 *call_count_clone.lock().unwrap() += 1;
420 });
421
422 instance.mount();
424 assert_eq!(*call_count.lock().unwrap(), 1);
425
426 instance.mount();
428 assert_eq!(*call_count.lock().unwrap(), 1);
429 }
430
431 #[test]
432 fn test_lifecycle_unmount_idempotent() {
433 use std::sync::{Arc, Mutex};
434
435 let module = Module::new("TestModule");
436 let initial_state = json!({});
437 let mut instance = ModuleInstance::new(module, initial_state);
438
439 let call_count = Arc::new(Mutex::new(0));
440 let call_count_clone = call_count.clone();
441
442 instance.set_on_destroyed(move || {
443 *call_count_clone.lock().unwrap() += 1;
444 });
445
446 instance.mount();
448
449 instance.unmount();
451 assert_eq!(*call_count.lock().unwrap(), 1);
452
453 instance.unmount();
455 assert_eq!(*call_count.lock().unwrap(), 1);
456 }
457
458 #[test]
459 fn test_lifecycle_full_cycle() {
460 use std::sync::{Arc, Mutex};
461
462 let module = Module::new("TestModule");
463 let initial_state = json!({});
464 let mut instance = ModuleInstance::new(module, initial_state);
465
466 let events = Arc::new(Mutex::new(Vec::new()));
467 let events_created = events.clone();
468 let events_destroyed = events.clone();
469
470 instance.set_on_created(move || {
471 events_created.lock().unwrap().push("created");
472 });
473
474 instance.set_on_destroyed(move || {
475 events_destroyed.lock().unwrap().push("destroyed");
476 });
477
478 instance.mount();
480 instance.unmount();
481 instance.mount();
482 instance.unmount();
483
484 let events = events.lock().unwrap();
485 assert_eq!(events.len(), 4);
486 assert_eq!(events[0], "created");
487 assert_eq!(events[1], "destroyed");
488 assert_eq!(events[2], "created");
489 assert_eq!(events[3], "destroyed");
490 }
491
492 #[test]
493 fn test_set_value_at_path_simple() {
494 let mut state = json!({
495 "count": 0
496 });
497
498 set_value_at_path(&mut state, "count", json!(42));
499 assert_eq!(state["count"], 42);
500 }
501
502 #[test]
503 fn test_set_value_at_path_nested() {
504 let mut state = json!({
505 "user": {
506 "name": "Alice",
507 "profile": {
508 "bio": "Developer"
509 }
510 }
511 });
512
513 set_value_at_path(&mut state, "user.profile.bio", json!("Engineer"));
514 assert_eq!(state["user"]["profile"]["bio"], "Engineer");
515 assert_eq!(state["user"]["name"], "Alice");
517 }
518
519 #[test]
520 fn test_set_value_at_path_creates_intermediate() {
521 let mut state = json!({});
522
523 set_value_at_path(&mut state, "user.profile.name", json!("Bob"));
524 assert_eq!(state["user"]["profile"]["name"], "Bob");
525 }
526
527 #[test]
528 fn test_set_value_at_path_array_index() {
529 let mut state = json!({
530 "items": ["a", "b", "c"]
531 });
532
533 set_value_at_path(&mut state, "items.1", json!("modified"));
534 assert_eq!(state["items"][1], "modified");
535 assert_eq!(state["items"][0], "a");
536 assert_eq!(state["items"][2], "c");
537 }
538
539 #[test]
540 fn test_update_state_sparse_single_path() {
541 let module = Module::new("TestModule");
542 let initial_state = json!({
543 "count": 0,
544 "name": "Alice"
545 });
546 let mut instance = ModuleInstance::new(module, initial_state);
547
548 let paths = vec!["count".to_string()];
549 let values = json!({
550 "count": 42
551 });
552
553 instance.update_state_sparse(&paths, &values);
554
555 assert_eq!(instance.get_state()["count"], 42);
556 assert_eq!(instance.get_state()["name"], "Alice"); }
558
559 #[test]
560 fn test_update_state_sparse_nested_path() {
561 let module = Module::new("TestModule");
562 let initial_state = json!({
563 "user": {
564 "name": "Alice",
565 "age": 30
566 },
567 "settings": {
568 "theme": "dark"
569 }
570 });
571 let mut instance = ModuleInstance::new(module, initial_state);
572
573 let paths = vec!["user.age".to_string()];
574 let values = json!({
575 "user.age": 31
576 });
577
578 instance.update_state_sparse(&paths, &values);
579
580 assert_eq!(instance.get_state()["user"]["age"], 31);
581 assert_eq!(instance.get_state()["user"]["name"], "Alice"); assert_eq!(instance.get_state()["settings"]["theme"], "dark"); }
584
585 #[test]
586 fn test_update_state_sparse_multiple_paths() {
587 let module = Module::new("TestModule");
588 let initial_state = json!({
589 "count": 0,
590 "user": {
591 "name": "Alice"
592 }
593 });
594 let mut instance = ModuleInstance::new(module, initial_state);
595
596 let paths = vec!["count".to_string(), "user.name".to_string()];
597 let values = json!({
598 "count": 100,
599 "user.name": "Bob"
600 });
601
602 instance.update_state_sparse(&paths, &values);
603
604 assert_eq!(instance.get_state()["count"], 100);
605 assert_eq!(instance.get_state()["user"]["name"], "Bob");
606 }
607
608 #[test]
611 fn test_set_value_at_path_empty_path() {
612 let mut state = json!({"count": 0});
613 set_value_at_path(&mut state, "", json!(42));
615 assert_eq!(state["count"], 0);
616 }
617
618 #[test]
619 fn test_set_value_at_path_null_value() {
620 let mut state = json!({
621 "user": {
622 "name": "Alice",
623 "email": "alice@example.com"
624 }
625 });
626
627 set_value_at_path(&mut state, "user.email", json!(null));
628 assert_eq!(state["user"]["email"], serde_json::Value::Null);
629 assert_eq!(state["user"]["name"], "Alice"); }
631
632 #[test]
633 fn test_set_value_at_path_type_change() {
634 let mut state = json!({
635 "data": "string value"
636 });
637
638 set_value_at_path(&mut state, "data", json!({"nested": true}));
640 assert_eq!(state["data"]["nested"], true);
641
642 set_value_at_path(&mut state, "data", json!([1, 2, 3]));
644 assert_eq!(state["data"][0], 1);
645
646 set_value_at_path(&mut state, "data", json!(42));
648 assert_eq!(state["data"], 42);
649 }
650
651 #[test]
652 fn test_set_value_at_path_deeply_nested() {
653 let mut state = json!({});
654
655 set_value_at_path(&mut state, "a.b.c.d.e.f", json!("deep value"));
657 assert_eq!(state["a"]["b"]["c"]["d"]["e"]["f"], "deep value");
658 }
659
660 #[test]
661 fn test_set_value_at_path_nested_array_object() {
662 let mut state = json!({
663 "users": [
664 {"name": "Alice", "tags": ["admin"]},
665 {"name": "Bob", "tags": ["user"]}
666 ]
667 });
668
669 set_value_at_path(&mut state, "users.1.name", json!("Robert"));
671 assert_eq!(state["users"][1]["name"], "Robert");
672 assert_eq!(state["users"][0]["name"], "Alice"); set_value_at_path(&mut state, "users.0.tags.0", json!("superadmin"));
676 assert_eq!(state["users"][0]["tags"][0], "superadmin");
677 }
678
679 #[test]
680 fn test_set_value_at_path_extend_array() {
681 let mut state = json!({
682 "items": ["a", "b"]
683 });
684
685 set_value_at_path(&mut state, "items.5", json!("extended"));
687 assert_eq!(state["items"].as_array().unwrap().len(), 6);
688 assert_eq!(state["items"][5], "extended");
689 assert_eq!(state["items"][2], serde_json::Value::Null);
690 assert_eq!(state["items"][3], serde_json::Value::Null);
691 assert_eq!(state["items"][4], serde_json::Value::Null);
692 }
693
694 #[test]
695 fn test_set_value_at_path_overwrite_primitive_with_nested() {
696 let mut state = json!({
697 "config": 42
698 });
699
700 set_value_at_path(&mut state, "config.nested.value", json!("test"));
703 assert_eq!(state["config"]["nested"]["value"], "test");
704 }
705
706 #[test]
707 fn test_set_value_at_path_boolean_values() {
708 let mut state = json!({
709 "flags": {
710 "enabled": true,
711 "visible": false
712 }
713 });
714
715 set_value_at_path(&mut state, "flags.enabled", json!(false));
716 set_value_at_path(&mut state, "flags.visible", json!(true));
717 assert_eq!(state["flags"]["enabled"], false);
718 assert_eq!(state["flags"]["visible"], true);
719 }
720
721 #[test]
722 fn test_set_value_at_path_float_values() {
723 let mut state = json!({
724 "coordinates": {
725 "lat": 0.0,
726 "lng": 0.0
727 }
728 });
729
730 set_value_at_path(&mut state, "coordinates.lat", json!(37.7749));
731 set_value_at_path(&mut state, "coordinates.lng", json!(-122.4194));
732
733 let lat = state["coordinates"]["lat"].as_f64().unwrap();
735 let lng = state["coordinates"]["lng"].as_f64().unwrap();
736 assert!((lat - 37.7749).abs() < 0.0001);
737 assert!((lng - (-122.4194)).abs() < 0.0001);
738 }
739
740 #[test]
741 fn test_update_state_sparse_empty_paths() {
742 let module = Module::new("TestModule");
743 let initial_state = json!({
744 "count": 0
745 });
746 let mut instance = ModuleInstance::new(module, initial_state);
747
748 let paths: Vec<String> = vec![];
749 let values = json!({});
750
751 instance.update_state_sparse(&paths, &values);
753 assert_eq!(instance.get_state()["count"], 0);
754 }
755
756 #[test]
757 fn test_update_state_sparse_path_not_in_values() {
758 let module = Module::new("TestModule");
759 let initial_state = json!({
760 "count": 0,
761 "name": "Alice"
762 });
763 let mut instance = ModuleInstance::new(module, initial_state);
764
765 let paths = vec!["count".to_string(), "missing".to_string()];
767 let values = json!({
768 "count": 42
769 });
771
772 instance.update_state_sparse(&paths, &values);
773 assert_eq!(instance.get_state()["count"], 42);
774 assert_eq!(instance.get_state()["name"], "Alice");
775 }
776
777 #[test]
778 fn test_update_state_sparse_invalid_values_type() {
779 let module = Module::new("TestModule");
780 let initial_state = json!({
781 "count": 0
782 });
783 let mut instance = ModuleInstance::new(module, initial_state);
784
785 let paths = vec!["count".to_string()];
787 let values = json!("not an object");
788
789 instance.update_state_sparse(&paths, &values);
790 assert_eq!(instance.get_state()["count"], 0);
792 }
793
794 #[test]
795 fn test_update_state_sparse_array_values() {
796 let module = Module::new("TestModule");
797 let initial_state = json!({
798 "items": []
799 });
800 let mut instance = ModuleInstance::new(module, initial_state);
801
802 let paths = vec!["items".to_string()];
803 let values = json!({
804 "items": ["a", "b", "c"]
805 });
806
807 instance.update_state_sparse(&paths, &values);
808 assert_eq!(instance.get_state()["items"], json!(["a", "b", "c"]));
809 }
810
811 #[test]
812 fn test_update_state_sparse_complex_nested_update() {
813 let module = Module::new("TestModule");
814 let initial_state = json!({
815 "app": {
816 "ui": {
817 "theme": "light",
818 "sidebar": {
819 "collapsed": false,
820 "width": 250
821 }
822 },
823 "data": {
824 "users": [],
825 "cache": {}
826 }
827 }
828 });
829 let mut instance = ModuleInstance::new(module, initial_state);
830
831 let paths = vec![
832 "app.ui.theme".to_string(),
833 "app.ui.sidebar.collapsed".to_string(),
834 "app.data.users".to_string(),
835 ];
836 let values = json!({
837 "app.ui.theme": "dark",
838 "app.ui.sidebar.collapsed": true,
839 "app.data.users": [{"id": 1, "name": "Alice"}]
840 });
841
842 instance.update_state_sparse(&paths, &values);
843
844 assert_eq!(instance.get_state()["app"]["ui"]["theme"], "dark");
845 assert_eq!(
846 instance.get_state()["app"]["ui"]["sidebar"]["collapsed"],
847 true
848 );
849 assert_eq!(instance.get_state()["app"]["ui"]["sidebar"]["width"], 250); assert_eq!(
851 instance.get_state()["app"]["data"]["users"][0]["name"],
852 "Alice"
853 );
854 assert!(instance.get_state()["app"]["data"]["cache"].is_object()); }
856
857 #[test]
858 fn test_update_state_sparse_preserves_sibling_keys() {
859 let module = Module::new("TestModule");
860 let initial_state = json!({
861 "user": {
862 "name": "Alice",
863 "email": "alice@example.com",
864 "profile": {
865 "bio": "Developer",
866 "avatar": "alice.png",
867 "social": {
868 "twitter": "@alice",
869 "github": "alice"
870 }
871 }
872 }
873 });
874 let mut instance = ModuleInstance::new(module, initial_state);
875
876 let paths = vec!["user.profile.social.twitter".to_string()];
878 let values = json!({
879 "user.profile.social.twitter": "@alice_new"
880 });
881
882 instance.update_state_sparse(&paths, &values);
883
884 assert_eq!(
886 instance.get_state()["user"]["profile"]["social"]["twitter"],
887 "@alice_new"
888 );
889
890 assert_eq!(instance.get_state()["user"]["name"], "Alice");
892 assert_eq!(instance.get_state()["user"]["email"], "alice@example.com");
893 assert_eq!(instance.get_state()["user"]["profile"]["bio"], "Developer");
894 assert_eq!(
895 instance.get_state()["user"]["profile"]["avatar"],
896 "alice.png"
897 );
898 assert_eq!(
899 instance.get_state()["user"]["profile"]["social"]["github"],
900 "alice"
901 );
902 }
903}