diff --git a/locale/locale.ha b/locale/locale.ha new file mode 100644 index 0000000..bf39c8c --- /dev/null +++ b/locale/locale.ha @@ -0,0 +1,61 @@ +use errors; +use fmt; +use strings; + +// A parsed locale name. +export type locale = struct { + language: str, + country: str, + encoding: str, + modifier: str, +}; + +// Parses a [[locale]] name of the form: +// +// lang_COUNTRY.ENCODING@MODIFIER +// +// Where _COUNTRY, .ENCODING, and @MODIFIER may be omitted. The function +// returns a [[locale]], or [[errors::invalid]] if the input cannot be parsed. +// All memory is borrowed from the input. +export fn parse_locale(in: str) (locale | errors::invalid) = { + let (in, modifier) = strings::rcut(in, "@"); + if (strings::compare(in, "") == 0) return void: errors::invalid; + let (in, encoding) = strings::rcut(in, "."); + if (strings::compare(in, "") == 0) return void: errors::invalid; + let (in, country) = strings::rcut(in, "_"); + if (strings::compare(in, "") == 0) return void: errors::invalid; + return locale { + language = in, + country = country, + encoding = encoding, + modifier = modifier, + }; +}; + +// Formats a [[locale]]. The caller must free the return value. +export fn format_locale(local: locale) str = { + let output = strings::dup(local.language); + if (strings::compare(local.country, "") != 0) { + let new_output = strings::join("_", output, local.country); + free(output); + output = new_output; + }; + if (strings::compare(local.encoding, "") != 0) { + let new_output = strings::join(".", output, local.encoding); + free(output); + output = new_output; + }; + if (strings::compare(local.modifier, "") != 0) { + let new_output = strings::join("@", output, local.modifier); + free(output); + output = new_output; + }; + return output; +}; + +// Checks if two [[locale]]s are equal. +export fn locale_equal(a: locale, b: locale) bool = + strings::compare(a.language, b.language) == 0 && + strings::compare(a.country, b.country ) == 0 && + strings::compare(a.encoding, b.encoding) == 0 && + strings::compare(a.modifier, b.modifier) == 0; diff --git a/locale/locale_test.ha b/locale/locale_test.ha new file mode 100644 index 0000000..ab8a743 --- /dev/null +++ b/locale/locale_test.ha @@ -0,0 +1,143 @@ +use fmt; +use errors; +use strings; + +@test fn parse_locale_full() void = { + let local = match(parse_locale("lang_COUNTRY.ENCODING@MODIFIER")) { + case let local: locale => yield local; + case => abort("error"); + }; + fmt::printf("[{}]\n", local.language)!; + assert(strings::compare(local.language, "lang") == 0); + fmt::printf("[{}]\n", local.country)!; + assert(strings::compare(local.country, "COUNTRY") == 0); + fmt::printf("[{}]\n", local.encoding)!; + assert(strings::compare(local.encoding, "ENCODING") == 0); + fmt::printf("[{}]\n", local.modifier)!; + assert(strings::compare(local.modifier, "MODIFIER") == 0); +}; + +@test fn parse_locale_sparse_a() void = { + let local = match(parse_locale("lang.ENCODING@MODIFIER")) { + case let local: locale => yield local; + case => abort("error"); + }; + fmt::printf("[{}]\n", local.language)!; + assert(strings::compare(local.language, "lang") == 0); + fmt::printf("[{}]\n", local.country)!; + assert(strings::compare(local.country, "") == 0); + fmt::printf("[{}]\n", local.encoding)!; + assert(strings::compare(local.encoding, "ENCODING") == 0); + fmt::printf("[{}]\n", local.modifier)!; + assert(strings::compare(local.modifier, "MODIFIER") == 0); +}; + +@test fn parse_locale_sparse_b() void = { + let local = match(parse_locale("lang.ENCODING")) { + case let local: locale => yield local; + case => abort("error"); + }; + fmt::printf("[{}]\n", local.language)!; + assert(strings::compare(local.language, "lang") == 0); + fmt::printf("[{}]\n", local.country)!; + assert(strings::compare(local.country, "") == 0); + fmt::printf("[{}]\n", local.encoding)!; + assert(strings::compare(local.encoding, "ENCODING") == 0); + fmt::printf("[{}]\n", local.modifier)!; + assert(strings::compare(local.modifier, "") == 0); +}; + +@test fn parse_locale_sparse_c() void = { + let local = match(parse_locale("lang")) { + case let local: locale => yield local; + case => abort("error"); + }; + fmt::printf("[{}]\n", local.language)!; + assert(strings::compare(local.language, "lang") == 0); + fmt::printf("[{}]\n", local.country)!; + assert(strings::compare(local.country, "") == 0); + fmt::printf("[{}]\n", local.encoding)!; + assert(strings::compare(local.encoding, "") == 0); + fmt::printf("[{}]\n", local.modifier)!; + assert(strings::compare(local.modifier, "") == 0); +}; + +@test fn parse_locale_error() void = { + let local = match(parse_locale("_COUNTRY.ENCODING@MODIFIER")) { + case errors::invalid => void; + case => abort("error"); + }; +}; + +@test fn format_locale_a() void = { + let string = format_locale(locale { + language = "lang", + country = "COUNTRY", + encoding = "ENCODING", + modifier = "MODIFIER", + }); + defer free(string); + fmt::printf("[{}]\n", string)!; + assert(strings::compare(string, "lang_COUNTRY.ENCODING@MODIFIER") == 0); +}; + +@test fn format_locale_b() void = { + let string = format_locale(locale { + language = "lang", + country = "COUNTRY", + modifier = "MODIFIER", + ... + }); + defer free(string); + fmt::printf("[{}]\n", string)!; + assert(strings::compare(string, "lang_COUNTRY@MODIFIER") == 0); +}; + +@test fn format_locale_c() void = { + let string = format_locale(locale { + language = "lang", + ... + }); + defer free(string); + fmt::printf("[{}]\n", string)!; + assert(strings::compare(string, "lang") == 0); +}; + +@test fn locale_equal() void = { + assert(locale_equal(locale { + language = "lang", + country = "COUNTRY", + encoding = "ENCODING", + modifier = "MODIFIER", + }, + locale { + language = "lang", + country = "COUNTRY", + encoding = "ENCODING", + modifier = "MODIFIER", + })); + assert(!locale_equal(locale { + language = "lang", + country = "COUNTRY", + encoding = "ENCODING", + modifier = "MODIFIER", + }, + locale { + language = "foo", + country = "COUNTRY", + encoding = "ENCODING", + modifier = "MODIFIER", + })); + assert(!locale_equal(locale { + language = "lang", + encoding = "ENCODING", + modifier = "MODIFIER", + ... + }, + locale { + language = "lang", + country = "COUNTRY", + encoding = "ENCODING", + ... + })); +};