1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize, Default)]
10pub struct IntentSlotTemplate {
11 #[serde(default)]
13 pub slots: Vec<String>,
14
15 #[serde(default)]
17 pub layout: Option<String>,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize, Default)]
22pub struct IntentModeTemplates {
23 #[serde(default)]
25 pub display: IntentSlotTemplate,
26
27 #[serde(default)]
29 pub input: IntentSlotTemplate,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, Default)]
47pub struct ThemeTemplates {
48 #[serde(default)]
50 pub browse: Option<IntentModeTemplates>,
51
52 #[serde(default)]
54 pub focus: Option<IntentModeTemplates>,
55
56 #[serde(default)]
58 pub collect: Option<IntentModeTemplates>,
59
60 #[serde(default)]
62 pub process: Option<IntentModeTemplates>,
63
64 #[serde(default)]
66 pub summarize: Option<IntentModeTemplates>,
67
68 #[serde(default)]
70 pub analyze: Option<IntentModeTemplates>,
71
72 #[serde(default)]
74 pub track: Option<IntentModeTemplates>,
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn intent_slot_template_default_has_empty_slots_and_none_layout() {
83 let t = IntentSlotTemplate::default();
84 assert!(t.slots.is_empty());
85 assert!(t.layout.is_none());
86 }
87
88 #[test]
89 fn intent_mode_templates_default_has_empty_display_and_input() {
90 let t = IntentModeTemplates::default();
91 assert!(t.display.slots.is_empty());
92 assert!(t.display.layout.is_none());
93 assert!(t.input.slots.is_empty());
94 assert!(t.input.layout.is_none());
95 }
96
97 #[test]
98 fn theme_templates_deserializes_empty_json_to_all_none() {
99 let t: ThemeTemplates = serde_json::from_str("{}").unwrap();
100 assert!(t.browse.is_none());
101 assert!(t.focus.is_none());
102 assert!(t.collect.is_none());
103 assert!(t.process.is_none());
104 assert!(t.summarize.is_none());
105 assert!(t.analyze.is_none());
106 assert!(t.track.is_none());
107 }
108
109 #[test]
110 fn theme_templates_deserializes_partial_json_with_other_fields_none() {
111 let json = r#"{"browse": {"display": {"slots": ["title", "fields"]}}}"#;
112 let t: ThemeTemplates = serde_json::from_str(json).unwrap();
113 assert!(t.browse.is_some());
114 let browse = t.browse.unwrap();
115 assert_eq!(browse.display.slots, vec!["title", "fields"]);
116 assert!(browse.display.layout.is_none());
117 assert!(t.focus.is_none());
118 assert!(t.collect.is_none());
119 assert!(t.process.is_none());
120 assert!(t.summarize.is_none());
121 assert!(t.analyze.is_none());
122 assert!(t.track.is_none());
123 }
124
125 #[test]
126 fn theme_templates_deserializes_full_json_with_all_7_intents() {
127 let json = r#"{
128 "browse": {"display": {"slots": ["title"], "layout": "Table"}, "input": {"slots": ["fields"]}},
129 "focus": {"display": {"slots": ["title", "body"]}},
130 "collect": {"input": {"slots": ["fields", "actions"], "layout": "Form"}},
131 "process": {"display": {"slots": ["title", "actions"]}},
132 "summarize": {"display": {"slots": ["stats", "metadata"]}},
133 "analyze": {"display": {"slots": ["fields", "stats"]}},
134 "track": {"display": {"slots": ["title", "metadata", "pagination"]}}
135 }"#;
136 let t: ThemeTemplates = serde_json::from_str(json).unwrap();
137 assert!(t.browse.is_some());
138 assert!(t.focus.is_some());
139 assert!(t.collect.is_some());
140 assert!(t.process.is_some());
141 assert!(t.summarize.is_some());
142 assert!(t.analyze.is_some());
143 assert!(t.track.is_some());
144 let browse = t.browse.unwrap();
145 assert_eq!(browse.display.layout, Some("Table".to_string()));
146 assert_eq!(browse.display.slots, vec!["title"]);
147 assert_eq!(browse.input.slots, vec!["fields"]);
148 }
149
150 #[test]
151 fn theme_templates_serde_round_trip_preserves_all_fields() {
152 let original = ThemeTemplates {
153 browse: Some(IntentModeTemplates {
154 display: IntentSlotTemplate {
155 slots: vec!["title".to_string(), "fields".to_string()],
156 layout: Some("Table".to_string()),
157 },
158 input: IntentSlotTemplate::default(),
159 }),
160 focus: Some(IntentModeTemplates::default()),
161 collect: None,
162 process: None,
163 summarize: None,
164 analyze: None,
165 track: None,
166 };
167 let json = serde_json::to_string(&original).unwrap();
168 let restored: ThemeTemplates = serde_json::from_str(&json).unwrap();
169 assert!(restored.browse.is_some());
170 let browse = restored.browse.unwrap();
171 assert_eq!(browse.display.slots, vec!["title", "fields"]);
172 assert_eq!(browse.display.layout, Some("Table".to_string()));
173 assert!(restored.focus.is_some());
174 assert!(restored.collect.is_none());
175 }
176}