1#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct OptionInfo {
9 pub name: String,
11 pub default_value: String,
13 pub group: String,
15 pub supported_values: Vec<String>,
17}
18
19#[derive(Debug, Clone, Default)]
41pub struct OptionsCollection {
42 pub options: Vec<OptionInfo>,
45}
46
47impl OptionsCollection {
48 #[cfg(feature = "zbus-backend")]
51 pub fn from_dbus(raw: Vec<crate::proxy::RawOption>) -> Self {
52 let options = raw
53 .into_iter()
54 .map(|r| OptionInfo {
55 name: r.option_name,
56 group: r.group_name,
57 default_value: r.default_value,
58 supported_values: r.supported_values.into_iter().map(|(s,)| s).collect(),
59 })
60 .collect();
61 Self { options }
62 }
63
64 #[inline]
66 pub fn len(&self) -> usize {
67 self.options.len()
68 }
69
70 #[inline]
72 pub fn is_empty(&self) -> bool {
73 self.options.is_empty()
74 }
75
76 pub fn get(&self, name: &str) -> Option<&OptionInfo> {
78 self.options.iter().find(|o| o.name == name)
79 }
80
81 pub fn iter(&self) -> impl Iterator<Item = &OptionInfo> {
83 self.options.iter()
84 }
85
86 pub fn groups(&self) -> Vec<&str> {
89 let mut seen = std::collections::HashSet::new();
90 let mut result = Vec::new();
91 for opt in &self.options {
92 if !opt.group.is_empty() && seen.insert(opt.group.as_str()) {
93 result.push(opt.group.as_str());
94 }
95 }
96 result
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn empty_collection_helpers() {
106 let col = OptionsCollection::default();
107 assert!(col.is_empty());
108 assert_eq!(col.len(), 0);
109 assert!(col.get("copies").is_none());
110 assert!(col.groups().is_empty());
111 }
112
113 #[test]
114 fn collection_get_finds_by_name() {
115 let col = OptionsCollection {
116 options: vec![
117 OptionInfo {
118 name: "copies".to_string(),
119 default_value: "1".to_string(),
120 group: "General".to_string(),
121 supported_values: vec!["1".to_string(), "2".to_string()],
122 },
123 OptionInfo {
124 name: "sides".to_string(),
125 default_value: "one-sided".to_string(),
126 group: "General".to_string(),
127 supported_values: vec![
128 "one-sided".to_string(),
129 "two-sided-long-edge".to_string(),
130 ],
131 },
132 ],
133 };
134 let found = col.get("sides");
135 assert_eq!(found.unwrap().default_value, "one-sided");
136 assert!(col.get("nonexistent").is_none());
137 }
138
139 #[test]
140 fn collection_len_and_iter() {
141 let col = OptionsCollection {
142 options: vec![
143 OptionInfo {
144 name: "a".into(),
145 default_value: String::new(),
146 group: String::new(),
147 supported_values: vec![],
148 },
149 OptionInfo {
150 name: "b".into(),
151 default_value: String::new(),
152 group: String::new(),
153 supported_values: vec![],
154 },
155 ],
156 };
157 assert_eq!(col.len(), 2);
158 assert_eq!(col.iter().count(), 2);
159 }
160
161 #[test]
162 fn collection_groups() {
163 let col = OptionsCollection {
164 options: vec![
165 OptionInfo {
166 name: "copies".to_string(),
167 default_value: "1".to_string(),
168 group: "General".to_string(),
169 supported_values: vec![],
170 },
171 OptionInfo {
172 name: "media".to_string(),
173 default_value: "A4".to_string(),
174 group: "Page Setup".to_string(),
175 supported_values: vec![],
176 },
177 OptionInfo {
178 name: "sides".to_string(),
179 default_value: "one-sided".to_string(),
180 group: "General".to_string(),
181 supported_values: vec![],
182 },
183 OptionInfo {
184 name: "fit-to-page".to_string(),
185 default_value: "false".to_string(),
186 group: "".to_string(),
187 supported_values: vec![],
188 },
189 ],
190 };
191 assert_eq!(col.groups(), vec!["General", "Page Setup"]);
192 }
193}