1use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
13pub struct PrinterConfig {
14 pub last_printer_id: Option<String>,
16 pub last_backend: Option<String>,
18 pub global_settings: HashMap<String, String>,
20 pub printer_settings: HashMap<String, HashMap<String, String>>,
22}
23
24impl PrinterConfig {
25 pub fn new() -> Self {
27 Self::default()
28 }
29
30 pub fn has_printer(&self) -> bool {
32 self.last_printer_id.is_some()
33 }
34
35 pub fn set_last_printer(&mut self, printer_id: &str, backend: &str) {
37 self.last_printer_id = Some(printer_id.to_string());
38 self.last_backend = Some(backend.to_string());
39 }
40
41 pub fn get_global_setting(&self, key: &str) -> Option<&str> {
43 self.global_settings.get(key).map(|s| s.as_str())
44 }
45
46 pub fn set_global_setting(&mut self, key: String, value: String) {
48 self.global_settings.insert(key, value);
49 }
50
51 pub fn get_settings_for(&self, printer_id: &str, backend: &str) -> HashMap<String, String> {
56 let mut merged = self.global_settings.clone();
57 let key = format!("{}@{}", printer_id, backend);
58 if let Some(specific) = self.printer_settings.get(&key) {
59 for (k, v) in specific {
60 merged.insert(k.clone(), v.clone());
61 }
62 }
63 merged
64 }
65
66 pub fn set_settings_for(
68 &mut self,
69 printer_id: &str,
70 backend: &str,
71 settings: HashMap<String, String>,
72 ) {
73 let key = format!("{}@{}", printer_id, backend);
74 self.printer_settings.insert(key, settings);
75 }
76
77 pub fn set_setting_for(&mut self, printer_id: &str, backend: &str, key: String, value: String) {
79 let p_key = format!("{}@{}", printer_id, backend);
80 self.printer_settings
81 .entry(p_key)
82 .or_default()
83 .insert(key, value);
84 }
85
86 pub fn get_setting_for(&self, printer_id: &str, backend: &str, key: &str) -> Option<&str> {
88 let p_key = format!("{}@{}", printer_id, backend);
89 if let Some(val) = self.printer_settings.get(&p_key).and_then(|m| m.get(key)) {
90 Some(val.as_str())
91 } else {
92 self.get_global_setting(key)
93 }
94 }
95
96 pub fn remove_settings_for(&mut self, printer_id: &str, backend: &str) {
98 let key = format!("{}@{}", printer_id, backend);
99 self.printer_settings.remove(&key);
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn default_config_is_empty() {
109 let config = PrinterConfig::default();
110 assert!(config.last_printer_id.is_none());
111 assert!(config.last_backend.is_none());
112 assert!(config.global_settings.is_empty());
113 assert!(config.printer_settings.is_empty());
114 assert!(!config.has_printer());
115 }
116
117 #[test]
118 fn set_last_printer_works() {
119 let mut config = PrinterConfig::new();
120 config.set_last_printer("HP-123", "CUPS");
121 assert_eq!(config.last_printer_id.as_deref(), Some("HP-123"));
122 assert_eq!(config.last_backend.as_deref(), Some("CUPS"));
123 assert!(config.has_printer());
124 }
125
126 #[test]
127 fn global_settings_store_and_fallback() {
128 let mut config = PrinterConfig::new();
129 config.set_global_setting("copies".to_string(), "2".to_string());
130 assert_eq!(config.get_global_setting("copies"), Some("2"));
131 assert_eq!(config.get_global_setting("nonexistent"), None);
132
133 assert_eq!(
135 config.get_setting_for("HP-123", "CUPS", "copies"),
136 Some("2")
137 );
138 }
139
140 #[test]
141 fn printer_specific_overrides() {
142 let mut config = PrinterConfig::new();
143 config.set_global_setting("copies".to_string(), "1".to_string());
144 config.set_global_setting("media".to_string(), "Letter".to_string());
145
146 let mut hp_settings = HashMap::new();
147 hp_settings.insert("media".to_string(), "A4".to_string());
148 config.set_settings_for("HP-123", "CUPS", hp_settings);
149
150 assert_eq!(
152 config.get_setting_for("HP-123", "CUPS", "media"),
153 Some("A4")
154 );
155 assert_eq!(
156 config.get_setting_for("HP-123", "CUPS", "copies"),
157 Some("1")
158 );
159
160 config.set_setting_for(
162 "HP-123",
163 "CUPS",
164 "sides".to_string(),
165 "two-sided-long-edge".to_string(),
166 );
167 assert_eq!(
168 config.get_setting_for("HP-123", "CUPS", "sides"),
169 Some("two-sided-long-edge")
170 );
171
172 let merged = config.get_settings_for("HP-123", "CUPS");
174 assert_eq!(merged.get("media").map(|s| s.as_str()), Some("A4"));
175 assert_eq!(merged.get("copies").map(|s| s.as_str()), Some("1"));
176 assert_eq!(
177 merged.get("sides").map(|s| s.as_str()),
178 Some("two-sided-long-edge")
179 );
180
181 config.remove_settings_for("HP-123", "CUPS");
183 assert_eq!(
184 config.get_setting_for("HP-123", "CUPS", "media"),
185 Some("Letter")
186 );
187 assert_eq!(config.get_setting_for("HP-123", "CUPS", "sides"), None);
188 }
189
190 #[test]
191 fn serde_roundtrip_json() {
192 let mut config = PrinterConfig::new();
193 config.set_last_printer("Epson", "CUPS");
194 config.set_global_setting("copies".to_string(), "3".to_string());
195
196 let mut ep_settings = HashMap::new();
197 ep_settings.insert("media".to_string(), "A4".to_string());
198 config.set_settings_for("Epson", "CUPS", ep_settings);
199
200 let json = serde_json::to_string(&config).unwrap();
201 let loaded: PrinterConfig = serde_json::from_str(&json).unwrap();
202 assert_eq!(config, loaded);
203 }
204}