Compare commits
21 Commits
main
...
kifu/local
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | b36e152050 | |
Savanni D'Gerinel | 9e5bac3001 | |
Savanni D'Gerinel | 1e5341d0e6 | |
Savanni D'Gerinel | 4114e64b8e | |
Savanni D'Gerinel | ff1d117e8c | |
Savanni D'Gerinel | 5c7f7766de | |
Savanni D'Gerinel | 034bda502a | |
Savanni D'Gerinel | 15b1d4bfd6 | |
Savanni D'Gerinel | 8ceca454b1 | |
Savanni D'Gerinel | 30198f0038 | |
Savanni D'Gerinel | 1881fb99ce | |
Savanni D'Gerinel | 4cfc6425e1 | |
Savanni D'Gerinel | 137a88ad8e | |
Savanni D'Gerinel | f8c19b2d18 | |
Savanni D'Gerinel | 35c034b04b | |
Savanni D'Gerinel | 9699c63e50 | |
Savanni D'Gerinel | 822e88a8ce | |
Savanni D'Gerinel | a9d29e6518 | |
Savanni D'Gerinel | d95dd4de50 | |
Savanni D'Gerinel | 245f9d0997 | |
Savanni D'Gerinel | 22b772a8c7 |
|
@ -483,6 +483,16 @@ dependencies = [
|
||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "calendrical_calculations"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8dfe3bc6a50b4667fafdb6d9cf26731c5418c457e317d8166c972014facf9a5d"
|
||||||
|
dependencies = [
|
||||||
|
"core_maths",
|
||||||
|
"displaydoc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.83"
|
version = "1.0.83"
|
||||||
|
@ -648,6 +658,15 @@ version = "0.9.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie"
|
name = "cookie"
|
||||||
version = "0.17.0"
|
version = "0.17.0"
|
||||||
|
@ -692,6 +711,15 @@ version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core_maths"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3b02505ccb8c50b0aa21ace0fc08c3e53adebd4e58caa18a36152803c7709a3"
|
||||||
|
dependencies = [
|
||||||
|
"libm",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
|
@ -1117,6 +1145,18 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fixed_decimal"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cbc7fdec9d7f6671a3ebb3282c969962aba67c49f6abac5311959b65cafabc10"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"ryu",
|
||||||
|
"smallvec",
|
||||||
|
"writeable",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.28"
|
version = "1.0.28"
|
||||||
|
@ -2020,6 +2060,422 @@ dependencies = [
|
||||||
"libadwaita",
|
"libadwaita",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21be1c98fbdb29fff7e34b2939a4f30dad330d4cc20f7be1b3956e21032f67ba"
|
||||||
|
dependencies = [
|
||||||
|
"icu_calendar",
|
||||||
|
"icu_casemap",
|
||||||
|
"icu_collator",
|
||||||
|
"icu_collections",
|
||||||
|
"icu_compactdecimal",
|
||||||
|
"icu_datetime",
|
||||||
|
"icu_decimal",
|
||||||
|
"icu_displaynames",
|
||||||
|
"icu_list",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_locid_transform",
|
||||||
|
"icu_normalizer",
|
||||||
|
"icu_plurals",
|
||||||
|
"icu_properties",
|
||||||
|
"icu_provider",
|
||||||
|
"icu_relativetime",
|
||||||
|
"icu_segmenter",
|
||||||
|
"icu_timezone",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_calendar"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7eb932a690c92f87955e923106181ee0d5682e688ff37fb5c7b296e1fe806edb"
|
||||||
|
dependencies = [
|
||||||
|
"calendrical_calculations",
|
||||||
|
"displaydoc",
|
||||||
|
"icu_calendar_data",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_locid_transform",
|
||||||
|
"icu_provider",
|
||||||
|
"tinystr",
|
||||||
|
"writeable",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_calendar_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22aec7d032735d9acb256eeef72adcac43c3b7572f19b51576a63d664b524ca2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_casemap"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7988d4f2655012592ac5b027722a93fbe12ff2a86d3e0f9ae686aedba0984f5e"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_casemap_data",
|
||||||
|
"icu_collections",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_properties",
|
||||||
|
"icu_provider",
|
||||||
|
"writeable",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_casemap_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f36409fb8ce6f87c408310d87396ac471cc7320e007e648814c607c60fe77cc5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_collator"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a2a45056e541cffde068f5c81ac1c0503b9ee2a4b967546422e509c5c653750"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_collator_data",
|
||||||
|
"icu_collections",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_locid_transform",
|
||||||
|
"icu_normalizer",
|
||||||
|
"icu_properties",
|
||||||
|
"icu_provider",
|
||||||
|
"smallvec",
|
||||||
|
"utf16_iter",
|
||||||
|
"utf8_iter",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_collator_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39774016c5b9ad006941f3196fea83ade662d9167eb573111c8f4cc9593e2999"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_collections"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "137d96353afc8544d437e8a99eceb10ab291352699573b0de5b08bda38c78c60"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_compactdecimal"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d3c22da90659121ae1e38bd0fb378d15179c820f56a0c278032fb4eb5a0ca60"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"fixed_decimal",
|
||||||
|
"icu_compactdecimal_data",
|
||||||
|
"icu_decimal",
|
||||||
|
"icu_locid_transform",
|
||||||
|
"icu_plurals",
|
||||||
|
"icu_provider",
|
||||||
|
"writeable",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_compactdecimal_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53eaf4902ee2e804f2583611722f6961fe85f31eab7ec790f47dde2d1cd494fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_datetime"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1508c7ed627cc0b031c81203eb98f34433e24b32b39d5b2c0238e4962a00957d"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"either",
|
||||||
|
"fixed_decimal",
|
||||||
|
"icu_calendar",
|
||||||
|
"icu_datetime_data",
|
||||||
|
"icu_decimal",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_locid_transform",
|
||||||
|
"icu_plurals",
|
||||||
|
"icu_provider",
|
||||||
|
"icu_timezone",
|
||||||
|
"smallvec",
|
||||||
|
"tinystr",
|
||||||
|
"writeable",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_datetime_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6abc569cb4ee80b30707566f05c5c9ed4bed765f91ce41e7f5a37c5e6a75b3f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_decimal"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcf994f9ed8061c17bb313f28fba6cffc736f0a16c7fab827efc9b73fd3f7778"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"fixed_decimal",
|
||||||
|
"icu_decimal_data",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_locid_transform",
|
||||||
|
"icu_provider",
|
||||||
|
"writeable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_decimal_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df2de3548316b697c70f30dec1395c9212db09df1d86a27624ee24872b71326c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_displaynames"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "726c0d83ff52f05907275f39e5bb7949a92fa3d09538de60cf73ccf8ee89a613"
|
||||||
|
dependencies = [
|
||||||
|
"icu_displaynames_data",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_locid_transform",
|
||||||
|
"icu_provider",
|
||||||
|
"tinystr",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_displaynames_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af40e6b723e5e6d9359cf0bb4e4ed6dfb9d6ab16b73b5c82b61f947e88bb30f6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_list"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe6c04ec71ad1bacdbfb47164d4801f80a0533d9340f94f1a880f521eff59f54"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_list_data",
|
||||||
|
"icu_locid_transform",
|
||||||
|
"icu_provider",
|
||||||
|
"regex-automata 0.2.0",
|
||||||
|
"writeable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_list_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f6afcf7a9a7fedece70b7f17d7a7ecdfb8df145d37ae46d0277cd1e3932532"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_locid"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c0aa2536adc14c07e2a521e95512b75ed8ef832f0fdf9299d4a0a45d2be2a9d"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"litemap",
|
||||||
|
"tinystr",
|
||||||
|
"writeable",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_locid_transform"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c17d8f6524fdca4471101dd71f0a132eb6382b5d6d7f2970441cb25f6f435a"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_locid_transform_data",
|
||||||
|
"icu_provider",
|
||||||
|
"tinystr",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_locid_transform_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "545c6c3e8bf9580e2dafee8de6f9ec14826aaf359787789c7724f1f85f47d3dc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_normalizer"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c183e31ed700f1ecd6b032d104c52fe8b15d028956b73727c97ec176b170e187"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_collections",
|
||||||
|
"icu_normalizer_data",
|
||||||
|
"icu_properties",
|
||||||
|
"icu_provider",
|
||||||
|
"smallvec",
|
||||||
|
"utf16_iter",
|
||||||
|
"utf8_iter",
|
||||||
|
"write16",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_normalizer_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22026918a80e6a9a330cb01b60f950e2b4e5284c59528fd0c6150076ef4c8522"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_plurals"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37d807b123eb2a9ae8f12080fb8cce479f5c8a761fba0bb5ab52da6dd5e31a03"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"fixed_decimal",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_locid_transform",
|
||||||
|
"icu_plurals_data",
|
||||||
|
"icu_provider",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_plurals_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3acd5f1f2f988ed2dae9316c3d3560dfe4e03a7516d142b4b89b92252ada41a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_properties"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "976e296217453af983efa25f287a4c1da04b9a63bf1ed63719455068e4453eb5"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_collections",
|
||||||
|
"icu_locid_transform",
|
||||||
|
"icu_properties_data",
|
||||||
|
"icu_provider",
|
||||||
|
"tinystr",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_properties_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6a86c0e384532b06b6c104814f9c1b13bcd5b64409001c0d05713a1f3529d99"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_provider"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba58e782287eb6950247abbf11719f83f5d4e4a5c1f2cd490d30a334bc47c2f4"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_provider_macros",
|
||||||
|
"stable_deref_trait",
|
||||||
|
"tinystr",
|
||||||
|
"writeable",
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_provider_macros"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2abdd3a62551e8337af119c5899e600ca0c88ec8f23a46c60ba216c803dcf1a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_relativetime"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47825312a5eb0790bad7b718fa8d41a8ea1e0ba597b4f7bb84bcfe97d7fc5aba"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"fixed_decimal",
|
||||||
|
"icu_decimal",
|
||||||
|
"icu_locid_transform",
|
||||||
|
"icu_plurals",
|
||||||
|
"icu_provider",
|
||||||
|
"icu_relativetime_data",
|
||||||
|
"writeable",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_relativetime_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05b55cc15ea8981fbba78e9347d0c4003d4490c85f76e9adc7f270290046cae8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_segmenter"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2dc1e8f4ba33a6a4956770ac5c08570f255d6605519fb3a859a0c0a270a2f8f"
|
||||||
|
dependencies = [
|
||||||
|
"core_maths",
|
||||||
|
"displaydoc",
|
||||||
|
"icu_collections",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_provider",
|
||||||
|
"icu_segmenter_data",
|
||||||
|
"utf8_iter",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_segmenter_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3673d6698dcffce08cfe8fc5da3c11c3f2c663d5d6137fd58ab2cbf44235ab46"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_timezone"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b35aabe571a7c653c0f543ff1512b8a1b2ad481cfa24b3d25115298d2ff3b50f"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_calendar",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_provider",
|
||||||
|
"icu_timezone_data",
|
||||||
|
"tinystr",
|
||||||
|
"zerotrie",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_timezone_data"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ceee21e181cce2ab44e95923da6b3418df75369f570df82264c29c51ca398d4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
@ -2241,15 +2697,20 @@ dependencies = [
|
||||||
"async-channel 2.1.1",
|
"async-channel 2.1.1",
|
||||||
"async-std",
|
"async-std",
|
||||||
"cairo-rs",
|
"cairo-rs",
|
||||||
|
"fluent",
|
||||||
|
"fluent-ergonomics",
|
||||||
"gio",
|
"gio",
|
||||||
"glib",
|
"glib",
|
||||||
"glib-build-tools 0.17.10",
|
"glib-build-tools 0.17.10",
|
||||||
"gtk4",
|
"gtk4",
|
||||||
"image 0.24.7",
|
"image 0.24.7",
|
||||||
"kifu-core",
|
"kifu-core",
|
||||||
|
"l10n",
|
||||||
"libadwaita",
|
"libadwaita",
|
||||||
"pango",
|
"pango",
|
||||||
"sgf",
|
"sgf",
|
||||||
|
"sys-locale",
|
||||||
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2262,6 +2723,27 @@ dependencies = [
|
||||||
"log 0.4.20",
|
"log 0.4.20",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "l10n"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"chrono-tz",
|
||||||
|
"convert_case",
|
||||||
|
"fixed_decimal",
|
||||||
|
"fluent",
|
||||||
|
"fluent-ergonomics",
|
||||||
|
"icu",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_provider",
|
||||||
|
"intl-memoizer",
|
||||||
|
"serde 1.0.193",
|
||||||
|
"serde_yaml",
|
||||||
|
"sys-locale",
|
||||||
|
"thiserror",
|
||||||
|
"unic-langid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "language-tags"
|
name = "language-tags"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -2350,6 +2832,12 @@ version = "0.4.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
|
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "litemap"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9d642685b028806386b2b6e75685faadd3eb65a85fff7df711ce18446a422da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.11"
|
version = "0.4.11"
|
||||||
|
@ -3364,10 +3852,19 @@ checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata",
|
"regex-automata 0.4.3",
|
||||||
"regex-syntax",
|
"regex-syntax",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e9368763f5a9b804326f3af749e16f9abf378d227bcdee7634b13d8f17793782"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
@ -3677,6 +4174,19 @@ dependencies = [
|
||||||
"serde 1.0.193",
|
"serde 1.0.193",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_yaml"
|
||||||
|
version = "0.9.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a15e0ef66bf939a7c890a0bf6d5a733c70202225f9888a89ed5c62298b019129"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde 1.0.193",
|
||||||
|
"unsafe-libyaml",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sgf"
|
name = "sgf"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -4015,6 +4525,12 @@ dependencies = [
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stringprep"
|
name = "stringprep"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
@ -4060,6 +4576,26 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synstructure"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sys-locale"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "system-configuration"
|
name = "system-configuration"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -4229,6 +4765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece"
|
checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4577,6 +5114,12 @@ dependencies = [
|
||||||
"traitobject",
|
"traitobject",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unsafe-libyaml"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "1.7.2"
|
version = "1.7.2"
|
||||||
|
@ -4611,6 +5154,18 @@ version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf16_iter"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8_iter"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -5034,6 +5589,46 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "write16"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "writeable"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dad7bb64b8ef9c0aa27b6da38b452b0ee9fd82beaf276a87dd796fb55cbae14e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xdg-test"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yoke"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "65e71b2e4f287f467794c671e2b8f8a5f3716b3c829079a1c44740148eff07e4"
|
||||||
|
dependencies = [
|
||||||
|
"serde 1.0.193",
|
||||||
|
"stable_deref_trait",
|
||||||
|
"yoke-derive",
|
||||||
|
"zerofrom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yoke-derive"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.48",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.31"
|
version = "0.7.31"
|
||||||
|
@ -5054,12 +5649,66 @@ dependencies = [
|
||||||
"syn 2.0.48",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerofrom"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "655b0814c5c0b19ade497851070c640773304939a6c0fd5f5fb43da0696d05b7"
|
||||||
|
dependencies = [
|
||||||
|
"zerofrom-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerofrom-derive"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.48",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zeroize"
|
name = "zeroize"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
|
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerotrie"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0594125a0574fb93059c92c588ab209cc036a23d1baeb3410fa9181bea551a0"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerovec"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eff4439ae91fb5c72b8abc12f3f2dbf51bd27e6eadb9f8a5bc8898dddb0e27ea"
|
||||||
|
dependencies = [
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
"zerovec-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerovec-derive"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b4e5997cbf58990550ef1f0e5124a05e47e1ebd33a84af25739be6031a62c20"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zune-inflate"
|
name = "zune-inflate"
|
||||||
version = "0.2.54"
|
version = "0.2.54"
|
||||||
|
|
|
@ -20,6 +20,7 @@ members = [
|
||||||
"ifc",
|
"ifc",
|
||||||
"kifu/core",
|
"kifu/core",
|
||||||
"kifu/gtk",
|
"kifu/gtk",
|
||||||
|
"l10n",
|
||||||
"memorycache",
|
"memorycache",
|
||||||
"nom-training",
|
"nom-training",
|
||||||
"result-extended",
|
"result-extended",
|
||||||
|
@ -28,4 +29,5 @@ members = [
|
||||||
"timezone-testing",
|
"timezone-testing",
|
||||||
"tree",
|
"tree",
|
||||||
"visions/server",
|
"visions/server",
|
||||||
|
"xdg-test",
|
||||||
]
|
]
|
||||||
|
|
|
@ -58,6 +58,10 @@ fn setup_app_close_action(app: &adw::Application) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
for (name, value) in std::env::vars() {
|
||||||
|
println!("{}: {}", name, value);
|
||||||
|
}
|
||||||
|
|
||||||
// I still don't fully understand gio resources. resources_register_include! is convenient
|
// I still don't fully understand gio resources. resources_register_include! is convenient
|
||||||
// because I don't have to deal with filesystem locations at runtime. However, I think other
|
// because I don't have to deal with filesystem locations at runtime. However, I think other
|
||||||
// GTK applications do that rather than compiling the resources directly into the app. So, I'm
|
// GTK applications do that rather than compiling the resources directly into the app. So, I'm
|
||||||
|
|
22
flake.nix
22
flake.nix
|
@ -17,6 +17,21 @@
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||||
pkgs-unstable = import unstable { system = "x86_64-linux"; };
|
pkgs-unstable = import unstable { system = "x86_64-linux"; };
|
||||||
|
|
||||||
|
cargo_nix = pkgs.callPackage ./Cargo.nix {
|
||||||
|
nixpkgs = pkgs;
|
||||||
|
};
|
||||||
|
|
||||||
|
l10n-codegen-rust = pkgs.stdenv.mkDerivation {
|
||||||
|
name = "l10n-codegen-rust";
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cp ${cargo_nix.workspaceMembers.l10n.build}/bin/codegen-rust $out/bin/l10n-codegen-rust
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
in
|
in
|
||||||
pkgs.mkShell {
|
pkgs.mkShell {
|
||||||
name = "ld-tools-devshell";
|
name = "ld-tools-devshell";
|
||||||
|
@ -45,6 +60,8 @@
|
||||||
pkgs.udev
|
pkgs.udev
|
||||||
pkgs.wasm-pack
|
pkgs.wasm-pack
|
||||||
typeshare.packages."x86_64-linux".default
|
typeshare.packages."x86_64-linux".default
|
||||||
|
|
||||||
|
l10n-codegen-rust
|
||||||
];
|
];
|
||||||
LIBCLANG_PATH="${pkgs.llvmPackages.libclang.lib}/lib";
|
LIBCLANG_PATH="${pkgs.llvmPackages.libclang.lib}/lib";
|
||||||
ENV = "dev";
|
ENV = "dev";
|
||||||
|
@ -71,6 +88,7 @@
|
||||||
dashboard = attrs: { nativeBuildInputs = gtkNativeInputs; };
|
dashboard = attrs: { nativeBuildInputs = gtkNativeInputs; };
|
||||||
fitnesstrax = import ./fitnesstrax/app/override.nix { gtkNativeInputs = gtkNativeInputs; };
|
fitnesstrax = import ./fitnesstrax/app/override.nix { gtkNativeInputs = gtkNativeInputs; };
|
||||||
kifu-gtk = import ./kifu/gtk/override.nix { gtkNativeInputs = gtkNativeInputs; };
|
kifu-gtk = import ./kifu/gtk/override.nix { gtkNativeInputs = gtkNativeInputs; };
|
||||||
|
xdg-test = import ./xdg-test/override.nix { wrapGAppsHook4 = pkgs.wrapGAppsHook4; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,6 +103,7 @@
|
||||||
file-service = cargo_nix.workspaceMembers.file-service.build;
|
file-service = cargo_nix.workspaceMembers.file-service.build;
|
||||||
fitnesstrax = cargo_nix.workspaceMembers.fitnesstrax.build;
|
fitnesstrax = cargo_nix.workspaceMembers.fitnesstrax.build;
|
||||||
kifu-gtk = cargo_nix.workspaceMembers.kifu-gtk.build;
|
kifu-gtk = cargo_nix.workspaceMembers.kifu-gtk.build;
|
||||||
|
xdg-test = cargo_nix.workspaceMembers.xdg-test.build;
|
||||||
|
|
||||||
all = pkgs.symlinkJoin {
|
all = pkgs.symlinkJoin {
|
||||||
name = "all";
|
name = "all";
|
||||||
|
@ -93,7 +112,8 @@
|
||||||
dashboard
|
dashboard
|
||||||
file-service
|
file-service
|
||||||
fitnesstrax
|
fitnesstrax
|
||||||
kifu-gtk
|
# kifu-gtk
|
||||||
|
xdg-test
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -13,24 +13,19 @@ adw = { version = "0.5", package = "libadwaita", features = [ "v1_2"
|
||||||
async-channel = { version = "2" }
|
async-channel = { version = "2" }
|
||||||
async-std = { version = "1" }
|
async-std = { version = "1" }
|
||||||
cairo-rs = { version = "0.18" }
|
cairo-rs = { version = "0.18" }
|
||||||
|
fluent-ergonomics = { path = "../../fluent-ergonomics" }
|
||||||
|
fluent = { version = "0.16" }
|
||||||
gio = { version = "0.18" }
|
gio = { version = "0.18" }
|
||||||
glib = { version = "0.18" }
|
glib = { version = "0.18" }
|
||||||
gtk = { version = "0.7", package = "gtk4", features = [ "v4_8" ] }
|
gtk = { version = "0.7", package = "gtk4", features = [ "v4_8" ] }
|
||||||
image = { version = "0.24" }
|
image = { version = "0.24" }
|
||||||
kifu-core = { path = "../core" }
|
kifu-core = { path = "../core" }
|
||||||
|
l10n = { path = "../../l10n" }
|
||||||
pango = { version = "*" }
|
pango = { version = "*" }
|
||||||
sgf = { path = "../../sgf" }
|
sgf = { path = "../../sgf" }
|
||||||
|
sys-locale = { version = "0.3" }
|
||||||
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1.26", features = [ "full" ] }
|
tokio = { version = "1.26", features = [ "full" ] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
glib-build-tools = "0.17"
|
glib-build-tools = "0.17"
|
||||||
|
|
||||||
# [[bin]]
|
|
||||||
# name = "kifu-gtk"
|
|
||||||
# path = "src/main.rs"
|
|
||||||
|
|
||||||
# [[bin]]
|
|
||||||
# name = "screenplay"
|
|
||||||
# path = "src/bin/screenplay.rs"
|
|
||||||
# required-features = [ "screenplay" ]
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,29 @@
|
||||||
|
use std::{path::PathBuf, env, process, fs::File, io::Write};
|
||||||
|
|
||||||
|
fn generate_message_types(dest: &PathBuf) {
|
||||||
|
println!("message types");
|
||||||
|
let output: Vec<u8> = process::Command::new("l10n-codegen-rust")
|
||||||
|
.arg("messages/en.yaml")
|
||||||
|
.output()
|
||||||
|
.unwrap()
|
||||||
|
.stdout;
|
||||||
|
|
||||||
|
let mut file = File::create(dest).unwrap();
|
||||||
|
file.write(&output).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
glib_build_tools::compile_resources(
|
glib_build_tools::compile_resources(
|
||||||
&["resources"],
|
&["resources"],
|
||||||
"gresources.xml",
|
"gresources.xml",
|
||||||
"com.luminescent-dreams.kifu-gtk.gresource",
|
"com.luminescent-dreams.kifu-gtk.gresource",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// println!("OUT_DIR={}", env::var("OUT_DIR").unwrap());
|
||||||
|
let mut message_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||||
|
message_path.push("messages.rs");
|
||||||
|
|
||||||
|
generate_message_types(&message_path);
|
||||||
|
|
||||||
|
println!("cargo:rustc-env=KIFU_GTK_MESSAGES={}", message_path.to_string_lossy());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
nothing-here = Nothing Here
|
||||||
|
hello = Hello, ${name}
|
||||||
|
games-in-database = {$count ->
|
||||||
|
[one] There is one game in the database
|
||||||
|
*[other] There are ${count} games in the database
|
||||||
|
}
|
||||||
|
|
||||||
|
welcome = Welcome to Kifu
|
|
@ -0,0 +1,7 @@
|
||||||
|
nothing-here = Nenio estas ĉi tie
|
||||||
|
hello = Saluton, ${name}
|
||||||
|
welcome = Bonvenon
|
||||||
|
games-in-database = {$count ->
|
||||||
|
[one] Estas unu ludon en la datumbazo.
|
||||||
|
*[other] Estas ${count} ludojn en la datumbazo.
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
nothing-here:
|
||||||
|
content: "Nothing Here"
|
||||||
|
|
||||||
|
welcome:
|
||||||
|
content: "Welcome to Kifu"
|
||||||
|
|
||||||
|
hello:
|
||||||
|
parameters:
|
||||||
|
name: string
|
||||||
|
content: "Hello, ${name}"
|
||||||
|
|
||||||
|
games-in-database:
|
||||||
|
parameters:
|
||||||
|
count: count
|
||||||
|
content: |
|
||||||
|
{$count ->
|
||||||
|
[one] There is one game in the database
|
||||||
|
*[other] There are ${count} games in the database
|
||||||
|
}
|
|
@ -1,13 +1,18 @@
|
||||||
|
use async_std::task::yield_now;
|
||||||
|
use kifu_core::{Core, CoreRequest, Observable};
|
||||||
|
use l10n::{NonEmptyList, L10N};
|
||||||
|
use std::{rc::Rc, sync::Arc};
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
|
mod messages {
|
||||||
|
include!(env!("KIFU_GTK_MESSAGES"));
|
||||||
|
}
|
||||||
|
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
|
||||||
mod view_models;
|
mod view_models;
|
||||||
mod views;
|
mod views;
|
||||||
|
|
||||||
use async_std::task::yield_now;
|
|
||||||
use kifu_core::{Core, CoreRequest, CoreResponse, Observable};
|
|
||||||
use std::{rc::Rc, sync::Arc};
|
|
||||||
use tokio::runtime::Runtime;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CoreApi {
|
pub struct CoreApi {
|
||||||
pub rt: Arc<Runtime>,
|
pub rt: Arc<Runtime>,
|
||||||
|
@ -39,6 +44,45 @@ where
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// AppContext is our global store for things that need to be available everywhere. Developers
|
||||||
|
/// should never pass this around directly, but should instead pass it around by traits. This is to
|
||||||
|
/// provide systems such as:
|
||||||
|
///
|
||||||
|
/// - Feature Flags
|
||||||
|
/// - L10N
|
||||||
|
pub struct AppContext {
|
||||||
|
pub l10n: L10N,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AppContextInitError {}
|
||||||
|
|
||||||
|
impl AppContext {
|
||||||
|
pub fn new() -> Result<Self, AppContextInitError> {
|
||||||
|
let mut locale_list = NonEmptyList::new("en-US");
|
||||||
|
|
||||||
|
let user_locale = sys_locale::get_locale().unwrap();
|
||||||
|
println!("user locale: {}", user_locale);
|
||||||
|
if locale_list.find(|l| *l == user_locale).is_none() {
|
||||||
|
locale_list.push(&user_locale);
|
||||||
|
}
|
||||||
|
let mut l10n = l10n::L10N::new("messages".into());
|
||||||
|
l10n.set_locales(locale_list);
|
||||||
|
|
||||||
|
Ok(Self { l10n })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ProvidesL10N {
|
||||||
|
fn l10n(&self) -> &L10N;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProvidesL10N for AppContext {
|
||||||
|
fn l10n(&self) -> &L10N {
|
||||||
|
&self.l10n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// LocalObserver creates a task on the current thread which watches the specified observer for notifications and calls the handler function with each one.
|
/// LocalObserver creates a task on the current thread which watches the specified observer for notifications and calls the handler function with each one.
|
||||||
///
|
///
|
||||||
/// The LocalObserver starts a task which listens for notifications during the constructor. When the observer goes out of scope, it will make a point of aborting the task. This combination means that anything which uses the observer can create it, hold on to a reference of it, and then drop it when done, and not have to do anything else with the observer object.
|
/// The LocalObserver starts a task which listens for notifications during the constructor. When the observer goes out of scope, it will make a point of aborting the task. This combination means that anything which uses the observer can create it, hold on to a reference of it, and then drop it when done, and not have to do anything else with the observer object.
|
||||||
|
|
|
@ -3,8 +3,9 @@ use kifu_core::{Config, ConfigOption, Core, CoreRequest, CoreResponse, DatabaseP
|
||||||
use kifu_gtk::{
|
use kifu_gtk::{
|
||||||
perftrace,
|
perftrace,
|
||||||
ui::{AppWindow, ConfigurationPage, Home, PlayingField},
|
ui::{AppWindow, ConfigurationPage, Home, PlayingField},
|
||||||
CoreApi,
|
AppContext, CoreApi,
|
||||||
};
|
};
|
||||||
|
use l10n::NonEmptyList;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
const APP_ID_DEV: &str = "com.luminescent-dreams.kifu-gtk.dev";
|
const APP_ID_DEV: &str = "com.luminescent-dreams.kifu-gtk.dev";
|
||||||
|
@ -104,7 +105,9 @@ fn main() {
|
||||||
app.connect_activate({
|
app.connect_activate({
|
||||||
let runtime = runtime.clone();
|
let runtime = runtime.clone();
|
||||||
move |app| {
|
move |app| {
|
||||||
let app_window = AppWindow::new(app);
|
|
||||||
|
let ctx = AppContext::new().unwrap();
|
||||||
|
let app_window = AppWindow::new(app, &ctx);
|
||||||
|
|
||||||
let api = CoreApi {
|
let api = CoreApi {
|
||||||
rt: runtime.clone(),
|
rt: runtime.clone(),
|
||||||
|
|
|
@ -2,6 +2,9 @@ use adw::prelude::*;
|
||||||
use gio::resources_lookup_data;
|
use gio::resources_lookup_data;
|
||||||
use glib::IsA;
|
use glib::IsA;
|
||||||
use gtk::STYLE_PROVIDER_PRIORITY_USER;
|
use gtk::STYLE_PROVIDER_PRIORITY_USER;
|
||||||
|
use l10n::{L10N, NonEmptyList};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use crate::{ProvidesL10N, messages};
|
||||||
|
|
||||||
mod chat;
|
mod chat;
|
||||||
pub use chat::Chat;
|
pub use chat::Chat;
|
||||||
|
@ -37,7 +40,7 @@ pub struct AppWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppWindow {
|
impl AppWindow {
|
||||||
pub fn new(app: &adw::Application) -> Self {
|
pub fn new(app: &adw::Application, ctx: &impl ProvidesL10N) -> Self {
|
||||||
let window = adw::ApplicationWindow::builder()
|
let window = adw::ApplicationWindow::builder()
|
||||||
.application(app)
|
.application(app)
|
||||||
.width_request(800)
|
.width_request(800)
|
||||||
|
@ -76,7 +79,7 @@ impl AppWindow {
|
||||||
|
|
||||||
let content = adw::Bin::builder().css_classes(vec!["content"]).build();
|
let content = adw::Bin::builder().css_classes(vec!["content"]).build();
|
||||||
content.set_child(Some(
|
content.set_child(Some(
|
||||||
&adw::StatusPage::builder().title("Nothing here").build(),
|
&adw::StatusPage::builder().title(ctx.l10n().tr(messages::NothingHere)).build(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let layout = gtk::Box::builder()
|
let layout = gtk::Box::builder()
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
[package]
|
||||||
|
name = "l10n"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono-tz = { version = "0.8" }
|
||||||
|
chrono = { version = "0.4" }
|
||||||
|
convert_case = { version = "0.6" }
|
||||||
|
fixed_decimal = { version = "0.5.5", features = [ "ryu" ] }
|
||||||
|
fluent-ergonomics = { path = "../fluent-ergonomics" }
|
||||||
|
fluent = { version = "0.16" }
|
||||||
|
icu_locid = { version = "1" }
|
||||||
|
icu_provider = { version = "1" }
|
||||||
|
icu = { version = "1" }
|
||||||
|
intl-memoizer = { version = "*" }
|
||||||
|
serde = { version = "1", features = [ "derive" ] }
|
||||||
|
serde_yaml = { version = "0.9" }
|
||||||
|
sys-locale = { version = "0.3" }
|
||||||
|
thiserror = { version = "1" }
|
||||||
|
unic-langid = { version = "*" }
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "codegen-rust"
|
||||||
|
src = "src/bin/codegen-rust.rs"
|
|
@ -0,0 +1,164 @@
|
||||||
|
use convert_case::{Case, Casing};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_yaml;
|
||||||
|
use std::{collections::HashMap, fmt, fs::File, env};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
struct MessageJS {
|
||||||
|
#[serde(default)]
|
||||||
|
parameters: HashMap<String, String>,
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
enum Ty {
|
||||||
|
String,
|
||||||
|
Number,
|
||||||
|
Count,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Ty {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
|
match self {
|
||||||
|
Ty::String => write!(f, "String")?,
|
||||||
|
Ty::Number => write!(f, "i32")?,
|
||||||
|
Ty::Count => write!(f, "usize")?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
struct Parameter {
|
||||||
|
name: String,
|
||||||
|
ty: Ty,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Parameter {
|
||||||
|
fn from(val: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
name: "Nothing".to_owned(),
|
||||||
|
ty: Ty::String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(String, String)> for Parameter {
|
||||||
|
fn from((name, val): (String, String)) -> Self {
|
||||||
|
let ty = match val.as_ref() {
|
||||||
|
"string" => Ty::String,
|
||||||
|
"number" => Ty::Number,
|
||||||
|
"count" => Ty::Count,
|
||||||
|
_ => panic!("invalid value"),
|
||||||
|
};
|
||||||
|
Self { name, ty }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Message {
|
||||||
|
name: String,
|
||||||
|
parameters: Vec<Parameter>,
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message {
|
||||||
|
fn from_js(name: String, source: MessageJS) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
parameters: source
|
||||||
|
.parameters
|
||||||
|
.into_iter()
|
||||||
|
.map(|param| Parameter::from(param))
|
||||||
|
.collect(),
|
||||||
|
content: source.content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rust_code(&self) -> String {
|
||||||
|
if self.parameters.is_empty() {
|
||||||
|
let mut struct_strs = vec![];
|
||||||
|
|
||||||
|
struct_strs.push(format!("pub struct {}; ", self.name.to_case(Case::Pascal)));
|
||||||
|
struct_strs.push(format!("impl Message for {} {{
|
||||||
|
fn msgid(&self) -> &str {{
|
||||||
|
\"{}\"
|
||||||
|
}}
|
||||||
|
|
||||||
|
fn args(&self) -> Option<FluentArgs> {{
|
||||||
|
None
|
||||||
|
}}
|
||||||
|
}}",
|
||||||
|
self.name.to_case(Case::Pascal),
|
||||||
|
self.name,
|
||||||
|
));
|
||||||
|
|
||||||
|
struct_strs.join("\n")
|
||||||
|
} else {
|
||||||
|
let mut struct_strs = vec![];
|
||||||
|
|
||||||
|
let parameters: String = self
|
||||||
|
.parameters
|
||||||
|
.iter()
|
||||||
|
.map(|param| format!(" {}: {},", param.name, param.ty))
|
||||||
|
.collect::<Vec<String>>().join("\n");
|
||||||
|
|
||||||
|
struct_strs.push(format!("pub struct {} {{
|
||||||
|
{}
|
||||||
|
}}", self.name.to_case(Case::Pascal),
|
||||||
|
parameters));
|
||||||
|
|
||||||
|
struct_strs.push("".to_owned());
|
||||||
|
|
||||||
|
let parameters: String = self
|
||||||
|
.parameters
|
||||||
|
.iter()
|
||||||
|
.map(|param| format!(" args.set(\"{}\", self.{}.clone());", param.name, param.name))
|
||||||
|
.collect::<Vec<String>>().join("\n");
|
||||||
|
|
||||||
|
struct_strs.push(format!(
|
||||||
|
"impl Message for {} {{
|
||||||
|
fn msgid(&self) -> &str {{
|
||||||
|
\"{}\"
|
||||||
|
}}
|
||||||
|
|
||||||
|
fn args(&self) -> Option<FluentArgs> {{
|
||||||
|
let mut args = FluentArgs::new();
|
||||||
|
{}
|
||||||
|
Some(args)
|
||||||
|
}}
|
||||||
|
}}",
|
||||||
|
self.name.to_case(Case::Pascal),
|
||||||
|
self.name,
|
||||||
|
parameters,
|
||||||
|
));
|
||||||
|
|
||||||
|
struct_strs.join("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn content(&self) -> String {
|
||||||
|
format!("{} = {}", self.name, self.content.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut args = env::args();
|
||||||
|
let _ = args.next();
|
||||||
|
let input_file = args.next().unwrap();
|
||||||
|
let messages: HashMap<String, MessageJS> =
|
||||||
|
serde_yaml::from_reader(File::open(input_file).unwrap()).unwrap();
|
||||||
|
|
||||||
|
println!("// This file was autogenerated from l10n-codegen-rust. Edits will be lost
|
||||||
|
// on next generation.");
|
||||||
|
println!("use l10n::Message;");
|
||||||
|
println!("use fluent::FluentArgs;");
|
||||||
|
|
||||||
|
let messages = messages
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, msg)| Message::from_js(name, msg));
|
||||||
|
for message in messages {
|
||||||
|
println!();
|
||||||
|
println!("{}", message.rust_code());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
use std::{collections::HashMap, fs::File, env};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_yaml;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
struct MessageJS {
|
||||||
|
#[serde(default)]
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut args = env::args();
|
||||||
|
let _ = args.next();
|
||||||
|
let input_file = args.next().unwrap();
|
||||||
|
let messages: HashMap<String, MessageJS> =
|
||||||
|
serde_yaml::from_reader(File::open(input_file).unwrap()).unwrap();
|
||||||
|
|
||||||
|
for (name, message) in messages {
|
||||||
|
println!("{} = {}", name, message.content);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,547 @@
|
||||||
|
use chrono::{Datelike, NaiveDate, Timelike};
|
||||||
|
use chrono_tz::Tz;
|
||||||
|
use fixed_decimal::FixedDecimal;
|
||||||
|
use fluent::{bundle::FluentBundle, FluentResource};
|
||||||
|
use icu::{datetime::options::length, decimal::FixedDecimalFormatter, locid::Locale};
|
||||||
|
use icu_provider::DataLocale;
|
||||||
|
use std::{fs::File, io::Read, ops::Deref};
|
||||||
|
use sys_locale::get_locale;
|
||||||
|
use thiserror::Error;
|
||||||
|
use unic_langid::LanguageIdentifierError;
|
||||||
|
|
||||||
|
// Re-exports. I'm doing these so that clients of this library don't have to go tracking down
|
||||||
|
// additional structures
|
||||||
|
pub use fixed_decimal::FloatPrecision;
|
||||||
|
pub use fluent::{FluentArgs, FluentValue};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum NonEmptyListError {
|
||||||
|
BuildFromEmptyContainer,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NonEmptyList<A>(Vec<A>);
|
||||||
|
|
||||||
|
impl<A> NonEmptyList<A> {
|
||||||
|
pub fn new(elem: A) -> Self {
|
||||||
|
Self(vec![elem])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_iter(
|
||||||
|
iter: impl IntoIterator<Item = A>,
|
||||||
|
) -> Result<NonEmptyList<A>, NonEmptyListError> {
|
||||||
|
let lst = iter.into_iter().collect::<Vec<A>>();
|
||||||
|
if lst.len() > 0 {
|
||||||
|
Ok(NonEmptyList(lst))
|
||||||
|
} else {
|
||||||
|
Err(NonEmptyListError::BuildFromEmptyContainer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, item: A) {
|
||||||
|
self.0.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find(&self, f: impl Fn(&A) -> bool) -> Option<&A> {
|
||||||
|
self.0.iter().find(|item| f(*item))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first(&self) -> &A {
|
||||||
|
&self.0[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter<'a>(&'a self) -> impl Iterator<Item = &'a A> {
|
||||||
|
self.0.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A> Deref for NonEmptyList<A> {
|
||||||
|
type Target = Vec<A>;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum L10NError {
|
||||||
|
#[error("Unparsable Locale")]
|
||||||
|
UnparsableLocale,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<icu::locid::Error> for L10NError {
|
||||||
|
fn from(_: icu::locid::Error) -> L10NError {
|
||||||
|
L10NError::UnparsableLocale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum FileLoadError {
|
||||||
|
#[error("Unparsable Locale")]
|
||||||
|
UnparsableLocale,
|
||||||
|
|
||||||
|
#[error("Source string file not found")]
|
||||||
|
FileNotFound,
|
||||||
|
|
||||||
|
#[error("The Fluent file is malformed")]
|
||||||
|
FluentParseError(String),
|
||||||
|
|
||||||
|
#[error("An unknown IO error was found")]
|
||||||
|
IOError(std::io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LanguageIdentifierError> for FileLoadError {
|
||||||
|
fn from(_: LanguageIdentifierError) -> Self {
|
||||||
|
Self::UnparsableLocale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for FileLoadError {
|
||||||
|
fn from(err: std::io::Error) -> Self {
|
||||||
|
Self::IOError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Potential Message structure.
|
||||||
|
//
|
||||||
|
// Let's assume the application has an enumeration that implements Message. For each element of the
|
||||||
|
// enumeration, there should be some boilerplate code that returns the message ID and the arguments
|
||||||
|
// as a FluentArgs.
|
||||||
|
//
|
||||||
|
// Nobody wants to generate all of that code, though I have done so in the past, and manually
|
||||||
|
// generating that code could be useful for illustration. I think I'm going to want to do code
|
||||||
|
// generation from the source strings file, and then compile the enumeration into the code.
|
||||||
|
// However, I have not found a mechanism in Fluent to identify all of the placeholders within a
|
||||||
|
// message, so I'm not even sure that I can automate this code generation.
|
||||||
|
pub trait Message {
|
||||||
|
fn msgid(&self) -> &str;
|
||||||
|
fn args(&self) -> Option<FluentArgs>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct L10N {
|
||||||
|
messages_root: std::path::PathBuf,
|
||||||
|
message_bundles: Vec<FluentBundle<FluentResource, intl_memoizer::concurrent::IntlLangMemoizer>>,
|
||||||
|
|
||||||
|
locales: NonEmptyList<Locale>,
|
||||||
|
zone: chrono_tz::Tz,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl L10N {
|
||||||
|
pub fn new(messages_root: std::path::PathBuf) -> Self {
|
||||||
|
let english = "en-US".parse::<Locale>().unwrap();
|
||||||
|
let sys_locale = get_locale()
|
||||||
|
.and_then(|locale_str| locale_str.parse::<Locale>().ok())
|
||||||
|
.unwrap_or(english.clone());
|
||||||
|
let locales = NonEmptyList::new(sys_locale.clone());
|
||||||
|
let zone = chrono_tz::UTC;
|
||||||
|
|
||||||
|
/*
|
||||||
|
let mut source_message_path = messages_root.clone();
|
||||||
|
source_message_path.push("en-US.ftl");
|
||||||
|
let english_phrases = FluentResource::try_new
|
||||||
|
*/
|
||||||
|
|
||||||
|
let mut s = Self {
|
||||||
|
messages_root,
|
||||||
|
message_bundles: vec![],
|
||||||
|
locales,
|
||||||
|
zone,
|
||||||
|
};
|
||||||
|
|
||||||
|
s.load_messages_from_file("en-US".to_owned()).unwrap();
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_messages_from_file(&mut self, locale: String) -> Result<(), FileLoadError> {
|
||||||
|
let langid: unic_langid::LanguageIdentifier = locale.parse()?;
|
||||||
|
|
||||||
|
let mut path = self.messages_root.clone();
|
||||||
|
path.push(locale);
|
||||||
|
path.set_extension("ftl");
|
||||||
|
println!("{:?}", path);
|
||||||
|
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
let mut f = File::open(path)?;
|
||||||
|
f.read_to_end(&mut buffer)?;
|
||||||
|
let text = String::from_utf8(buffer).unwrap();
|
||||||
|
match FluentResource::try_new(text) {
|
||||||
|
Ok(resource) => {
|
||||||
|
let mut bundle = FluentBundle::new_concurrent(vec![langid]);
|
||||||
|
let _ = bundle.add_resource(resource);
|
||||||
|
self.message_bundles.push(bundle);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err((_, errors)) => Err(FileLoadError::FluentParseError(
|
||||||
|
errors
|
||||||
|
.into_iter()
|
||||||
|
.map(|err| err.to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n"),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, whenever the user changes the locales, the list of messages has to change. How do we
|
||||||
|
// automatically set up the messages? Theoretically they all need to be reloaded, and I've
|
||||||
|
// already split how the messages get loaded from how the locales are specified.
|
||||||
|
//
|
||||||
|
// But, FluentErgo does that, too. It already has the concept of being constructed with a list
|
||||||
|
// of languages and then having each language bundle manually loaded afterwards.
|
||||||
|
//
|
||||||
|
// Problem: be able to change the preferred list of locales and automatically have a new
|
||||||
|
// FluentBundle which has all relevant translations loaded.
|
||||||
|
//
|
||||||
|
// One solution is that all bundles get loaded at startup time, and the bundle list gets
|
||||||
|
// changed any time the list of locales gets changed. Also, the system can just run through the
|
||||||
|
// entire list of fallbacks.
|
||||||
|
pub fn set_locales(&mut self, locales: NonEmptyList<&str>) -> Result<(), L10NError> {
|
||||||
|
let locales = locales
|
||||||
|
.iter()
|
||||||
|
.map(|locale| Locale::try_from_bytes(locale.as_bytes()))
|
||||||
|
.collect::<Result<Vec<Locale>, icu::locid::Error>>()?;
|
||||||
|
|
||||||
|
for locale in locales.iter() {
|
||||||
|
self.load_messages_from_file(locale.to_string()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.locales = NonEmptyList(locales);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_timezone(&mut self, zone: Tz) {
|
||||||
|
self.zone = zone;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to take a message and turn it into a string in the current language. Except I don't
|
||||||
|
// know yet what form the message should take. Forming an adapter around fluent_ergonomics or
|
||||||
|
// even around fluent itself. I would want for the message to be statically typed, but then I
|
||||||
|
// don't know what can be the data type that gets passed in here.
|
||||||
|
//
|
||||||
|
// Formatting a message requires identifying the message and passing it any relevant
|
||||||
|
// parameters. In an ideal world, neither of these can be incorrect. Messages are all checked
|
||||||
|
// at compile time, as are their parameters. That implies an enumeration, with one element per
|
||||||
|
// message, and with each element knowing its parameters.
|
||||||
|
// pub fn messages(&self) -> Vec<FluentBundle<FluentResource>> {
|
||||||
|
// self.message_bundles.clone()
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn tr(&self, message: impl Message) -> String {
|
||||||
|
for bundle in self.message_bundles.iter().rev() {
|
||||||
|
let msg = bundle
|
||||||
|
.get_message(message.msgid())
|
||||||
|
.and_then(|msg| msg.value());
|
||||||
|
match msg {
|
||||||
|
Some(msg) => {
|
||||||
|
let mut errors = vec![];
|
||||||
|
return self.message_bundles[0]
|
||||||
|
.format_pattern(msg, message.args().as_ref(), &mut errors)
|
||||||
|
.to_string();
|
||||||
|
}
|
||||||
|
None => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable!("The message {} is missing", message.msgid());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format_date_time_utc(
|
||||||
|
&self,
|
||||||
|
time: DateTime,
|
||||||
|
date_style: length::Date,
|
||||||
|
time_style: length::Time,
|
||||||
|
) -> String {
|
||||||
|
let time: DateTime = time.with_timezone(&chrono_tz::UTC).into();
|
||||||
|
let options = length::Bag::from_date_time_style(date_style, time_style);
|
||||||
|
let formatter = icu::datetime::DateTimeFormatter::try_new(
|
||||||
|
&DataLocale::from(self.locales.first()),
|
||||||
|
options.into(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let icu_time: icu::calendar::DateTime<icu::calendar::Gregorian> = time.into();
|
||||||
|
formatter.format_to_string(&icu_time.to_any()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format_date_time_local(
|
||||||
|
&self,
|
||||||
|
time: DateTime,
|
||||||
|
date_style: length::Date,
|
||||||
|
time_style: length::Time,
|
||||||
|
) -> String {
|
||||||
|
let time: DateTime = time.with_timezone(&self.zone).into();
|
||||||
|
let options = length::Bag::from_date_time_style(date_style, time_style);
|
||||||
|
let formatter = icu::datetime::DateTimeFormatter::try_new(
|
||||||
|
&DataLocale::from(self.locales.first()),
|
||||||
|
options.into(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let icu_time: icu::calendar::DateTime<icu::calendar::Gregorian> = time.into();
|
||||||
|
formatter.format_to_string(&icu_time.to_any()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* I have been unable to get from a chrono_tz::Tz to an ICU timezone. I have tried a variety of
|
||||||
|
* parsers on the CustomTimeZone object. I have not researched the data provider to see what is
|
||||||
|
* available there. The ZoneID for the reference date is US/Mountain, and the abbreviation is
|
||||||
|
* MST. I'll want to get to a CustomTimeZone so that the formatter can render MST or Mountain
|
||||||
|
* Standard Time or something similar.
|
||||||
|
fn format_date_time_tz(
|
||||||
|
&self,
|
||||||
|
time: DateTime,
|
||||||
|
date_style: length::Date,
|
||||||
|
time_style: length::Time,
|
||||||
|
) -> String {
|
||||||
|
let options = length::Bag::from_date_time_style(date_style, time_style);
|
||||||
|
let formatter = icu::datetime::ZonedDateTimeFormatter::try_new(
|
||||||
|
&DataLocale::from(&self.locale),
|
||||||
|
options.into(),
|
||||||
|
Default::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let icu_time: icu::calendar::DateTime<icu::calendar::Gregorian> = time.into();
|
||||||
|
let any = icu_time.to_any();
|
||||||
|
|
||||||
|
println!("{:?}", time.offset());
|
||||||
|
|
||||||
|
let zone_id: String = time.offset().abbreviation().to_owned();
|
||||||
|
println!("{:?}", zone_id);
|
||||||
|
let zone_id = icu::timezone::TimeZoneBcp47Id::from_str(&zone_id).unwrap();
|
||||||
|
|
||||||
|
let zone: CustomTimeZone = CustomTimeZone {
|
||||||
|
gmt_offset: None,
|
||||||
|
time_zone_id: Some(zone_id),
|
||||||
|
/*
|
||||||
|
icu::timezone::TimeZoneBcp47Id::from_str(time.offset().tz_id().parse().unwrap())
|
||||||
|
.unwrap(),
|
||||||
|
*/
|
||||||
|
metazone_id: None,
|
||||||
|
zone_variant: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
formatter.format_to_string(&any, &zone).unwrap()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub fn format_date(&self, date: NaiveDate, date_style: length::Date) -> String {
|
||||||
|
let formatter = icu::datetime::DateFormatter::try_new_with_length(
|
||||||
|
&DataLocale::from(self.locales.first()),
|
||||||
|
date_style,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let icu_date: icu::calendar::Date<icu::calendar::Gregorian> =
|
||||||
|
icu::calendar::Date::try_new_gregorian_date(
|
||||||
|
date.year(),
|
||||||
|
date.month().try_into().unwrap(),
|
||||||
|
date.day().try_into().unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
formatter.format_to_string(&icu_date.to_any()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format_f64(&self, value: f64, precision: FloatPrecision) -> String {
|
||||||
|
let fdf = FixedDecimalFormatter::try_new(
|
||||||
|
&self.locales.first().clone().into(),
|
||||||
|
Default::default(),
|
||||||
|
)
|
||||||
|
.expect("locale should be present");
|
||||||
|
|
||||||
|
let number = FixedDecimal::try_from_f64(value, precision).unwrap();
|
||||||
|
|
||||||
|
fdf.format_to_string(&FixedDecimal::try_from_f64(value, precision).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct DateTime(chrono::DateTime<Tz>);
|
||||||
|
|
||||||
|
impl Deref for DateTime {
|
||||||
|
type Target = chrono::DateTime<Tz>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<chrono::DateTime<Tz>> for DateTime {
|
||||||
|
fn from(time: chrono::DateTime<Tz>) -> Self {
|
||||||
|
Self(time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DateTime> for icu::calendar::DateTime<icu::calendar::Gregorian> {
|
||||||
|
fn from(time: DateTime) -> Self {
|
||||||
|
// SAFETY: these unwraps should be safe since chrono dates are already valid Gregorian
|
||||||
|
// dates
|
||||||
|
icu::calendar::DateTime::try_new_gregorian_datetime(
|
||||||
|
time.year(),
|
||||||
|
time.month().try_into().unwrap(),
|
||||||
|
time.day().try_into().unwrap(),
|
||||||
|
time.hour().try_into().unwrap(),
|
||||||
|
time.minute().try_into().unwrap(),
|
||||||
|
time.second().try_into().unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use fluent::fluent_args;
|
||||||
|
|
||||||
|
fn ref_l10n() -> L10N {
|
||||||
|
let mut l10n = L10N::new(std::path::PathBuf::from("./test_files"));
|
||||||
|
// Make sure we know the locale before the test begins. Some systems, such as my own, are
|
||||||
|
// not actually in English.
|
||||||
|
l10n.set_locales(NonEmptyList::from_iter(vec!["en-US"]).unwrap());
|
||||||
|
l10n.set_timezone(chrono_tz::US::Eastern);
|
||||||
|
l10n
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ref_date() -> NaiveDate {
|
||||||
|
NaiveDate::from_ymd_opt(2006, 1, 2).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ref_time() -> DateTime {
|
||||||
|
NaiveDate::from_ymd_opt(2006, 1, 2)
|
||||||
|
.unwrap()
|
||||||
|
.and_hms_opt(3, 4, 5)
|
||||||
|
.unwrap()
|
||||||
|
.and_local_timezone(Tz::US__Mountain)
|
||||||
|
.unwrap()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_formats_a_time_in_utc() {
|
||||||
|
let mut l10n = ref_l10n();
|
||||||
|
let now = ref_time();
|
||||||
|
|
||||||
|
// 202f is the code-point for a narrow non-breaking space. Presumably this is used in
|
||||||
|
// particular to ensure that the am/pm marker doesn't get split off from the time
|
||||||
|
assert_eq!(
|
||||||
|
l10n.format_date_time_utc(now.clone(), length::Date::Long, length::Time::Medium),
|
||||||
|
"January 2, 2006, 10:04:05\u{202f}AM"
|
||||||
|
);
|
||||||
|
|
||||||
|
l10n.set_locales(NonEmptyList::from_iter(vec!["eo-EO", "en-US"]).unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
l10n.format_date_time_utc(now.clone(), length::Date::Long, length::Time::Medium),
|
||||||
|
"2006-Januaro-02 10:04:05"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_formats_a_time_in_the_current_zone() {
|
||||||
|
let mut l10n = ref_l10n();
|
||||||
|
let now = ref_time();
|
||||||
|
|
||||||
|
// 202f is the code-point for a narrow non-breaking space. Presumably this is used in
|
||||||
|
// particular to ensure that the am/pm marker doesn't get split off from the time
|
||||||
|
assert_eq!(
|
||||||
|
l10n.format_date_time_local(now.clone(), length::Date::Long, length::Time::Medium),
|
||||||
|
"January 2, 2006, 5:04:05\u{202f}AM"
|
||||||
|
);
|
||||||
|
|
||||||
|
l10n.set_locales(NonEmptyList::from_iter(vec!["eo-EO", "en-US"]).unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
l10n.format_date_time_local(now.clone(), length::Date::Long, length::Time::Medium),
|
||||||
|
"2006-Januaro-02 05:04:05"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_formats_dates() {
|
||||||
|
let mut l10n = ref_l10n();
|
||||||
|
let today = ref_date();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
l10n.format_date(today.clone(), length::Date::Long),
|
||||||
|
"January 2, 2006"
|
||||||
|
);
|
||||||
|
|
||||||
|
l10n.set_locales(NonEmptyList::from_iter(vec!["eo-EO", "en-US"]).unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
l10n.format_date(today.clone(), length::Date::Long),
|
||||||
|
"2006-Januaro-02"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_formats_a_number_according_to_locale() {
|
||||||
|
let mut l10n = ref_l10n();
|
||||||
|
|
||||||
|
assert_eq!(l10n.format_f64(100.4, FloatPrecision::Floating), "100.4",);
|
||||||
|
assert_eq!(
|
||||||
|
l10n.format_f64(15000.4, FloatPrecision::Floating),
|
||||||
|
"15,000.4",
|
||||||
|
);
|
||||||
|
|
||||||
|
l10n.set_locales(NonEmptyList::from_iter(vec!["de-DE", "en-US"]).unwrap());
|
||||||
|
assert_eq!(l10n.format_f64(100.4, FloatPrecision::Floating), "100,4",);
|
||||||
|
assert_eq!(
|
||||||
|
l10n.format_f64(15000.4, FloatPrecision::Floating),
|
||||||
|
"15.000,4",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_can_load_message_files() {
|
||||||
|
let mut l10n = ref_l10n();
|
||||||
|
let messages = l10n.messages();
|
||||||
|
|
||||||
|
let args = fluent_args![
|
||||||
|
"name" => "Savanni"
|
||||||
|
];
|
||||||
|
assert_eq!(
|
||||||
|
messages.tr("welcome", Some(&args)).unwrap(),
|
||||||
|
"Hello, Savanni"
|
||||||
|
);
|
||||||
|
|
||||||
|
let args = fluent_args![
|
||||||
|
"count" => 1
|
||||||
|
];
|
||||||
|
assert_eq!(
|
||||||
|
messages.tr("games-in-database", Some(&args)).unwrap(),
|
||||||
|
"There is one game in the database"
|
||||||
|
);
|
||||||
|
|
||||||
|
let args = fluent_args![
|
||||||
|
"count" => 2
|
||||||
|
];
|
||||||
|
assert_eq!(
|
||||||
|
messages.tr("games-in-database", Some(&args)).unwrap(),
|
||||||
|
"There are 2 games in the database"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
#[test]
|
||||||
|
fn it_can_change_languages_on_locale_change() {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn phrases_can_be_translated() {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn phrases_can_fall_back() {
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Not really a unit test, more of a test to see what I could introspect within a fluent
|
||||||
|
* message. I was hoping that attributes would give me placeholder names, but that doesn't seem
|
||||||
|
* to be the case.
|
||||||
|
#[test]
|
||||||
|
fn messages() {
|
||||||
|
let langid_en = "en-US".parse().expect("Parsing failed.");
|
||||||
|
let resource = FluentResource::try_new(MESSAGES.to_owned()).unwrap();
|
||||||
|
let mut bundle = FluentBundle::new(vec![langid_en]);
|
||||||
|
bundle.add_resource(&resource).unwrap();
|
||||||
|
|
||||||
|
let msg = bundle.get_message("welcome").expect("message should exist");
|
||||||
|
for attr in msg.attributes() {
|
||||||
|
println!("attr: {:?}", attr);
|
||||||
|
}
|
||||||
|
assert!(false);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
welcome = Hello, {$name}
|
||||||
|
games-in-database = {$count ->
|
||||||
|
[one] There is one game in the database
|
||||||
|
*[other] There are {$count} games in the database
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "xdg-test"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
|
@ -0,0 +1,7 @@
|
||||||
|
{ wrapGAppsHook4 }:
|
||||||
|
attrs:
|
||||||
|
let
|
||||||
|
gsettingsDir = "${attrs.crateName}-${attrs.version}";
|
||||||
|
in {
|
||||||
|
nativeBuildInputs = [ wrapGAppsHook4 ];
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let data_dirs = env::var("XDG_DATA_DIRS");
|
||||||
|
|
||||||
|
println!("{:?}", data_dirs);
|
||||||
|
}
|
Loading…
Reference in New Issue