oxidize_pdf/actions/
launch_action.rs1use crate::objects::{Dictionary, Object};
4
5#[derive(Debug, Clone)]
7pub struct WindowsLaunchParams {
8 pub file_name: String,
10 pub default_directory: Option<String>,
12 pub operation: Option<String>,
14 pub parameters: Option<String>,
16}
17
18#[derive(Debug, Clone)]
20pub enum LaunchParameters {
21 Simple(String),
23 Windows(WindowsLaunchParams),
25}
26
27#[derive(Debug, Clone)]
29pub struct LaunchAction {
30 pub file: String,
32 pub parameters: Option<LaunchParameters>,
34 pub new_window: Option<bool>,
36}
37
38impl LaunchAction {
39 pub fn new(file: impl Into<String>) -> Self {
41 Self {
42 file: file.into(),
43 parameters: None,
44 new_window: None,
45 }
46 }
47
48 pub fn application(app: impl Into<String>) -> Self {
50 Self::new(app)
51 }
52
53 pub fn document(path: impl Into<String>) -> Self {
55 Self::new(path)
56 }
57
58 pub fn with_params(mut self, params: impl Into<String>) -> Self {
60 self.parameters = Some(LaunchParameters::Simple(params.into()));
61 self
62 }
63
64 pub fn with_windows_params(mut self, params: WindowsLaunchParams) -> Self {
66 self.parameters = Some(LaunchParameters::Windows(params));
67 self
68 }
69
70 pub fn in_new_window(mut self, new_window: bool) -> Self {
72 self.new_window = Some(new_window);
73 self
74 }
75
76 pub fn to_dict(&self) -> Dictionary {
78 let mut dict = Dictionary::new();
79 dict.set("Type", Object::Name("Action".to_string()));
80 dict.set("S", Object::Name("Launch".to_string()));
81
82 dict.set("F", Object::String(self.file.clone()));
84
85 if let Some(params) = &self.parameters {
87 match params {
88 LaunchParameters::Simple(p) => {
89 dict.set("P", Object::String(p.clone()));
90 }
91 LaunchParameters::Windows(wp) => {
92 let mut win_dict = Dictionary::new();
93 win_dict.set("F", Object::String(wp.file_name.clone()));
94
95 if let Some(dir) = &wp.default_directory {
96 win_dict.set("D", Object::String(dir.clone()));
97 }
98 if let Some(op) = &wp.operation {
99 win_dict.set("O", Object::String(op.clone()));
100 }
101 if let Some(params) = &wp.parameters {
102 win_dict.set("P", Object::String(params.clone()));
103 }
104
105 dict.set("Win", Object::Dictionary(win_dict));
106 }
107 }
108 }
109
110 if let Some(nw) = self.new_window {
112 dict.set("NewWindow", Object::Boolean(nw));
113 }
114
115 dict
116 }
117}
118
119impl WindowsLaunchParams {
120 pub fn new(file_name: impl Into<String>) -> Self {
122 Self {
123 file_name: file_name.into(),
124 default_directory: None,
125 operation: None,
126 parameters: None,
127 }
128 }
129
130 pub fn with_directory(mut self, dir: impl Into<String>) -> Self {
132 self.default_directory = Some(dir.into());
133 self
134 }
135
136 pub fn with_operation(mut self, op: impl Into<String>) -> Self {
138 self.operation = Some(op.into());
139 self
140 }
141
142 pub fn with_parameters(mut self, params: impl Into<String>) -> Self {
144 self.parameters = Some(params.into());
145 self
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn test_launch_action_simple() {
155 let action = LaunchAction::application("notepad.exe");
156 let dict = action.to_dict();
157
158 assert_eq!(dict.get("S"), Some(&Object::Name("Launch".to_string())));
159 assert_eq!(
160 dict.get("F"),
161 Some(&Object::String("notepad.exe".to_string()))
162 );
163 }
164
165 #[test]
166 fn test_launch_action_with_params() {
167 let action = LaunchAction::document("document.txt")
168 .with_params("/p")
169 .in_new_window(true);
170
171 let dict = action.to_dict();
172
173 assert_eq!(
174 dict.get("F"),
175 Some(&Object::String("document.txt".to_string()))
176 );
177 assert_eq!(dict.get("P"), Some(&Object::String("/p".to_string())));
178 assert_eq!(dict.get("NewWindow"), Some(&Object::Boolean(true)));
179 }
180
181 #[test]
182 fn test_windows_launch_params() {
183 let win_params = WindowsLaunchParams::new("cmd.exe")
184 .with_directory("C:\\Windows\\System32")
185 .with_operation("open")
186 .with_parameters("/c dir");
187
188 let action = LaunchAction::new("cmd.exe").with_windows_params(win_params);
189
190 let dict = action.to_dict();
191
192 assert!(dict.get("Win").is_some());
193 if let Some(Object::Dictionary(win_dict)) = dict.get("Win") {
194 assert_eq!(
195 win_dict.get("F"),
196 Some(&Object::String("cmd.exe".to_string()))
197 );
198 assert_eq!(
199 win_dict.get("D"),
200 Some(&Object::String("C:\\Windows\\System32".to_string()))
201 );
202 assert_eq!(win_dict.get("O"), Some(&Object::String("open".to_string())));
203 assert_eq!(
204 win_dict.get("P"),
205 Some(&Object::String("/c dir".to_string()))
206 );
207 }
208 }
209
210 #[test]
211 fn test_launch_action_document() {
212 let action = LaunchAction::document("report.pdf");
213 assert_eq!(action.file, "report.pdf");
214 assert!(action.parameters.is_none());
215 assert!(action.new_window.is_none());
216 }
217
218 #[test]
219 fn test_launch_action_application() {
220 let action = LaunchAction::application("firefox");
221 assert_eq!(action.file, "firefox");
222 assert!(action.parameters.is_none());
223 assert!(action.new_window.is_none());
224 }
225
226 #[test]
227 fn test_launch_parameters_simple() {
228 let params = LaunchParameters::Simple("--help".to_string());
229 match params {
230 LaunchParameters::Simple(p) => assert_eq!(p, "--help"),
231 _ => panic!("Expected Simple parameters"),
232 }
233 }
234
235 #[test]
236 fn test_launch_parameters_windows() {
237 let win_params = WindowsLaunchParams::new("explorer.exe");
238 let params = LaunchParameters::Windows(win_params);
239 match params {
240 LaunchParameters::Windows(wp) => assert_eq!(wp.file_name, "explorer.exe"),
241 _ => panic!("Expected Windows parameters"),
242 }
243 }
244
245 #[test]
246 fn test_windows_params_minimal() {
247 let win_params = WindowsLaunchParams::new("calc.exe");
248 assert_eq!(win_params.file_name, "calc.exe");
249 assert!(win_params.default_directory.is_none());
250 assert!(win_params.operation.is_none());
251 assert!(win_params.parameters.is_none());
252 }
253
254 #[test]
255 fn test_windows_params_with_directory_only() {
256 let win_params = WindowsLaunchParams::new("app.exe").with_directory("C:\\Program Files");
257 assert_eq!(win_params.file_name, "app.exe");
258 assert_eq!(
259 win_params.default_directory,
260 Some("C:\\Program Files".to_string())
261 );
262 assert!(win_params.operation.is_none());
263 assert!(win_params.parameters.is_none());
264 }
265
266 #[test]
267 fn test_windows_params_with_operation_only() {
268 let win_params = WindowsLaunchParams::new("document.pdf").with_operation("print");
269 assert_eq!(win_params.file_name, "document.pdf");
270 assert!(win_params.default_directory.is_none());
271 assert_eq!(win_params.operation, Some("print".to_string()));
272 assert!(win_params.parameters.is_none());
273 }
274
275 #[test]
276 fn test_launch_action_without_new_window() {
277 let action = LaunchAction::new("app.exe");
278 let dict = action.to_dict();
279
280 assert!(dict.get("NewWindow").is_none());
282 }
283
284 #[test]
285 fn test_launch_action_with_false_new_window() {
286 let action = LaunchAction::new("app.exe").in_new_window(false);
287 let dict = action.to_dict();
288
289 assert_eq!(dict.get("NewWindow"), Some(&Object::Boolean(false)));
290 }
291
292 #[test]
293 fn test_launch_action_dict_type_fields() {
294 let action = LaunchAction::new("test.exe");
295 let dict = action.to_dict();
296
297 assert_eq!(dict.get("Type"), Some(&Object::Name("Action".to_string())));
299 assert_eq!(dict.get("S"), Some(&Object::Name("Launch".to_string())));
300 }
301
302 #[test]
303 fn test_windows_params_partial_dict() {
304 let win_params = WindowsLaunchParams::new("app.exe").with_parameters("/silent");
306
307 let action = LaunchAction::new("app.exe").with_windows_params(win_params);
308 let dict = action.to_dict();
309
310 if let Some(Object::Dictionary(win_dict)) = dict.get("Win") {
311 assert_eq!(
312 win_dict.get("F"),
313 Some(&Object::String("app.exe".to_string()))
314 );
315 assert!(win_dict.get("D").is_none()); assert!(win_dict.get("O").is_none()); assert_eq!(
318 win_dict.get("P"),
319 Some(&Object::String("/silent".to_string()))
320 );
321 } else {
322 panic!("Expected Win dictionary");
323 }
324 }
325}