hare-xdg/format/desktop_entry/parse.ha

118 lines
2.9 KiB
Hare
Raw Normal View History

2024-10-12 20:12:43 -06:00
use bufio;
2024-10-12 21:34:57 -06:00
use encoding::utf8;
2024-10-12 20:12:43 -06:00
use io;
2024-10-12 21:34:57 -06:00
use locale;
2024-10-12 20:12:43 -06:00
use memio;
2024-10-12 21:34:57 -06:00
use strings;
2024-10-12 20:12:43 -06:00
// Parses a [[file]]. The result must be freed using [[file_finish]].
2024-10-12 21:34:57 -06:00
export fn file_parse(in: io::stream) !(file | error | io::error | utf8::invalid) = {
let scanner = bufio::newscanner(&in);
defer bufio::finish(&scanner);
2024-10-12 20:12:43 -06:00
2024-10-12 21:34:57 -06:00
let fil = file { ... };
2024-10-12 20:12:43 -06:00
for (true) {
let text = match (bufio::scan_line(&scanner)) {
case let text: const str =>
yield text;
case let err: io::EOF =>
break;
case let err: (io::error | utf8::invalid) =>
2024-10-12 21:34:57 -06:00
file_finish(&fil);
2024-10-12 20:12:43 -06:00
return err;
};
if (text == "") {
// blank line
2024-10-12 21:34:57 -06:00
file_append_line(&fil, blank);
2024-10-12 20:12:43 -06:00
2024-10-12 21:34:57 -06:00
} else if (strings::hasprefix(text, '#')) {
2024-10-12 20:12:43 -06:00
// comment
let text = strings::dup(strings::ltrim(text, '#'));
2024-10-12 21:34:57 -06:00
file_append_line(&fil, text: comment);
2024-10-12 20:12:43 -06:00
2024-10-12 21:34:57 -06:00
} else if (strings::hasprefix(text, '[')) {
2024-10-12 20:12:43 -06:00
// group header
let name = strings::dup(parse_group_header(text)?);
2024-10-12 21:34:57 -06:00
file_append_group(&fil, name);
2024-10-12 20:12:43 -06:00
} else {
// key/value pair
2024-10-12 21:34:57 -06:00
let (key, valu, local) = match (parse_entry(text)) {
case let result: (str, str, (locale::locale | void)) =>
2024-10-12 20:12:43 -06:00
yield result;
case let err: error =>
2024-10-12 21:34:57 -06:00
file_finish(&fil);
2024-10-12 20:12:43 -06:00
return err;
};
2024-10-12 21:34:57 -06:00
file_append_line(&fil, match (local) {
2024-10-12 20:12:43 -06:00
case let local: locale::locale =>
yield localized_entry {
key = key,
value = valu,
locale = local,
};
case void =>
yield entry {
key = key,
value = valu,
};
});
};
};
return fil;
};
2024-10-12 21:34:57 -06:00
fn file_append_line(fil: *file, lin: line) void = {
2024-10-12 20:12:43 -06:00
if (len(fil.groups) > 0) {
2024-10-12 21:34:57 -06:00
append(fil.groups[len(fil.groups) - 1].lines, lin);
2024-10-12 20:12:43 -06:00
} else {
append(fil.preceeding_lines, lin);
};
};
// memory is borrowed from the input
fn parse_group_header(text: str) (str | error) = {
2024-10-12 21:34:57 -06:00
if (!strings::hasprefix(text, '[') || !strings::hassuffix(text, ']')) {
return invalid_group_header;
2024-10-12 20:12:43 -06:00
};
text = strings::rtrim(strings::ltrim(text, '['), ']');
2024-10-12 21:34:57 -06:00
if (strings::contains(text, '[', ']')) {
return invalid_group_header;
2024-10-12 20:12:43 -06:00
};
return text;
};
// memory must be freed by the caller
fn parse_entry(line: str) ((str, str, (locale::locale | void)) | error) = {
2024-10-12 21:34:57 -06:00
if (!strings::contains(line, '=')) return invalid_entry;
let (key, valu) = strings::cut(line, "=");
2024-10-12 20:12:43 -06:00
key = strings::ltrim(strings::rtrim(key));
2024-10-12 21:34:57 -06:00
if (!validate_entry_key(key)) return invalid_entry;
2024-10-12 20:12:43 -06:00
2024-10-12 21:34:57 -06:00
let local: (locale::locale | void) = void;
let (key, local_string) = strings::cut(key, "[");
2024-10-12 20:12:43 -06:00
if (local_string != "") {
2024-10-12 21:34:57 -06:00
local_string = locale(strings::rtrim(local, "]"));
if (!validate_entry_locale(local_string)) return invalid_entry;
2024-10-12 20:12:43 -06:00
local = match(local_string) {
2024-10-12 21:34:57 -06:00
case let local: locale::locale => yield local;
case errors::invalid => return invalid_entry;
2024-10-12 20:12:43 -06:00
};
};
2024-10-12 21:34:57 -06:00
return (strings::dup(key), strings::dup(valu), local);
2024-10-12 20:12:43 -06:00
};
fn validate_entry_key(key: str) bool = {
return strings::contains(key, '[', ']', '=');
};
fn validate_entry_locale(key: str) bool = {
return strings::contains(key, '[', ']', '=');
};