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