format::desktop_entry: Add untested desktop action parsing

This commit is contained in:
Sasha Koshka 2024-10-22 12:59:19 -04:00
parent 0362ae4515
commit 34f1864d5f

View File

@ -1,3 +1,4 @@
use io;
use locale; use locale;
use strings; use strings;
@ -18,7 +19,6 @@ export type file = struct {
exec: str, exec: str,
path: str, path: str,
terminal: bool, terminal: bool,
actions: []str,
mime_type: []str, mime_type: []str,
categories: []str, categories: []str,
implements: []str, implements: []str,
@ -28,127 +28,179 @@ export type file = struct {
url: str, url: str,
prefers_non_default_gpu: bool, prefers_non_default_gpu: bool,
single_main_window: bool, single_main_window: bool,
actions: []action,
};
export type action = struct {
key: str,
name: locale::string,
icon: locale::string,
exec: str,
}; };
// Parses a desktop entry file. Use [[file_finish]] to get rid of it. // Parses a desktop entry file. Use [[file_finish]] to get rid of it.
export fn parse(input: io::handle) (file | error) = { export fn parse(input: io::handle) (file | error) = {
let state = parse_state { ... }; let file = file { ... };
let scanne = scan(input); let scanne = scan(input);
defer finish(&scanne); defer finish(&scanne);
for (let entr => next_entry(&scanne)) { for (let entr => next_entry(&scanne)?) {
match(parse_handle_entry(entr)) { match(parse_handle_entry(&file, entr)) {
case let err: error => case let err: error =>
file_finish(&state.file); file_finish(&file);
return err; return err;
case void => void; case void => void;
}; };
}; };
return file;
}; };
// Frees resources associated with a [[file]]. // Frees resources associated with a [[file]].
export fn file_finish(file: &file) void = { export fn file_finish(this: *file) void = {
free(file.typ); free(this.typ);
free(file.version); free(this.version);
locale_string_finish(file.name); locale_string_finish(this.name);
locale_string_finish(file.generic_name); locale_string_finish(this.generic_name);
locale_string_finish(file.comment); locale_string_finish(this.comment);
locale_string_finish(file.icon); locale_string_finish(this.icon);
strings::freeall(file.only_show_in); strings::freeall(this.only_show_in);
strings::freeall(file.not_show_in); strings::freeall(this.not_show_in);
free(file.try_exec); free(this.try_exec);
free(file.exec); free(this.exec);
free(file.path); free(this.path);
strings::freeall(file.actions); strings::freeall(this.mime_type);
strings::freeall(file.mime_type); strings::freeall(this.categories);
strings::freeall(file.categories); strings::freeall(this.implements);
strings::freeall(file.implements); locale_strings_finish(this.keywords);
locale_strings_finish(file.keywords); free(this.startup_wm_class);
free(file.startup_wm_class); free(this.url);
free(file.url);
for (let actio .. this.actions) {
free(actio.key);
locale_string_finish(actio.name);
locale_string_finish(actio.icon);
free(actio.exec);
};
free(this.actions);
}; };
type parse_state = struct { fn parse_handle_entry(this: *file, entr: entry) (void | error) = {
file: file, const desktop_action_prefix = "Desktop Action ";
if (entr.group == "Desktop Entry") {
return parse_handle_desktop_entry_entry(this, entr);
} else if (strings::hasprefix(entr.group, desktop_action_prefix)) {
let key = strings::trimprefix(entr.group, desktop_action_prefix);
return parse_handle_desktop_action_entry(this, entr, key);
};
}; };
fn parse_handle_entry(this: *parse_state, entr: entry) (void | error) = { fn parse_handle_desktop_action_entry(this: *file, entr: entry, key: str) (void | error) = {
if (entr.group != "Desktop Entry") return void; let actio = match(parse_find_action(this, key)) {
case let actio: *action => yield actio;
case void => return void;
};
if (entr.key == "Name") {
parse_set_locale_string(
&actio.name, entr.locale,
parse_localestring(entr.value)?);
} else if (entr.key == "Icon") {
parse_set_locale_string(
&actio.icon, entr.locale,
parse_iconstring(entr.value)?);
} else if (entr.key == "Exec") {
if (parse_is_localized(entr)) return void;
actio.exec = parse_string(entr.value)?;
};
};
fn parse_handle_desktop_entry_entry(this: *file, entr: entry) (void | error) = {
if (entr.key == "Type") { if (entr.key == "Type") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.typ = parse_string(entr.value)?; this.typ = parse_string(entr.value)?;
} else if (entr.key == "Version") { } else if (entr.key == "Version") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.version = parse_string(entr.value)?; this.version = parse_string(entr.value)?;
} else if (entr.key == "Name") { } else if (entr.key == "Name") {
parse_set_locale_string( parse_set_locale_string(
*this.name, entr.locale, &this.name, entr.locale,
parse_localestring(entr.value)?)?; parse_localestring(entr.value)?);
} else if (entr.key == "GenericName") { } else if (entr.key == "GenericName") {
parse_set_locale_string( parse_set_locale_string(
*this.generic_name, entr.locale, &this.generic_name, entr.locale,
parse_localestring(entr.value)?)?; parse_localestring(entr.value)?);
} else if (entr.key == "NoDisplay") { } else if (entr.key == "NoDisplay") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.no_display = parse_boolean(entr.value)?; this.no_display = parse_boolean(entr.value)?;
} else if (entr.key == "Comment") { } else if (entr.key == "Comment") {
parse_set_locale_string( parse_set_locale_string(
*this.comment, entr.locale, &this.comment, entr.locale,
parse_localestring(entr.value)?)?; parse_localestring(entr.value)?);
} else if (entr.key == "Icon") { } else if (entr.key == "Icon") {
parse_set_locale_string( parse_set_locale_string(
*this.icon, entr.locale, &this.icon, entr.locale,
parse_iconstring(entr.value)?)?; parse_iconstring(entr.value)?);
} else if (entr.key == "Hidden") { } else if (entr.key == "Hidden") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.hidden = parse_boolean(entr.value)?; this.hidden = parse_boolean(entr.value)?;
} else if (entr.key == "OnlyShowIn") { } else if (entr.key == "OnlyShowIn") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.only_show_in = parse_strings(entr.value)?; this.only_show_in = parse_strings(entr.value)?;
} else if (entr.key == "NotShowIn") { } else if (entr.key == "NotShowIn") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.not_show_in = parse_strings(entr.value)?; this.not_show_in = parse_strings(entr.value)?;
} else if (entr.key == "DBusActivatable") { } else if (entr.key == "DBusActivatable") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.dbus_activatable = parse_bool(entr.value)?; this.dbus_activatable = parse_boolean(entr.value)?;
} else if (entr.key == "TryExec") { } else if (entr.key == "TryExec") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.try_exec = parse_string(entr.value)?; this.try_exec = parse_string(entr.value)?;
} else if (entr.key == "Exec") {
if (parse_is_localized(entr)) return void;
this.exec = parse_string(entr.value)?;
} else if (entr.key == "Path") { } else if (entr.key == "Path") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.path = parse_string(entr.value)?; this.path = parse_string(entr.value)?;
} else if (entr.key == "Terminal") { } else if (entr.key == "Terminal") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.terminal = parse_boolean(entr.value)?; this.terminal = parse_boolean(entr.value)?;
} else if (entr.key == "Actions") { } else if (entr.key == "Actions") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.actions = parse_strings(entr.value)?; let strings = parse_strings(entr.value)?;
defer free(strings);
this.actions = alloc([], len(strings));
for (let string .. strings) {
append(this.actions, action {
key = string,
...
});
};
} else if (entr.key == "MimeType") { } else if (entr.key == "MimeType") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.mime_type = parse_strings(entr.value)?; this.mime_type = parse_strings(entr.value)?;
} else if (entr.key == "Categories") { } else if (entr.key == "Categories") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.categories = parse_strings(entr.value)?; this.categories = parse_strings(entr.value)?;
} else if (entr.key == "Implements") { } else if (entr.key == "Implements") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.implements = parse_strings(entr.value)?; this.implements = parse_strings(entr.value)?;
} else if (entr.key == "Keywords") { } else if (entr.key == "Keywords") {
parse_set_locale_strings( parse_set_locale_strings(
*this.keywords, entr.locale, &this.keywords, entr.locale,
parse_localestring(entr.value)?)?; parse_localestrings(entr.value)?);
} else if (entr.key == "StartupWMClass") { } else if (entr.key == "StartupWMClass") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.startup_wm_class = parse_string(entr.value)?; this.startup_wm_class = parse_string(entr.value)?;
} else if (entr.key == "URL") { } else if (entr.key == "URL") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.url = parse_string(entr.value)?; this.url = parse_string(entr.value)?;
} else if (entr.key == "PrefersNonDefaultGPU") { } else if (entr.key == "PrefersNonDefaultGPU") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.prefers_non_default_gpu = parse_bool(entr.value)?; this.prefers_non_default_gpu = parse_boolean(entr.value)?;
} else if (entr.key == "SingleMainWindow") { } else if (entr.key == "SingleMainWindow") {
if parse_is_localized(entr) return void; if (parse_is_localized(entr)) return void;
this.single_main_window = parse_bool(entr.value)?; this.single_main_window = parse_boolean(entr.value)?;
}; };
}; };
@ -156,24 +208,30 @@ fn parse_is_localized(entr: entry) bool = {
return !locale::equal(entr.locale, locale::c); return !locale::equal(entr.locale, locale::c);
}; };
fn parse_set_locale_string(dest *locale::string, local: locale::locale, value: str) void = { fn parse_set_locale_string(dest: *locale::string, local: locale::locale, value: str) void = {
for (let existing &.. *dest) { for (let existing &.. *dest) {
if (locale::equal(existing.0, locale)) { if (locale::equal(existing.0, local)) {
existing.1 = value; existing.1 = value;
return; return;
}; };
}; };
append(*dest, (locale::dup(local), strings::dup(value))); append(dest, (locale::dup(local), strings::dup(value)));
}; };
fn parse_set_locale_strings(dest *locale::strings, local: locale::locale, value: []str) void = { fn parse_set_locale_strings(dest: *locale::strings, local: locale::locale, value: []str) void = {
for (let existing &.. *dest) { for (let existing &.. *dest) {
if (locale::equal(existing.0, locale)) { if (locale::equal(existing.0, local)) {
existing.1 = value; existing.1 = value;
return; return;
}; };
}; };
append(*dest, (locale::dup(local), strings::dupall(value))); append(dest, (locale::dup(local), strings::dupall(value)));
};
fn parse_find_action(this: *file, key: str) (*action | void) = {
for (let actio &.. this.actions) {
if (actio.key == key) return actio;
};
}; };
fn locale_string_finish(string: locale::string) void = { fn locale_string_finish(string: locale::string) void = {
@ -185,5 +243,9 @@ fn locale_string_finish(string: locale::string) void = {
}; };
fn locale_strings_finish(strings: locale::strings) void = { fn locale_strings_finish(strings: locale::strings) void = {
for (string .. strings) locale_string_finish(string); for (let pair .. strings) {
locale::finish(pair.0);
strings::freeall(pair.1);
};
free(strings);
}; };