freedesktop_file_parser/
lib.rs

1pub mod internal_structs;
2pub mod parser;
3pub mod structs;
4
5pub use parser::parse;
6pub use structs::*;
7
8#[cfg(test)]
9mod tests {
10    use super::*;
11
12    #[test]
13    fn test_basic_valid_entry() {
14        let content = r#"
15[Desktop Entry]
16Name=Firefox
17Exec=firefox %U
18Type=Application
19Categories=Network;WebBrowser;
20"#;
21        let f = parse(content).unwrap();
22        let entry = f.entry;
23
24        assert_eq!(entry.name.default, "Firefox");
25
26        match entry.entry_type {
27            EntryType::Application(fields) => {
28                assert_eq!(fields.exec.unwrap(), "firefox %U");
29                assert_eq!(fields.categories.unwrap(), vec!["Network", "WebBrowser"]);
30            }
31            _ => panic!("Entry type is not Application"),
32        }
33    }
34
35    #[test]
36    fn test_localized_strings() {
37        let content = r#"
38[Desktop Entry]
39Name=Text Editor
40Name[es]=Editor de texto
41Name[fr]=Éditeur de texte
42Name[de]=Texteditor
43GenericName=Text Editor
44GenericName[es]=Editor
45Comment=Edit text files
46Comment[fr]=Éditer des fichiers texte
47Exec=gedit %F
48Type=Application
49"#;
50        let f = parse(content).unwrap();
51        let entry = f.entry;
52
53        let name = entry.name;
54        assert_eq!(name.default, "Text Editor");
55        assert_eq!(name.variants.get("es").unwrap(), "Editor de texto");
56        assert_eq!(name.variants.get("fr").unwrap(), "Éditeur de texte");
57        assert_eq!(name.variants.get("de").unwrap(), "Texteditor");
58
59        let generic_name = entry.generic_name.unwrap();
60        assert_eq!(generic_name.default, "Text Editor");
61        assert_eq!(generic_name.variants.get("es").unwrap(), "Editor");
62
63        let comment = entry.comment.unwrap();
64        assert_eq!(comment.default, "Edit text files");
65        assert_eq!(
66            comment.variants.get("fr").unwrap(),
67            "Éditer des fichiers texte"
68        );
69    }
70
71    #[test]
72    fn test_desktop_actions() {
73        let content = r#"
74[Desktop Entry]
75Name=Firefox
76Exec=firefox %U
77Type=Application
78Actions=new-window;new-private-window;
79
80[Desktop Action new-window]
81Name=New Window
82Name[es]=Nueva ventana
83Exec=firefox --new-window
84Icon=firefox-new-window
85
86[Desktop Action new-private-window]
87Name=New Private Window
88Exec=firefox --private-window
89"#;
90        let f = parse(content).unwrap();
91        let actions = f.actions;
92
93        assert_eq!(actions.len(), 2);
94
95        assert_eq!(
96            actions.get("new-window").unwrap().name.default,
97            "New Window"
98        );
99        assert_eq!(
100            actions
101                .get("new-window")
102                .unwrap()
103                .name
104                .variants
105                .get("es")
106                .unwrap(),
107            "Nueva ventana"
108        );
109        assert_eq!(
110            actions.get("new-window").unwrap().exec.as_ref().unwrap(),
111            "firefox --new-window"
112        );
113        assert_eq!(
114            actions
115                .get("new-window")
116                .unwrap()
117                .icon
118                .as_ref()
119                .unwrap()
120                .content,
121            "firefox-new-window"
122        );
123
124        assert_eq!(
125            actions.get("new-private-window").unwrap().name.default,
126            "New Private Window"
127        );
128        assert_eq!(
129            actions
130                .get("new-private-window")
131                .unwrap()
132                .exec
133                .as_ref()
134                .unwrap(),
135            "firefox --private-window"
136        );
137        assert!(actions.get("new-private-window").unwrap().icon.is_none());
138    }
139
140    #[test]
141    fn test_boolean_values() {
142        let content = r#"
143[Desktop Entry]
144Name=Test App
145Exec=test
146Type=Application
147Terminal=true
148NoDisplay=false
149Hidden=true
150DBusActivatable=true
151StartupNotify=true
152PrefersNonDefaultGPU=true
153SingleMainWindow=true
154"#;
155        let f = parse(content).unwrap();
156        let entry = f.entry;
157
158        assert_eq!(entry.no_display.unwrap(), false);
159        assert_eq!(entry.hidden.unwrap(), true);
160        assert_eq!(entry.dbus_activatable.unwrap(), true);
161
162        match entry.entry_type {
163            EntryType::Application(fields) => {
164                assert_eq!(fields.terminal.unwrap(), true);
165                assert_eq!(fields.startup_notify.unwrap(), true);
166                assert_eq!(fields.prefers_non_default_gpu.unwrap(), true);
167                assert_eq!(fields.single_main_window.unwrap(), true);
168            }
169            _ => panic!("Type not Application"),
170        }
171    }
172
173    #[test]
174    fn test_list_values() {
175        let content = r#"
176[Desktop Entry]
177Name=Test App
178Exec=test
179Type=Application
180Categories=Development;IDE;Programming;
181MimeType=text/plain;application/x-python;
182OnlyShowIn=GNOME;KDE;
183NotShowIn=XFCE;
184Keywords=development;coding;
185Keywords[es]=desarrollo;programación;
186Implements=org.freedesktop.Application;
187"#;
188        let f = parse(content).unwrap();
189        let entry = f.entry;
190        assert_eq!(entry.only_show_in.unwrap(), vec!["GNOME", "KDE"]);
191        assert_eq!(entry.not_show_in.unwrap(), vec!["XFCE"]);
192
193        match entry.entry_type {
194            EntryType::Application(fields) => {
195                let keywords = fields.keywords.unwrap();
196                assert_eq!(keywords.default, vec!["development", "coding"]);
197                assert_eq!(
198                    keywords.variants.get("es").unwrap(),
199                    &vec!["desarrollo", "programación"]
200                );
201
202                assert_eq!(
203                    fields.categories.unwrap(),
204                    vec!["Development", "IDE", "Programming"]
205                );
206                assert_eq!(
207                    fields.mime_type.unwrap(),
208                    vec!["text/plain", "application/x-python"]
209                );
210                assert_eq!(
211                    fields.implements.unwrap(),
212                    vec!["org.freedesktop.Application"]
213                );
214            }
215            _ => panic!("Entry type is not Application"),
216        }
217    }
218
219    #[test]
220    fn test_entry_types() {
221        let app_content = "[Desktop Entry]\nType=Application\nName=Test\nExec=test";
222        let link_content = "[Desktop Entry]\nType=Link\nName=Test\nURL=https://example.com";
223        let dir_content = "[Desktop Entry]\nType=Directory\nName=Test";
224        let unknown_content = "[Desktop Entry]\nType=CustomType\nName=Test";
225
226        let f = parse(app_content).unwrap();
227        let app_entry = f.entry;
228        let f = parse(link_content).unwrap();
229        let link_entry = f.entry;
230        let f = parse(dir_content).unwrap();
231        let dir_entry = f.entry;
232        let f = parse(unknown_content).unwrap();
233        let unknown_entry = f.entry;
234
235        assert!(matches!(app_entry.entry_type, EntryType::Application(_)));
236        assert!(matches!(link_entry.entry_type, EntryType::Link(_)));
237        assert!(matches!(dir_entry.entry_type, EntryType::Directory));
238        assert!(matches!(unknown_entry.entry_type, EntryType::Unknown));
239    }
240
241    #[test]
242    fn test_icon_string() {
243        let content = r#"
244[Desktop Entry]
245Name=Test App
246Exec=test
247Type=Application
248Icon=test-icon
249"#;
250        let f = parse(content).unwrap();
251        let entry = f.entry;
252        let icon = entry.icon.unwrap();
253        assert_eq!(icon.content, "test-icon");
254        // Note: We can't effectively test get_icon_path() here without mocking the filesystem
255    }
256
257    #[test]
258    #[should_panic]
259    fn test_missing_required_fields() {
260        let content = r#"
261[Desktop Entry]
262Exec=test
263Type=Application
264"#;
265        parse(content).unwrap();
266    }
267}