Skip to main content

acp_utils/
config_meta.rs

1use serde::{Deserialize, Serialize};
2use utils::{ReasoningEffort, is_false};
3
4type Meta = serde_json::Map<String, serde_json::Value>;
5
6/// Meta for a top-level `SessionConfigOption` (e.g. the "model" config).
7#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
8pub struct ConfigOptionMeta {
9    #[serde(default, skip_serializing_if = "is_false")]
10    pub multi_select: bool,
11}
12
13/// Meta for an individual `SessionConfigSelectOption` (e.g. one model choice).
14#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
15pub struct SelectOptionMeta {
16    #[serde(default, skip_serializing_if = "Vec::is_empty")]
17    pub reasoning_levels: Vec<ReasoningEffort>,
18    #[serde(default, skip_serializing_if = "is_false")]
19    pub supports_image: bool,
20    #[serde(default, skip_serializing_if = "is_false")]
21    pub supports_audio: bool,
22}
23
24impl SelectOptionMeta {
25    pub fn supports_reasoning(&self) -> bool {
26        !self.reasoning_levels.is_empty()
27    }
28}
29
30impl ConfigOptionMeta {
31    pub fn into_meta(self) -> Option<Meta> {
32        if self == Self::default() {
33            return None;
34        }
35        match serde_json::to_value(self).expect("ConfigOptionMeta should serialize") {
36            serde_json::Value::Object(map) => Some(map),
37            _ => unreachable!(),
38        }
39    }
40
41    pub fn from_meta(meta: Option<&Meta>) -> Self {
42        meta.and_then(|m| serde_json::from_value(serde_json::Value::Object(m.clone())).ok()).unwrap_or_default()
43    }
44}
45
46impl SelectOptionMeta {
47    pub fn into_meta(self) -> Option<Meta> {
48        if self == Self::default() {
49            return None;
50        }
51        match serde_json::to_value(self).expect("SelectOptionMeta should serialize") {
52            serde_json::Value::Object(map) => Some(map),
53            _ => unreachable!(),
54        }
55    }
56
57    pub fn from_meta(meta: Option<&Meta>) -> Self {
58        meta.and_then(|m| serde_json::from_value(serde_json::Value::Object(m.clone())).ok()).unwrap_or_default()
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn config_option_meta_roundtrip() {
68        let original = ConfigOptionMeta { multi_select: true };
69        let meta = original.clone().into_meta();
70        assert!(meta.is_some());
71        let restored = ConfigOptionMeta::from_meta(meta.as_ref());
72        assert_eq!(restored, original);
73    }
74
75    #[test]
76    fn select_option_meta_roundtrip() {
77        let original = SelectOptionMeta {
78            reasoning_levels: vec![ReasoningEffort::Low, ReasoningEffort::Medium, ReasoningEffort::High],
79            supports_image: true,
80            supports_audio: false,
81        };
82        let meta = original.clone().into_meta();
83        assert!(meta.is_some());
84        let restored = SelectOptionMeta::from_meta(meta.as_ref());
85        assert_eq!(restored, original);
86    }
87
88    #[test]
89    fn default_produces_none() {
90        assert!(ConfigOptionMeta::default().into_meta().is_none());
91        assert!(SelectOptionMeta::default().into_meta().is_none());
92    }
93
94    #[test]
95    fn from_meta_none_returns_default() {
96        assert_eq!(ConfigOptionMeta::from_meta(None), ConfigOptionMeta::default());
97        assert_eq!(SelectOptionMeta::from_meta(None), SelectOptionMeta::default());
98    }
99
100    #[test]
101    fn unknown_keys_are_ignored() {
102        let mut map = serde_json::Map::new();
103        map.insert("multi_select".to_string(), serde_json::Value::Bool(true));
104        map.insert("unknown_field".to_string(), serde_json::Value::String("hello".to_string()));
105        let parsed = ConfigOptionMeta::from_meta(Some(&map));
106        assert_eq!(parsed, ConfigOptionMeta { multi_select: true });
107    }
108
109    #[test]
110    fn false_fields_omitted_from_serialized_output() {
111        let meta = ConfigOptionMeta { multi_select: false };
112        let value = serde_json::to_value(&meta).unwrap();
113        let obj = value.as_object().unwrap();
114        assert!(!obj.contains_key("multi_select"));
115
116        let meta = SelectOptionMeta::default();
117        let value = serde_json::to_value(&meta).unwrap();
118        let obj = value.as_object().unwrap();
119        assert!(!obj.contains_key("reasoning_levels"));
120        assert!(!obj.contains_key("supports_image"));
121        assert!(!obj.contains_key("supports_audio"));
122    }
123
124    #[test]
125    fn supports_reasoning_convenience() {
126        assert!(!SelectOptionMeta::default().supports_reasoning());
127        let meta = SelectOptionMeta {
128            reasoning_levels: vec![ReasoningEffort::Low, ReasoningEffort::High],
129            supports_image: false,
130            supports_audio: false,
131        };
132        assert!(meta.supports_reasoning());
133    }
134}