From 990a1e43add22e0e1cc723ddaa76af254d862432 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 21 Jun 2026 23:35:33 +0800 Subject: [PATCH 01/71] feat!: rewrite getter as package-centric CLI core Replace the old hub-app centered implementation with the reusable rewrite spine for the UpgradeAll architecture. Add split core/storage/CLI crates, Lua repository evaluation, SQLite main/cache storage, repository overlay behavior, and legacy JSON bridge-bundle import coverage. --- Cargo.lock | 2555 +++++------------ Cargo.toml | 65 +- README.md | 26 +- crates/getter-cli/Cargo.toml | 21 + crates/getter-cli/src/lib.rs | 817 ++++++ crates/getter-cli/src/main.rs | 10 + crates/getter-cli/tests/bdd_cli.rs | 486 ++++ .../tests/features/cli/app_list.feature | 8 + .../tests/features/cli/hub_list.feature | 8 + .../tests/features/cli/init.feature | 8 + .../legacy_import_room_bundle_failure.feature | 18 + .../tests/features/cli/repo_list.feature | 8 + .../features/cli/repository_overlay.feature | 14 + .../cli/repository_package_eval.feature | 17 + .../features/cli/storage_validate.feature | 8 + crates/getter-core/Cargo.toml | 14 + crates/getter-core/src/lib.rs | 408 +++ crates/getter-core/src/lua.rs | 460 +++ crates/getter-core/src/repository.rs | 288 ++ crates/getter-downloader/Cargo.toml | 7 + crates/getter-downloader/src/lib.rs | 3 + crates/getter-ffi/Cargo.toml | 7 + crates/getter-ffi/src/lib.rs | 3 + crates/getter-plugin-api/Cargo.toml | 7 + crates/getter-plugin-api/src/lib.rs | 3 + crates/getter-providers/Cargo.toml | 7 + crates/getter-providers/src/lib.rs | 3 + crates/getter-rpc/Cargo.toml | 7 + crates/getter-rpc/src/lib.rs | 3 + crates/getter-storage/Cargo.toml | 12 + crates/getter-storage/src/legacy_room.rs | 215 ++ crates/getter-storage/src/lib.rs | 491 ++++ src/cache.rs | 34 - src/cache/local.rs | 77 - src/cache/manager.rs | 265 -- src/cache/moka.rs | 0 src/core.rs | 1 - src/core/config.rs | 3 - src/core/config/data.rs | 1 - src/core/config/data/rule_list.rs | 89 - src/core/config/utils.rs | 41 - src/core/config/world.rs | 35 - src/core/config/world/local_repo.rs | 31 - src/core/config/world/world_config_wrapper.rs | 71 - src/core/config/world/world_list.rs | 100 - src/database/mod.rs | 229 -- src/database/models/app.rs | 101 - src/database/models/extra_app.rs | 48 - src/database/models/extra_hub.rs | 56 - src/database/models/hub.rs | 80 - src/database/models/mod.rs | 4 - src/database/store.rs | 266 -- src/downloader/config.rs | 199 -- src/downloader/error.rs | 95 - src/downloader/external_rpc_impl.rs | 290 -- src/downloader/hub_dispatch.rs | 208 -- src/downloader/mod.rs | 58 - src/downloader/state.rs | 532 ---- src/downloader/task_manager.rs | 1009 ------- src/downloader/traits.rs | 262 -- src/downloader/trauma_impl.rs | 751 ----- src/error.rs | 82 - src/lib.rs | 76 +- src/locale.rs | 64 - src/main.rs | 16 +- src/manager/android_api.rs | 104 - src/manager/app_manager.rs | 462 --- src/manager/app_status.rs | 18 - src/manager/auto_template.rs | 206 -- src/manager/cloud_config_getter.rs | 403 --- src/manager/data_getter.rs | 99 - src/manager/hub_manager.rs | 173 -- src/manager/mod.rs | 12 - src/manager/notification.rs | 152 - src/manager/updater.rs | 151 - src/manager/url_replace.rs | 170 -- src/manager/version.rs | 136 - src/manager/version_map.rs | 192 -- src/rpc.rs | 3 - src/rpc/client.rs | 77 - src/rpc/data.rs | 411 --- src/rpc/server.rs | 1255 -------- src/utils.rs | 6 - src/utils/convert.rs | 21 - src/utils/http.rs | 327 --- src/utils/instance.rs | 18 - src/utils/json.rs | 31 - src/utils/time.rs | 8 - src/utils/versioning.rs | 170 -- src/websdk.rs | 2 - src/websdk/cloud_rules.rs | 3 - src/websdk/cloud_rules/cloud_rules_manager.rs | 112 - src/websdk/cloud_rules/cloud_rules_wrapper.rs | 109 - src/websdk/cloud_rules/data.rs | 3 - src/websdk/cloud_rules/data/app_item.rs | 94 - src/websdk/cloud_rules/data/config_list.rs | 80 - src/websdk/cloud_rules/data/hub_item.rs | 123 - src/websdk/repo.rs | 3 - src/websdk/repo/api.rs | 166 -- src/websdk/repo/controller.rs | 121 - src/websdk/repo/data.rs | 1 - src/websdk/repo/data/release.rs | 17 - src/websdk/repo/provider.rs | 99 - src/websdk/repo/provider/base_provider.rs | 427 --- src/websdk/repo/provider/fdroid.rs | 356 --- src/websdk/repo/provider/github.rs | 262 -- src/websdk/repo/provider/gitlab.rs | 327 --- src/websdk/repo/provider/lsposed_repo.rs | 224 -- src/websdk/repo/provider/outside_rpc.rs | 115 - 109 files changed, 4237 insertions(+), 14193 deletions(-) create mode 100644 crates/getter-cli/Cargo.toml create mode 100644 crates/getter-cli/src/lib.rs create mode 100644 crates/getter-cli/src/main.rs create mode 100644 crates/getter-cli/tests/bdd_cli.rs create mode 100644 crates/getter-cli/tests/features/cli/app_list.feature create mode 100644 crates/getter-cli/tests/features/cli/hub_list.feature create mode 100644 crates/getter-cli/tests/features/cli/init.feature create mode 100644 crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature create mode 100644 crates/getter-cli/tests/features/cli/repo_list.feature create mode 100644 crates/getter-cli/tests/features/cli/repository_overlay.feature create mode 100644 crates/getter-cli/tests/features/cli/repository_package_eval.feature create mode 100644 crates/getter-cli/tests/features/cli/storage_validate.feature create mode 100644 crates/getter-core/Cargo.toml create mode 100644 crates/getter-core/src/lib.rs create mode 100644 crates/getter-core/src/lua.rs create mode 100644 crates/getter-core/src/repository.rs create mode 100644 crates/getter-downloader/Cargo.toml create mode 100644 crates/getter-downloader/src/lib.rs create mode 100644 crates/getter-ffi/Cargo.toml create mode 100644 crates/getter-ffi/src/lib.rs create mode 100644 crates/getter-plugin-api/Cargo.toml create mode 100644 crates/getter-plugin-api/src/lib.rs create mode 100644 crates/getter-providers/Cargo.toml create mode 100644 crates/getter-providers/src/lib.rs create mode 100644 crates/getter-rpc/Cargo.toml create mode 100644 crates/getter-rpc/src/lib.rs create mode 100644 crates/getter-storage/Cargo.toml create mode 100644 crates/getter-storage/src/legacy_room.rs create mode 100644 crates/getter-storage/src/lib.rs delete mode 100644 src/cache.rs delete mode 100644 src/cache/local.rs delete mode 100644 src/cache/manager.rs delete mode 100644 src/cache/moka.rs delete mode 100644 src/core.rs delete mode 100644 src/core/config.rs delete mode 100644 src/core/config/data.rs delete mode 100644 src/core/config/data/rule_list.rs delete mode 100644 src/core/config/utils.rs delete mode 100644 src/core/config/world.rs delete mode 100644 src/core/config/world/local_repo.rs delete mode 100644 src/core/config/world/world_config_wrapper.rs delete mode 100644 src/core/config/world/world_list.rs delete mode 100644 src/database/mod.rs delete mode 100644 src/database/models/app.rs delete mode 100644 src/database/models/extra_app.rs delete mode 100644 src/database/models/extra_hub.rs delete mode 100644 src/database/models/hub.rs delete mode 100644 src/database/models/mod.rs delete mode 100644 src/database/store.rs delete mode 100644 src/downloader/config.rs delete mode 100644 src/downloader/error.rs delete mode 100644 src/downloader/external_rpc_impl.rs delete mode 100644 src/downloader/hub_dispatch.rs delete mode 100644 src/downloader/mod.rs delete mode 100644 src/downloader/state.rs delete mode 100644 src/downloader/task_manager.rs delete mode 100644 src/downloader/traits.rs delete mode 100644 src/downloader/trauma_impl.rs delete mode 100644 src/error.rs delete mode 100644 src/locale.rs delete mode 100644 src/manager/android_api.rs delete mode 100644 src/manager/app_manager.rs delete mode 100644 src/manager/app_status.rs delete mode 100644 src/manager/auto_template.rs delete mode 100644 src/manager/cloud_config_getter.rs delete mode 100644 src/manager/data_getter.rs delete mode 100644 src/manager/hub_manager.rs delete mode 100644 src/manager/mod.rs delete mode 100644 src/manager/notification.rs delete mode 100644 src/manager/updater.rs delete mode 100644 src/manager/url_replace.rs delete mode 100644 src/manager/version.rs delete mode 100644 src/manager/version_map.rs delete mode 100644 src/rpc.rs delete mode 100644 src/rpc/client.rs delete mode 100644 src/rpc/data.rs delete mode 100644 src/rpc/server.rs delete mode 100644 src/utils.rs delete mode 100644 src/utils/convert.rs delete mode 100644 src/utils/http.rs delete mode 100644 src/utils/instance.rs delete mode 100644 src/utils/json.rs delete mode 100644 src/utils/time.rs delete mode 100644 src/utils/versioning.rs delete mode 100644 src/websdk.rs delete mode 100644 src/websdk/cloud_rules.rs delete mode 100644 src/websdk/cloud_rules/cloud_rules_manager.rs delete mode 100644 src/websdk/cloud_rules/cloud_rules_wrapper.rs delete mode 100644 src/websdk/cloud_rules/data.rs delete mode 100644 src/websdk/cloud_rules/data/app_item.rs delete mode 100644 src/websdk/cloud_rules/data/config_list.rs delete mode 100644 src/websdk/cloud_rules/data/hub_item.rs delete mode 100644 src/websdk/repo.rs delete mode 100644 src/websdk/repo/api.rs delete mode 100644 src/websdk/repo/controller.rs delete mode 100644 src/websdk/repo/data.rs delete mode 100644 src/websdk/repo/data/release.rs delete mode 100644 src/websdk/repo/provider.rs delete mode 100644 src/websdk/repo/provider/base_provider.rs delete mode 100644 src/websdk/repo/provider/fdroid.rs delete mode 100644 src/websdk/repo/provider/github.rs delete mode 100644 src/websdk/repo/provider/gitlab.rs delete mode 100644 src/websdk/repo/provider/lsposed_repo.rs delete mode 100644 src/websdk/repo/provider/outside_rpc.rs diff --git a/Cargo.lock b/Cargo.lock index 0b56aaf..5d73f4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -12,131 +24,102 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" - -[[package]] -name = "assert-json-diff" -version = "2.0.2" +name = "anstream" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ - "serde", - "serde_json", + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", ] [[package]] -name = "async-trait" -version = "0.1.89" +name = "anstyle" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] -name = "async_fn_traits" -version = "0.1.1" +name = "anstyle-parse" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc58d489c5f2d2c5be31b9004cec7af25a70d23df4d8111715eee736234cf217" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ - "paste", + "utf8parse", ] [[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "aws-lc-rs" -version = "1.15.2" +name = "anstyle-query" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "aws-lc-sys", - "zeroize", + "windows-sys 0.61.2", ] [[package]] -name = "aws-lc-sys" -version = "0.35.0" +name = "anstyle-wincon" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", ] [[package]] -name = "base64" -version = "0.22.1" +name = "anyhow" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] -name = "bindgen" -version = "0.72.1" +name = "autocfg" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "itertools", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", -] +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] -name = "block-buffer" -version = "0.10.4" +name = "bstr" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ - "generic-array", + "memchr", + "serde", ] [[package]] -name = "bumpalo" -version = "3.19.1" +name = "bytecount" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" [[package]] name = "cc" -version = "1.2.52" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", - "jobserver", - "libc", "shlex", ] @@ -146,15 +129,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.4" @@ -162,50 +136,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "cfg_aliases" -version = "0.2.1" +name = "clap" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] [[package]] -name = "chacha20" -version = "0.10.0" +name = "clap_builder" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", - "rand_core 0.10.0", + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", ] [[package]] -name = "clang-sys" -version = "1.8.1" +name = "clap_derive" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ - "glob", - "libc", - "libloading", + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "cmake" -version = "0.1.57" +name = "clap_lex" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" -dependencies = [ - "cc", -] +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] -name = "colored" -version = "3.0.0" +name = "colorchoice" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" -dependencies = [ - "windows-sys 0.52.0", -] +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "combine" @@ -217,6 +192,27 @@ dependencies = [ "memchr", ] +[[package]] +name = "console" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +dependencies = [ + "encode_unicode", + "libc", + "unicode-width", + "windows-sys 0.61.2", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.10.1" @@ -234,138 +230,167 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "cpufeatures" -version = "0.2.17" +name = "crossbeam-deque" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ - "libc", + "crossbeam-epoch", + "crossbeam-utils", ] [[package]] -name = "cpufeatures" -version = "0.3.0" +name = "crossbeam-epoch" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "libc", + "crossbeam-utils", ] [[package]] -name = "crypto-common" -version = "0.1.7" +name = "crossbeam-utils" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] -name = "digest" -version = "0.10.7" +name = "cucumber" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "96a87e18d925b19ebe0fd47ea45316abd216d81ec0879c2448c3f9a0e9da62be" dependencies = [ - "block-buffer", - "crypto-common", + "anyhow", + "clap", + "console", + "cucumber-codegen", + "cucumber-expressions", + "derive_more", + "either", + "futures", + "gherkin", + "globwalk", + "humantime", + "inventory", + "itertools", + "linked-hash-map", + "pin-project", + "ref-cast", + "regex", + "sealed", + "smart-default", ] [[package]] -name = "displaydoc" -version = "0.2.5" +name = "cucumber-codegen" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "ed2fc8a8bbb73af3230db699e8690c5c786655f75eb89e5f18d76055fa1a9a4d" dependencies = [ + "cucumber-expressions", + "inflections", + "itertools", "proc-macro2", "quote", + "regex", "syn", + "synthez", ] [[package]] -name = "dunce" -version = "1.0.5" +name = "cucumber-expressions" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +checksum = "6401038de3af44fe74e6fccdb8a5b7db7ba418f480c8e9ad584c6f65c05a27a6" +dependencies = [ + "derive_more", + "either", + "nom", + "nom_locate", + "regex", + "regex-syntax", +] [[package]] -name = "either" -version = "1.15.0" +name = "derive_more" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] [[package]] -name = "encoding_rs" -version = "0.8.35" +name = "derive_more-impl" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "cfg-if", + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", + "unicode-xid", ] [[package]] -name = "equivalent" -version = "1.0.2" +name = "either" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] -name = "errno" -version = "0.3.14" +name = "encode_unicode" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] -name = "fastrand" -version = "2.3.0" +name = "env_home" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" [[package]] -name = "file-locker" -version = "1.1.4" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ae8b5984a4863d8a32109a848d038bd6d914f20f010cc141375f7a183c41cf" -dependencies = [ - "nix", -] +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "find-msvc-tools" -version = "0.1.7" +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] [[package]] -name = "fnv" -version = "1.0.7" +name = "fallible-iterator" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] -name = "foldhash" -version = "0.1.5" +name = "fallible-streaming-iterator" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] -name = "form_urlencoded" -version = "1.2.2" +name = "fastrand" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] -name = "fs_extra" -version = "1.3.0" +name = "find-msvc-tools" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "futures" @@ -438,16 +463,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" -[[package]] -name = "futures-timer" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" -dependencies = [ - "gloo-timers", - "send_wrapper", -] - [[package]] name = "futures-util" version = "0.3.32" @@ -465,446 +480,249 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi", - "wasip2", - "wasm-bindgen", ] [[package]] -name = "getrandom" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +name = "getter" +version = "0.1.0" dependencies = [ - "cfg-if", - "libc", - "r-efi", - "rand_core 0.10.0", - "wasip2", - "wasip3", + "getter-cli", + "getter-core", + "getter-storage", + "rustls-platform-verifier", + "thiserror 1.0.69", + "tokio", ] [[package]] -name = "getter" +name = "getter-cli" version = "0.1.0" dependencies = [ - "async-trait", - "async_fn_traits", - "bytes", - "file-locker", - "futures", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "jsonl", - "jsonrpsee", - "libversion-sys", - "markdown", - "mockito", - "once_cell", - "parking_lot", - "quick-xml", - "rand 0.10.0", - "regex", - "reqwest", - "rustls", - "rustls-platform-verifier 0.6.2", + "cucumber", + "getter-core", + "getter-storage", "serde", "serde_json", - "serial_test", "tempfile", + "thiserror 1.0.69", "tokio", - "tokio-util", - "url", - "uuid", ] [[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "gloo-net" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" +name = "getter-core" +version = "0.1.0" dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils", - "http", - "js-sys", - "pin-project", + "mlua", "serde", "serde_json", + "tempfile", "thiserror 1.0.69", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", + "toml", ] [[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +name = "getter-downloader" +version = "0.1.0" dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", + "getter-core", ] [[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +name = "getter-ffi" +version = "0.1.0" dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", + "getter-core", ] [[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +name = "getter-plugin-api" +version = "0.1.0" dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", + "getter-core", ] [[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +name = "getter-providers" +version = "0.1.0" dependencies = [ - "foldhash", + "getter-core", ] [[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +name = "getter-rpc" +version = "0.1.0" +dependencies = [ + "getter-core", +] [[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +name = "getter-storage" +version = "0.1.0" +dependencies = [ + "getter-core", + "rusqlite", + "tempfile", + "thiserror 1.0.69", +] [[package]] -name = "http" -version = "1.4.0" +name = "gherkin" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "9e2c0d8c632f8a251ce9a8198079b1022adc586ff4e3d33e18debd40eb463b31" dependencies = [ - "bytes", - "itoa", + "heck", + "peg", + "quote", + "serde", + "serde_json", + "syn", + "textwrap", + "thiserror 2.0.18", + "typed-builder", ] [[package]] -name = "http-body" -version = "1.0.1" +name = "globset" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" dependencies = [ - "bytes", - "http", + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", ] [[package]] -name = "http-body-util" -version = "0.1.3" +name = "globwalk" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", + "bitflags", + "ignore", + "walkdir", ] [[package]] -name = "httparse" -version = "1.10.1" +name = "hashbrown" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] [[package]] -name = "httpdate" -version = "1.0.3" +name = "hashbrown" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] -name = "hyper" -version = "1.8.1" +name = "hashlink" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", + "hashbrown 0.14.5", ] [[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "log", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "rustls-platform-verifier 0.6.2", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "icu_collections" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" - -[[package]] -name = "icu_properties" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" - -[[package]] -name = "icu_provider" -version = "2.1.1" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "id-arena" +name = "humantime" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] -name = "idna_adapter" -version = "1.2.1" +name = "ignore" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "b915661dd01db3f05050265b2477bcc6527b3792388e2749b41623cc592be67d" dependencies = [ - "icu_normalizer", - "icu_properties", + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", ] [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", - "serde", - "serde_core", + "hashbrown 0.17.1", ] [[package]] -name = "ipnet" -version = "2.11.0" +name = "inflections" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" [[package]] -name = "iri-string" -version = "0.7.10" +name = "inventory" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" dependencies = [ - "memchr", - "serde", + "rustversion", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jni" @@ -915,7 +733,7 @@ dependencies = [ "cesu8", "cfg-if", "combine", - "jni-sys", + "jni-sys 0.3.1", "log", "thiserror 1.0.69", "walkdir", @@ -924,229 +742,54 @@ dependencies = [ [[package]] name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "jsonl" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abae98f45234fc1980c198798166a80ad6b35eb5b7db4caa7bc72ff919e6b80" -dependencies = [ - "serde", - "serde_json", - "thiserror 1.0.69", -] - -[[package]] -name = "jsonrpsee" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3f48dc3e6b8bd21e15436c1ddd0bc22a6a54e8ec46fedd6adf3425f396ec6a" -dependencies = [ - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-http-client", - "jsonrpsee-server", - "jsonrpsee-types", - "jsonrpsee-wasm-client", - "jsonrpsee-ws-client", - "tokio", -] - -[[package]] -name = "jsonrpsee-client-transport" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf36eb27f8e13fa93dcb50ccb44c417e25b818cfa1a481b5470cd07b19c60b98" -dependencies = [ - "base64", - "futures-channel", - "futures-util", - "gloo-net", - "http", - "jsonrpsee-core", - "pin-project", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier 0.5.3", - "soketto", - "thiserror 2.0.17", - "tokio", - "tokio-rustls", - "tokio-util", - "tracing", - "url", -] - -[[package]] -name = "jsonrpsee-core" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "316c96719901f05d1137f19ba598b5fe9c9bc39f4335f67f6be8613921946480" -dependencies = [ - "async-trait", - "bytes", - "futures-timer", - "futures-util", - "http", - "http-body", - "http-body-util", - "jsonrpsee-types", - "parking_lot", - "pin-project", - "rand 0.9.2", - "rustc-hash", - "serde", - "serde_json", - "thiserror 2.0.17", - "tokio", - "tokio-stream", - "tower", - "tracing", - "wasm-bindgen-futures", -] - -[[package]] -name = "jsonrpsee-http-client" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790bedefcec85321e007ff3af84b4e417540d5c87b3c9779b9e247d1bcc3dab8" -dependencies = [ - "base64", - "http-body", - "hyper", - "hyper-rustls", - "hyper-util", - "jsonrpsee-core", - "jsonrpsee-types", - "rustls", - "rustls-platform-verifier 0.5.3", - "serde", - "serde_json", - "thiserror 2.0.17", - "tokio", - "tower", - "url", -] - -[[package]] -name = "jsonrpsee-server" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c51b7c290bb68ce3af2d029648148403863b982f138484a73f02a9dd52dbd7f" -dependencies = [ - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "jsonrpsee-core", - "jsonrpsee-types", - "pin-project", - "route-recognizer", - "serde", - "serde_json", - "soketto", - "thiserror 2.0.17", - "tokio", - "tokio-stream", - "tokio-util", - "tower", - "tracing", -] - -[[package]] -name = "jsonrpsee-types" -version = "0.26.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc88ff4688e43cc3fa9883a8a95c6fa27aa2e76c96e610b737b6554d650d7fd5" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" dependencies = [ - "http", - "serde", - "serde_json", - "thiserror 2.0.17", + "jni-sys 0.4.1", ] [[package]] -name = "jsonrpsee-wasm-client" -version = "0.26.0" +name = "jni-sys" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7902885de4779f711a95d82c8da2d7e5f9f3a7c7cfa44d51c067fd1c29d72a3c" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" dependencies = [ - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", - "tower", + "jni-sys-macros", ] [[package]] -name = "jsonrpsee-ws-client" -version = "0.26.0" +name = "jni-sys-macros" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6fceceeb05301cc4c065ab3bd2fa990d41ff4eb44e4ca1b30fa99c057c3e79" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ - "http", - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", - "tower", - "url", + "quote", + "syn", ] -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libc" -version = "0.2.182" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] -name = "libloading" -version = "0.8.9" +name = "libsqlite3-sys" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ - "cfg-if", - "windows-link", + "cc", + "pkg-config", + "vcpkg", ] [[package]] -name = "libversion-sys" -version = "0.1.0" -source = "git+https://github.com/DUpdateSystem/libversion-sys?rev=68391515ac8f555ca86bc5dfcd980f66352e414d#68391515ac8f555ca86bc5dfcd980f66352e414d" -dependencies = [ - "bindgen", - "cc", - "cmake", -] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" @@ -1154,12 +797,6 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" -[[package]] -name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - [[package]] name = "lock_api" version = "0.4.14" @@ -1171,42 +808,40 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] -name = "lru-slab" -version = "0.1.2" +name = "lua-src" +version = "547.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +checksum = "1edaf29e3517b49b8b746701e5648ccb5785cde1c119062cbabbc5d5cd115e42" +dependencies = [ + "cc", +] [[package]] -name = "markdown" -version = "1.0.0" +name = "luajit-src" +version = "210.5.12+a4f56a4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5cab8f2cadc416a82d2e783a1946388b31654d391d1c7d92cc1f03e295b1deb" +checksum = "b3a8e7962a5368d5f264d045a5a255e90f9aa3fc1941ae15a8d2940d42cac671" dependencies = [ - "unicode-id", + "cc", + "which", ] [[package]] name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "mio" -version = "1.1.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "wasi", @@ -1214,63 +849,79 @@ dependencies = [ ] [[package]] -name = "mockito" -version = "1.7.2" +name = "mlua" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90820618712cab19cfc46b274c6c22546a82affcb3c3bdf0f29e3db8e1bb92c0" +checksum = "c1f5f8fbebc7db5f671671134b9321c4b9aa9adeafccfd9a8c020ae45c6a35d0" dependencies = [ - "assert-json-diff", - "bytes", - "colored", - "futures-core", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "log", - "pin-project-lite", - "rand 0.9.2", - "regex", - "serde_json", - "serde_urlencoded", - "similar", - "tokio", + "bstr", + "either", + "mlua-sys", + "num-traits", + "parking_lot", + "rustc-hash", + "rustversion", ] [[package]] -name = "nix" -version = "0.29.0" +name = "mlua-sys" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "380c1f7e2099cafcf40e51d3a9f20a346977587aa4d012eae1f043149a728a93" dependencies = [ - "bitflags", + "cc", "cfg-if", - "cfg_aliases", - "libc", + "lua-src", + "luajit-src", + "pkg-config", ] [[package]] name = "nom" -version = "7.1.3" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nom_locate" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "0b577e2d69827c4740cba2b52efaad1c4cc7c73042860b199710b3575c68438d" dependencies = [ + "bytecount", "memchr", - "minimal-lexical", + "nom", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", ] [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openssl-probe" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "parking_lot" @@ -1296,31 +947,46 @@ dependencies = [ ] [[package]] -name = "paste" -version = "1.0.15" +name = "peg" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f76678828272f177ac33b7e2ac2e3e73cc6c1cd1e3e387928aa69562fa51367" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "636d60acf97633e48d266d7415a9355d4389cea327a193f87df395d88cd2b14d" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] [[package]] -name = "percent-encoding" -version = "2.3.2" +name = "peg-runtime" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5" [[package]] name = "pin-project" -version = "1.1.10" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.10" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", @@ -1329,225 +995,74 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] -name = "pin-utils" -version = "0.1.0" +name = "pkg-config" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] -name = "potential_utf" -version = "0.1.4" +name = "proc-macro2" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ - "zerovec", + "unicode-ident", ] [[package]] -name = "ppv-lite86" -version = "0.2.21" +name = "quote" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ - "zerocopy", + "proc-macro2", ] [[package]] -name = "prettyplease" -version = "0.2.37" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] -name = "proc-macro2" -version = "1.0.105" +name = "redox_syscall" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "unicode-ident", + "bitflags", ] [[package]] -name = "quick-xml" -version = "0.39.2" +name = "ref-cast" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ - "encoding_rs", - "memchr", - "tokio", + "ref-cast-impl", ] [[package]] -name = "quinn" -version = "0.11.9" +name = "ref-cast-impl" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.17", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.17", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - -[[package]] -name = "rand" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" -dependencies = [ - "chacha20", - "getrandom 0.4.1", - "rand_core 0.10.0", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rand_core" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", + "quote", + "syn", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -1557,9 +1072,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1568,49 +1083,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" - -[[package]] -name = "reqwest" -version = "0.13.2" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" -dependencies = [ - "base64", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier 0.6.2", - "serde", - "serde_json", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tokio-util", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", -] +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "ring" @@ -1620,23 +1095,40 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", ] [[package]] -name = "route-recognizer" -version = "0.3.1" +name = "rusqlite" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] [[package]] name = "rustix" @@ -1648,19 +1140,16 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ - "aws-lc-rs", - "log", "once_cell", - "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -1669,9 +1158,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -1681,35 +1170,13 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.2" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ - "web-time", "zeroize", ] -[[package]] -name = "rustls-platform-verifier" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" -dependencies = [ - "core-foundation", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs 0.26.11", - "windows-sys 0.52.0", -] - [[package]] name = "rustls-platform-verifier" version = "0.6.2" @@ -1727,8 +1194,8 @@ dependencies = [ "rustls-webpki", "security-framework", "security-framework-sys", - "webpki-root-certs 1.0.5", - "windows-sys 0.60.2", + "webpki-root-certs", + "windows-sys 0.61.2", ] [[package]] @@ -1739,11 +1206,10 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ - "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -1755,12 +1221,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" - [[package]] name = "same-file" version = "1.0.6" @@ -1770,20 +1230,11 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scc" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" -dependencies = [ - "sdd", -] - [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] @@ -1795,16 +1246,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "sdd" -version = "3.0.10" +name = "sealed" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" +checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "security-framework" -version = "3.5.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags", "core-foundation", @@ -1815,9 +1271,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -1825,15 +1281,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "send_wrapper" -version = "0.4.0" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -1867,9 +1317,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -1879,119 +1329,64 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "serde_spanned" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", "serde", ] [[package]] -name = "serial_test" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "911bd979bf1070a3f3aa7b691a3b3e9968f339ceeec89e08c280a8a22207a32f" -dependencies = [ - "futures-executor", - "futures-util", - "log", - "once_cell", - "parking_lot", - "scc", - "serial_test_derive", -] - -[[package]] -name = "serial_test_derive" -version = "3.4.0" +name = "shlex" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a7d91949b85b0d2fb687445e448b40d322b6b3e4af6b44a29b21d9a5f33e6d9" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] -name = "sha1" -version = "0.10.6" +name = "slab" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest", -] +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] -name = "shlex" -version = "1.3.0" +name = "smallvec" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] -name = "signal-hook-registry" -version = "1.4.8" +name = "smart-default" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" dependencies = [ - "errno", - "libc", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "similar" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "smallvec" -version = "1.15.1" +name = "smawk" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "e8e2fb0f499abb4d162f2bedad68f5ef91a1682b5a03596ddb67efd37768d100" [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.60.2", -] - -[[package]] -name = "soketto" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" -dependencies = [ - "base64", - "bytes", - "futures", - "http", - "httparse", - "log", - "rand 0.8.5", - "sha1", + "windows-sys 0.61.2", ] [[package]] -name = "stable_deref_trait" -version = "1.2.1" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" @@ -2001,9 +1396,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.114" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -2011,509 +1406,287 @@ dependencies = [ ] [[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tempfile" -version = "3.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" -dependencies = [ - "fastrand", - "getrandom 0.3.4", - "once_cell", - "rustix", - "windows-sys 0.60.2", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl 2.0.17", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" +name = "synthez" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "6d8a928f38f1bc873f28e0d2ba8298ad65374a6ac2241dabd297271531a736cd" dependencies = [ - "proc-macro2", - "quote", "syn", + "synthez-codegen", + "synthez-core", ] [[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.49.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.6.0" +name = "synthez-codegen" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "8fb83b8df4238e11746984dfb3819b155cd270de0e25847f45abad56b3671047" dependencies = [ - "proc-macro2", - "quote", "syn", + "synthez-core", ] [[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "iri-string", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" +name = "synthez-core" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +checksum = "906fba967105d822e7c7ed60477b5e76116724d33de68a585681fb253fc30d5c" dependencies = [ "proc-macro2", "quote", + "sealed", "syn", ] [[package]] -name = "tracing-core" -version = "0.1.36" +name = "tempfile" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ + "fastrand", + "getrandom 0.4.3", "once_cell", + "rustix", + "windows-sys 0.61.2", ] [[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "unicode-id" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ba288e709927c043cbe476718d37be306be53fb1fafecd0dbe36d072be2580" - -[[package]] -name = "unicode-ident" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" -dependencies = [ - "getrandom 0.4.1", - "js-sys", - "serde_core", - "wasm-bindgen", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" +name = "terminal_size" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ - "try-lock", + "rustix", + "windows-sys 0.61.2", ] [[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" +name = "textwrap" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] [[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" +name = "thiserror" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "wit-bindgen 0.46.0", + "thiserror-impl 1.0.69", ] [[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +name = "thiserror" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "wit-bindgen 0.51.0", + "thiserror-impl 2.0.18", ] [[package]] -name = "wasm-bindgen" -version = "0.2.108" +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "wasm-bindgen-futures" -version = "0.4.58" +name = "thiserror-impl" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ - "cfg-if", - "futures-util", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.108" +name = "tokio" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ - "quote", - "wasm-bindgen-macro-support", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", ] [[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.108" +name = "tokio-macros" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ - "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-shared", ] [[package]] -name = "wasm-bindgen-shared" -version = "0.2.108" +name = "toml" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ - "unicode-ident", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", ] [[package]] -name = "wasm-encoder" -version = "0.244.0" +name = "toml_datetime" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ - "leb128fmt", - "wasmparser", + "serde", ] [[package]] -name = "wasm-metadata" -version = "0.244.0" +name = "toml_edit" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "anyhow", "indexmap", - "wasm-encoder", - "wasmparser", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", ] [[package]] -name = "wasm-streams" -version = "0.5.0" +name = "toml_write" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] -name = "wasmparser" -version = "0.244.0" +name = "typed-builder" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +checksum = "31aa81521b70f94402501d848ccc0ecaa8f93c8eb6999eb9747e72287757ffda" dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", + "typed-builder-macro", ] [[package]] -name = "web-sys" -version = "0.3.85" +name = "typed-builder-macro" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "076a02dc54dd46795c2e9c8282ed40bcfb1e22747e955de9389a1de28190fb26" dependencies = [ - "js-sys", - "wasm-bindgen", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "web-time" -version = "1.1.0" +name = "unicode-ident" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] -name = "webpki-root-certs" -version = "0.26.11" +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ - "webpki-root-certs 1.0.5", + "same-file", + "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "webpki-root-certs" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" +checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" dependencies = [ "rustls-pki-types", ] [[package]] -name = "webpki-roots" -version = "1.0.5" +name = "which" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" dependencies = [ - "rustls-pki-types", + "either", + "env_home", + "rustix", + "winsafe", ] [[package]] @@ -2522,7 +1695,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2549,15 +1722,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -2591,30 +1755,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -2627,12 +1774,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -2645,12 +1786,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -2663,24 +1798,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -2693,12 +1816,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -2711,12 +1828,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -2729,12 +1840,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -2748,216 +1853,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - -[[package]] -name = "yoke" -version = "0.8.1" +name = "winnow" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", + "memchr", ] [[package]] -name = "yoke-derive" -version = "0.8.1" +name = "winsafe" +version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "zerocopy" -version = "0.8.33" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", "syn", - "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zmij" -version = "1.0.12" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index c7a8f49..500b0f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,44 +3,35 @@ name = "getter" version = "0.1.0" edition = "2021" +[workspace] +resolver = "2" +members = [ + "crates/getter-core", + "crates/getter-storage", + "crates/getter-providers", + "crates/getter-downloader", + "crates/getter-plugin-api", + "crates/getter-rpc", + "crates/getter-cli", + "crates/getter-ffi", +] + +[workspace.package] +version = "0.1.0" +edition = "2021" + [features] -default = ["rustls-platform-verifier"] -rustls-platform-verifier = ["hyper-rustls/rustls-platform-verifier", "dep:rustls-platform-verifier"] +default = ["cli"] +cli = ["dep:getter-cli"] +rustls-platform-verifier = ["dep:rustls-platform-verifier"] rustls-platform-verifier-android = ["rustls-platform-verifier", "rustls-platform-verifier/jni"] -webpki-roots = ["hyper-rustls/webpki-roots"] -native-tokio = ["hyper-rustls/native-tokio"] +webpki-roots = [] +native-tokio = [] [dependencies] -once_cell = "1.21.3" -async-trait = "0.1.89" -hyper = { version = "1.8", features = ["full"] } -tokio = { version = "1", features = ["full", "macros"] } -bytes = "1.11.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.149" -quick-xml = { version = "0.39.0", features = ["encoding", "async-tokio"] } -async_fn_traits = "0.1.1" -libversion-sys = { git = "https://github.com/DUpdateSystem/libversion-sys", rev = "68391515ac8f555ca86bc5dfcd980f66352e414d" } -regex = "1.12.2" -hyper-util = { version = "0.1.19", features = ["client", "tokio", "http1"] } -http-body-util = "0.1.3" -jsonrpsee = { version = "0.26.0", features = ["server", "client"] } -hyper-rustls = { version = "0.27.7", features = ["http1", "http2", "native-tokio", "ring", "tls12"], default-features = false } -rustls-platform-verifier = { version = "0.6.2", optional = true } -rustls = { version = "0.23.36", default-features = false } -markdown = "1.0.0" -# Downloader dependencies -reqwest = { version = "0.13", default-features = false, features = ["rustls", "stream", "json"] } -uuid = { version = "1.19", features = ["v4", "serde"] } -parking_lot = "0.12" -futures = "0.3" -tokio-util = { version = "0.7", features = ["io"] } -jsonl = "4.0" -file-locker = "1.1" -url = "2" - -[dev-dependencies] -mockito = "1.7.1" -rand = "0.10.0" -serial_test = "3.3.1" -tempfile = "3.24.0" +getter-core = { path = "crates/getter-core" } +getter-storage = { path = "crates/getter-storage" } +getter-cli = { path = "crates/getter-cli", optional = true } +thiserror = "1" +tokio = { version = "1", features = ["net"] } +rustls-platform-verifier = { version = "0.6", optional = true } diff --git a/README.md b/README.md index 24fef6c..34543a0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,24 @@ -# getter -Get update for everywhere +# UpgradeAll getter + +Reusable Rust getter core for the UpgradeAll rewrite. + +This repository is intentionally usable outside the UpgradeAll UI. The UpgradeAll app is a UI/platform adapter; package/repository evaluation, storage, migration mapping, update policy, and CLI behavior belong here. + +## Current rewrite spine + +- Rust workspace split into `getter-core`, `getter-storage`, `getter-cli`, and placeholder provider/downloader/RPC/FFI crates. +- Package IDs are readable, for example `android/org.fdroid.fdroid`. +- Lua package repositories use `repo.toml` plus `packages/`, `lib/`, and `templates/` directories. +- SQLite storage uses a main DB and a separate cache DB. +- `getter-cli` exposes JSON command contracts for init, app list, repository registration/evaluation, package evaluation, storage validation, and legacy bridge-bundle import. + +## Verify + +```bash +cargo fmt --all --check +cargo test --workspace --lib --bins +cargo test -p getter-cli --test bdd_cli +cargo check --workspace --all-targets +``` + +The CI helper at `tests/script/cargo_test.sh` runs the same core checks plus feature-compatibility checks. diff --git a/crates/getter-cli/Cargo.toml b/crates/getter-cli/Cargo.toml new file mode 100644 index 0000000..ca03088 --- /dev/null +++ b/crates/getter-cli/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "getter-cli" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } +getter-storage = { path = "../getter-storage" } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "1" + +[dev-dependencies] +cucumber = "0.23" +serde_json = "1" +tempfile = "3" +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } + +[[test]] +name = "bdd_cli" +harness = false diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs new file mode 100644 index 0000000..8e0155e --- /dev/null +++ b/crates/getter-cli/src/lib.rs @@ -0,0 +1,817 @@ +//! User-facing getter CLI implementation. +//! +//! The CLI is intentionally thin: it owns command parsing and JSON envelopes, +//! while durable state is initialized through `getter-storage` so the command +//! surface exercises the same Rust-owned SQLite direction used by embedders. + +use getter_core::lua::evaluate_package_file; +use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; +use getter_core::{PackageId, RepositoryId, RepositoryPriority}; +use getter_storage::legacy_room::{ + map_legacy_app, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, +}; +use getter_storage::{ + CacheDb, MainDb, StorageError, StoredPackageResolution, StoredRepository, StoredTrackedPackage, + TrackedPackageUpsert, +}; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use std::fs; +use std::path::{Path, PathBuf}; + +const MAIN_DB_FILE: &str = "main.db"; +const CACHE_DB_FILE: &str = "cache.db"; +const MIGRATION_REPORTS_DIR: &str = "migration-reports"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CliInvocation { + pub data_dir: PathBuf, + pub command: CliCommand, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CliCommand { + Init, + AppList, + HubList, + RepoList, + RepoAdd { + id: RepositoryId, + path: PathBuf, + priority: Option, + }, + RepoEval { + id: RepositoryId, + }, + PackageEval { + package_id: PackageId, + repo_id: Option, + }, + StorageValidate, + LegacyImportRoomBundle { + bundle: PathBuf, + }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ExitCode { + Success = 0, + GenericFailure = 1, + Usage = 2, + Storage = 10, + Migration = 20, +} + +impl ExitCode { + pub const fn code(self) -> i32 { + self as i32 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum CliError { + #[error("{0}")] + Usage(String), + #[error("storage error: {0}")] + Storage(String), + #[error("repository error: {0}")] + Repository(String), + #[error("package evaluation error: {0}")] + PackageEval(String), + #[error("Legacy Room export bundle is invalid")] + InvalidLegacyBundle { report_path: PathBuf }, + #[error("Legacy Room bundle import is not implemented yet")] + UnsupportedLegacyBundle { report_path: PathBuf }, +} + +impl CliError { + pub fn exit_code(&self) -> ExitCode { + match self { + Self::Usage(_) => ExitCode::Usage, + Self::Storage(_) => ExitCode::Storage, + Self::Repository(_) | Self::PackageEval(_) => ExitCode::GenericFailure, + Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } => { + ExitCode::Migration + } + } + } + + fn code(&self) -> &'static str { + match self { + Self::Usage(_) => "cli.usage", + Self::Storage(_) => "storage.error", + Self::Repository(_) => "repository.error", + Self::PackageEval(_) => "package.eval_error", + Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", + Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", + } + } + + fn message(&self) -> &'static str { + match self { + Self::Usage(_) => "Invalid getter CLI usage", + Self::Storage(_) => "Getter storage operation failed", + Self::Repository(_) => "Getter repository operation failed", + Self::PackageEval(_) => "Getter package evaluation failed", + Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", + Self::UnsupportedLegacyBundle { .. } => { + "Legacy Room bundle import is not implemented yet" + } + } + } + + fn detail(&self) -> Option<&str> { + match self { + Self::Usage(detail) + | Self::Storage(detail) + | Self::Repository(detail) + | Self::PackageEval(detail) => Some(detail.as_str()), + Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } => None, + } + } + + fn report_path(&self) -> Option<&Path> { + match self { + Self::InvalidLegacyBundle { report_path } + | Self::UnsupportedLegacyBundle { report_path } => Some(report_path.as_path()), + Self::Usage(_) | Self::Storage(_) | Self::Repository(_) | Self::PackageEval(_) => None, + } + } +} + +impl From for CliError { + fn from(value: StorageError) -> Self { + Self::Storage(value.to_string()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CliOutput { + pub exit_code: ExitCode, + pub stdout: String, + pub stderr: String, +} + +/// Parse and execute a getter CLI invocation, returning stdout/stderr and the +/// supported process exit code. This is used by the binary entrypoint and by +/// tests so command behavior stays consistent. +pub fn run(args: I) -> CliOutput +where + I: IntoIterator, + S: Into, +{ + match run_inner(args) { + Ok((command_name, data)) => CliOutput { + exit_code: ExitCode::Success, + stdout: success_envelope(&command_name, data), + stderr: String::new(), + }, + Err((command_name, error)) => { + let exit_code = error.exit_code(); + let stderr = match error { + CliError::Usage(_) => usage_text(), + _ => String::new(), + }; + CliOutput { + exit_code, + stdout: error_envelope(&command_name, &error), + stderr, + } + } + } +} + +fn run_inner(args: I) -> Result<(String, Value), (String, CliError)> +where + I: IntoIterator, + S: Into, +{ + let invocation = parse_args(args).map_err(|error| ("unknown".to_owned(), error))?; + let command_name = invocation.command.name().to_owned(); + execute(invocation) + .map(|data| (command_name.clone(), data)) + .map_err(|error| (command_name, error)) +} + +pub fn parse_args(args: I) -> Result +where + I: IntoIterator, + S: Into, +{ + let mut args: Vec = args.into_iter().map(Into::into).collect(); + if args.first().is_some_and(|arg| arg != "--data-dir") { + args.remove(0); + } + + if args.first().map(String::as_str) != Some("--data-dir") { + return Err(CliError::Usage( + "missing required --data-dir global option".to_owned(), + )); + } + if args.len() < 3 { + return Err(CliError::Usage( + "missing command after --data-dir ".to_owned(), + )); + } + + let data_dir = PathBuf::from(args[1].clone()); + let command_args = &args[2..]; + let command = match command_args { + [command] if command == "init" => CliCommand::Init, + [domain, command] if domain == "app" && command == "list" => CliCommand::AppList, + [domain, command] if domain == "hub" && command == "list" => CliCommand::HubList, + [domain, command] if domain == "repo" && command == "list" => CliCommand::RepoList, + [domain, command, id, path] if domain == "repo" && command == "add" => { + CliCommand::RepoAdd { + id: parse_repository_id(id)?, + path: PathBuf::from(path), + priority: None, + } + } + [domain, command, id, path, flag, priority] + if domain == "repo" && command == "add" && flag == "--priority" => + { + CliCommand::RepoAdd { + id: parse_repository_id(id)?, + path: PathBuf::from(path), + priority: Some(parse_priority(priority)?), + } + } + [domain, command, id] if domain == "repo" && command == "eval" => CliCommand::RepoEval { + id: parse_repository_id(id)?, + }, + [domain, command, package_id] if domain == "package" && command == "eval" => { + CliCommand::PackageEval { + package_id: parse_package_id(package_id)?, + repo_id: None, + } + } + [domain, command, package_id, flag, repo_id] + if domain == "package" && command == "eval" && flag == "--repo" => + { + CliCommand::PackageEval { + package_id: parse_package_id(package_id)?, + repo_id: Some(parse_repository_id(repo_id)?), + } + } + [domain, command] if domain == "storage" && command == "validate" => { + CliCommand::StorageValidate + } + [domain, command, bundle] if domain == "legacy" && command == "import-room-bundle" => { + CliCommand::LegacyImportRoomBundle { + bundle: PathBuf::from(bundle), + } + } + _ => { + return Err(CliError::Usage(format!( + "unsupported command: {}", + command_args.join(" ") + ))) + } + }; + + Ok(CliInvocation { data_dir, command }) +} + +fn execute(invocation: CliInvocation) -> Result { + match invocation.command { + CliCommand::Init => { + initialize_storage(&invocation.data_dir)?; + Ok(json!({ + "data_dir": invocation.data_dir, + "main_db": main_db_path(&invocation.data_dir), + "cache_db": cache_db_path(&invocation.data_dir), + })) + } + CliCommand::AppList => { + let db = open_main_db(&invocation.data_dir)?; + Ok(json!({ "apps": tracked_packages_json(db.tracked_packages()?) })) + } + CliCommand::HubList => { + open_initialized_storage(&invocation.data_dir)?; + Ok(json!({ "hubs": [] })) + } + CliCommand::RepoList => { + let db = open_main_db(&invocation.data_dir)?; + Ok(json!({ "repositories": list_repositories(&db)? })) + } + CliCommand::RepoAdd { id, path, priority } => { + let db = open_main_db(&invocation.data_dir)?; + let layout = load_repository_layout(&path)?; + if layout.metadata.id != id { + return Err(CliError::Repository(format!( + "repo.toml id '{}' does not match requested id '{}'", + layout.metadata.id, id + ))); + } + let metadata = RepositoryMetadata { + priority: priority.unwrap_or(layout.metadata.priority), + ..layout.metadata.clone() + }; + db.upsert_repository(&metadata, Some(&path), None)?; + Ok(json!({ "repository": repository_metadata_json(&metadata, Some(&path), None) })) + } + CliCommand::RepoEval { id } => { + let db = open_main_db(&invocation.data_dir)?; + let repo = find_repository(&db, &id)?; + let path = repo_path(&repo)?; + let layout = load_repository_layout(&path)?; + let mut packages = Vec::new(); + for package_file in &layout.packages { + let package = evaluate_package_file(&layout, &package_file.path) + .map_err(|error| CliError::PackageEval(error.to_string()))?; + packages.push(package_json(package)?); + } + Ok(json!({ + "repository": repository_json(repo), + "packages": packages, + })) + } + CliCommand::PackageEval { + package_id, + repo_id, + } => { + let db = open_main_db(&invocation.data_dir)?; + let package = match repo_id { + Some(repo_id) => evaluate_package_from_repo(&db, &repo_id, &package_id)?, + None => evaluate_highest_priority_package(&db, &package_id)?, + }; + Ok(json!({ "package": package_json(package)? })) + } + CliCommand::StorageValidate => { + initialize_storage(&invocation.data_dir)?; + Ok(json!({ + "valid": true, + "main_db": main_db_path(&invocation.data_dir), + "cache_db": cache_db_path(&invocation.data_dir), + })) + } + CliCommand::LegacyImportRoomBundle { bundle } => { + let db = open_main_db(&invocation.data_dir)?; + let bytes = fs::read(&bundle).map_err(|source| { + match create_migration_report( + &invocation.data_dir, + &bundle, + "migration.invalid_bundle", + &format!("failed to read bundle: {source}"), + 0, + 0, + ) { + Ok(report_path) => CliError::InvalidLegacyBundle { report_path }, + Err(error) => error, + } + })?; + let parsed: LegacyRoomBundle = serde_json::from_slice(&bytes).map_err(|source| { + match create_migration_report( + &invocation.data_dir, + &bundle, + "migration.invalid_bundle", + &format!("failed to parse JSON bundle: {source}"), + 0, + 0, + ) { + Ok(report_path) => CliError::InvalidLegacyBundle { report_path }, + Err(error) => error, + } + })?; + if parsed.format != "upgradeall-legacy-room-bundle" || parsed.version != 17 { + let report_path = create_migration_report( + &invocation.data_dir, + &bundle, + "migration.unsupported_bundle", + &format!( + "unsupported legacy bundle format '{}' version {}", + parsed.format, parsed.version + ), + 0, + 0, + )?; + return Err(CliError::UnsupportedLegacyBundle { report_path }); + } + import_legacy_room_bundle(&db, &parsed)?; + let report_path = create_migration_report( + &invocation.data_dir, + &bundle, + "migration.imported", + "Legacy Room bundle imported", + parsed.apps.len() as u64, + parsed.apps.len() as u64, + )?; + let records = db.tracked_packages()?; + Ok(json!({ + "report_path": report_path, + "imported_records": parsed.apps.len(), + "apps": tracked_packages_json(records), + })) + } + } +} + +fn initialize_storage(data_dir: &Path) -> Result<(), CliError> { + fs::create_dir_all(data_dir).map_err(|source| { + CliError::Storage(format!("failed to create data directory: {source}")) + })?; + MainDb::open(main_db_path(data_dir))?; + CacheDb::open(cache_db_path(data_dir))?; + Ok(()) +} + +fn open_initialized_storage(data_dir: &Path) -> Result<(), CliError> { + initialize_storage(data_dir) +} + +fn open_main_db(data_dir: &Path) -> Result { + initialize_storage(data_dir)?; + Ok(MainDb::open(main_db_path(data_dir))?) +} + +fn parse_repository_id(value: &str) -> Result { + RepositoryId::new(value).map_err(|source| CliError::Usage(source.to_string())) +} + +fn parse_package_id(value: &str) -> Result { + value + .parse() + .map_err(|source: getter_core::PackageIdError| CliError::Usage(source.to_string())) +} + +fn parse_priority(value: &str) -> Result { + value + .parse::() + .map(RepositoryPriority::new) + .map_err(|source| CliError::Usage(format!("invalid repository priority: {source}"))) +} + +fn load_repository_layout(path: &Path) -> Result { + RepositoryLayout::load(path).map_err(|source| CliError::Repository(source.to_string())) +} + +fn list_repositories(db: &MainDb) -> Result, CliError> { + Ok(db + .repositories()? + .into_iter() + .map(repository_json) + .collect()) +} + +fn find_repository(db: &MainDb, id: &RepositoryId) -> Result { + db.repositories()? + .into_iter() + .find(|repo| &repo.id == id) + .ok_or_else(|| CliError::Repository(format!("repository '{id}' is not registered"))) +} + +fn evaluate_package_from_repo( + db: &MainDb, + repo_id: &RepositoryId, + package_id: &PackageId, +) -> Result { + let repo = find_repository(db, repo_id)?; + let path = repo_path(&repo)?; + let layout = load_repository_layout(&path)?; + let package_file = layout.package_file(package_id).ok_or_else(|| { + CliError::PackageEval(format!( + "package '{}' was not found in repository '{}'", + package_id, repo_id + )) + })?; + evaluate_package_file(&layout, &package_file.path) + .map_err(|error| CliError::PackageEval(error.to_string())) +} + +fn evaluate_highest_priority_package( + db: &MainDb, + package_id: &PackageId, +) -> Result { + for repo in db.repositories()? { + let path = repo_path(&repo)?; + let layout = load_repository_layout(&path)?; + if let Some(package_file) = layout.package_file(package_id) { + return evaluate_package_file(&layout, &package_file.path) + .map_err(|error| CliError::PackageEval(error.to_string())); + } + } + Err(CliError::PackageEval(format!( + "package '{package_id}' was not found in any registered repository" + ))) +} + +fn repo_path(repo: &StoredRepository) -> Result { + repo.path + .as_ref() + .map(PathBuf::from) + .ok_or_else(|| CliError::Repository(format!("repository '{}' has no path", repo.id))) +} + +fn repository_json(repo: StoredRepository) -> Value { + json!({ + "id": repo.id.as_str(), + "name": repo.name, + "priority": repo.priority.value(), + "api_version": repo.api_version, + "path": repo.path, + "revision": repo.revision, + }) +} + +fn repository_metadata_json( + metadata: &RepositoryMetadata, + path: Option<&Path>, + revision: Option<&str>, +) -> Value { + json!({ + "id": metadata.id.as_str(), + "name": metadata.name, + "priority": metadata.priority.value(), + "api_version": metadata.api_version, + "path": path.map(|path| path.to_string_lossy().to_string()), + "revision": revision, + }) +} + +fn package_json(package: getter_core::ResolvedPackage) -> Result { + serde_json::to_value(package) + .map_err(|source| CliError::PackageEval(format!("failed to serialize package: {source}"))) +} + +fn tracked_packages_json(packages: Vec) -> Vec { + packages + .into_iter() + .map(|package| { + json!({ + "id": package.package_id.to_string(), + "enabled": package.enabled, + "favorite": package.favorite, + "ignored_version": package.ignored_version, + "repository_id": package.repository_id.map(|id| id.to_string()), + "package_resolution": package.package_resolution.as_str(), + }) + }) + .collect() +} + +fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<(), CliError> { + for app in &bundle.apps { + let mapping = map_legacy_app( + &LegacyAppRecord { + kind: app.kind.to_legacy_kind()?, + installed_id: app.installed_id.clone(), + official_package_available: app.official_package_available, + common_conversion_available: app.common_conversion_available, + }, + Some(&LegacyExtraAppRecord { + ignored_version: app.ignored_version.clone(), + favorite: app.favorite, + }), + ) + .map_err(|source| CliError::Storage(source.to_string()))?; + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: mapping.package_id, + enabled: true, + favorite: mapping.user_state.favorite, + ignored_version: mapping.user_state.ignored_version, + repository_id: None, + package_resolution: stored_resolution(mapping.package_resolution), + })?; + } + + let report_json = json!({ + "ok": true, + "source": "legacy-room-bundle", + "imported_records": bundle.apps.len(), + }) + .to_string(); + db.insert_migration_record("legacy-room-v17", "legacy-room-bundle", &report_json)?; + Ok(()) +} + +fn stored_resolution(resolution: LegacyPackageResolution) -> StoredPackageResolution { + match resolution { + LegacyPackageResolution::OfficialRepositoryPackage => { + StoredPackageResolution::OfficialRepositoryPackage + } + LegacyPackageResolution::GenerateLocalPackage => { + StoredPackageResolution::GenerateLocalPackage + } + LegacyPackageResolution::MissingPackageDefinition => { + StoredPackageResolution::MissingPackageDefinition + } + } +} + +fn main_db_path(data_dir: &Path) -> PathBuf { + data_dir.join(MAIN_DB_FILE) +} + +fn cache_db_path(data_dir: &Path) -> PathBuf { + data_dir.join(CACHE_DB_FILE) +} + +fn create_migration_report( + data_dir: &Path, + bundle: &Path, + code: &str, + detail: &str, + imported_records: u64, + tracked_records: u64, +) -> Result { + let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); + fs::create_dir_all(&reports_dir).map_err(|source| { + CliError::Storage(format!( + "failed to create migration report directory: {source}" + )) + })?; + let report_path = reports_dir.join(report_file_name(code)); + let report = MigrationReport { + ok: code == "migration.imported", + code, + message: match code { + "migration.invalid_bundle" => "Legacy Room export bundle is invalid", + "migration.unsupported_bundle" => "Legacy Room bundle import is not implemented yet", + "migration.imported" => "Legacy Room bundle imported", + _ => "Legacy migration failed", + }, + bundle_file_name: bundle.file_name().and_then(|name| name.to_str()), + detail, + imported_records, + tracked_records, + }; + let bytes = serde_json::to_vec_pretty(&report) + .map_err(|source| CliError::Storage(format!("failed to serialize report: {source}")))?; + fs::write(&report_path, bytes) + .map_err(|source| CliError::Storage(format!("failed to write report: {source}")))?; + Ok(report_path) +} + +fn report_file_name(code: &str) -> String { + format!("{}.json", code.replace('.', "-")) +} + +#[derive(Debug, Serialize)] +struct MigrationReport<'a> { + ok: bool, + code: &'a str, + message: &'a str, + bundle_file_name: Option<&'a str>, + detail: &'a str, + imported_records: u64, + tracked_records: u64, +} + +fn success_envelope(command: &str, data: Value) -> String { + envelope_to_string(json!({ + "ok": true, + "command": command, + "data": data, + "warnings": [] + })) +} + +fn error_envelope(command: &str, error: &CliError) -> String { + let mut error_value = json!({ + "code": error.code(), + "message": error.message(), + }); + if let Some(detail) = error.detail() { + error_value["detail"] = json!(detail); + } + if let Some(report_path) = error.report_path() { + error_value["report_path"] = json!(report_path); + } + + envelope_to_string(json!({ + "ok": false, + "command": command, + "error": error_value, + })) +} + +fn envelope_to_string(value: Value) -> String { + let mut rendered = serde_json::to_string_pretty(&value).expect("JSON envelope is serializable"); + rendered.push('\n'); + rendered +} + +fn usage_text() -> String { + "Usage: getter --data-dir [--priority ]|repo eval |package eval [--repo ]|storage validate|hub list|legacy import-room-bundle >\n".to_owned() +} + +#[derive(Debug, Deserialize)] +struct LegacyRoomBundle { + format: String, + version: u32, + #[serde(default)] + apps: Vec, +} + +#[derive(Debug, Deserialize)] +struct LegacyBundleApp { + kind: LegacyBundleAppKind, + installed_id: String, + #[serde(default)] + official_package_available: bool, + #[serde(default)] + common_conversion_available: bool, + #[serde(default)] + ignored_version: Option, + #[serde(default)] + favorite: bool, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "snake_case")] +enum LegacyBundleAppKind { + Android, + Magisk, +} + +impl LegacyBundleAppKind { + fn to_legacy_kind(&self) -> Result { + Ok(match self { + Self::Android => LegacyAppKind::Android, + Self::Magisk => LegacyAppKind::Magisk, + }) + } +} + +impl CliCommand { + fn name(&self) -> &'static str { + match self { + Self::Init => "init", + Self::AppList => "app list", + Self::HubList => "hub list", + Self::RepoList => "repo list", + Self::RepoAdd { .. } => "repo add", + Self::RepoEval { .. } => "repo eval", + Self::PackageEval { .. } => "package eval", + Self::StorageValidate => "storage validate", + Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_global_data_dir_and_app_list_command() { + let invocation = + parse_args(["getter", "--data-dir", "/tmp/ua-getter", "app", "list"]).unwrap(); + + assert_eq!(invocation.data_dir, PathBuf::from("/tmp/ua-getter")); + assert_eq!(invocation.command, CliCommand::AppList); + } + + #[test] + fn run_init_creates_sqlite_database_files_and_json_envelope() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path().join("getter-data"); + + let output = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "init".to_owned(), + ]); + + assert_eq!(output.exit_code, ExitCode::Success); + assert!(data_dir.join(MAIN_DB_FILE).is_file()); + assert!(data_dir.join(CACHE_DB_FILE).is_file()); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "init"); + } + + #[test] + fn malformed_bundle_report_does_not_create_imported_state() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path().join("getter-data"); + let bundle = temp.path().join("bad.json"); + fs::write(&bundle, "not-json").unwrap(); + + let init = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "init".to_owned(), + ]); + assert_eq!(init.exit_code, ExitCode::Success); + + let output = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "legacy".to_owned(), + "import-room-bundle".to_owned(), + bundle.to_string_lossy().to_string(), + ]); + + assert_eq!(output.exit_code, ExitCode::Migration); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["ok"], false); + assert_eq!(json["error"]["code"], "migration.invalid_bundle"); + let report_path = json["error"]["report_path"].as_str().unwrap(); + assert!(Path::new(report_path).is_file()); + } +} diff --git a/crates/getter-cli/src/main.rs b/crates/getter-cli/src/main.rs new file mode 100644 index 0000000..dc49483 --- /dev/null +++ b/crates/getter-cli/src/main.rs @@ -0,0 +1,10 @@ +fn main() { + let output = getter_cli::run(std::env::args()); + if !output.stdout.is_empty() { + print!("{}", output.stdout); + } + if !output.stderr.is_empty() { + eprint!("{}", output.stderr); + } + std::process::exit(output.exit_code.code()); +} diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs new file mode 100644 index 0000000..807aaed --- /dev/null +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -0,0 +1,486 @@ +use cucumber::{given, then, when, World as _}; +use serde_json::Value; +use std::fs; +use std::path::PathBuf; +use std::process::Command; +use tempfile::TempDir; + +#[derive(Debug, Default, cucumber::World)] +struct CliWorld { + temp: Option, + data_dir: Option, + bundle: Option, + fixture_repo_id: Option, + fixture_repo_path: Option, + fixture_package_id: Option, + output: Option, + json: Option, +} + +#[given("an empty getter data directory")] +fn empty_getter_data_dir(world: &mut CliWorld) { + let temp = tempfile::tempdir().expect("create tempdir"); + let data_dir = temp.path().join("getter-data"); + world.temp = Some(temp); + world.data_dir = Some(data_dir); + world.json = None; +} + +#[given("an initialized getter data directory")] +fn initialized_getter_data_dir(world: &mut CliWorld) { + empty_getter_data_dir(world); + let output = run_getter(world, ["init".to_owned()]); + assert_success(&output); +} + +#[given("a corrupted legacy export bundle")] +fn corrupted_legacy_export_bundle(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let bundle = temp.path().join("corrupted-room-bundle.json"); + fs::write(&bundle, "not-json").expect("write corrupted bundle"); + world.bundle = Some(bundle); +} + +#[given("a syntactically valid legacy export bundle with an Android app")] +fn valid_legacy_export_bundle_with_android_app(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let bundle = temp.path().join("valid-room-bundle.json"); + fs::write( + &bundle, + r#"{ + "format": "upgradeall-legacy-room-bundle", + "version": 17, + "apps": [ + { + "kind": "android", + "installed_id": "org.fdroid.fdroid", + "official_package_available": true, + "ignored_version": "1.20.0", + "favorite": true + } + ] +}"#, + ) + .expect("write valid bundle"); + world.bundle = Some(bundle); +} + +#[given(expr = "a fixture Lua repository {string} with package {string}")] +fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { + create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); +} + +#[given(expr = "a fixture Lua repository {string} with package {string} named {string}")] +fn fixture_lua_repository_named( + world: &mut CliWorld, + repo_id: String, + package_id: String, + package_name: String, +) { + create_fixture_lua_repository(world, repo_id, package_id, package_name); +} + +#[when("I run getter init for that directory")] +fn run_getter_init(world: &mut CliWorld) { + let output = run_getter(world, ["init".to_owned()]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter app list for that directory")] +fn run_getter_app_list(world: &mut CliWorld) { + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter hub list for that directory")] +fn run_getter_hub_list(world: &mut CliWorld) { + let output = run_getter(world, ["hub".to_owned(), "list".to_owned()]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter repo list for that directory")] +fn run_getter_repo_list(world: &mut CliWorld) { + let output = run_getter(world, ["repo".to_owned(), "list".to_owned()]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter storage validate for that directory")] +fn run_getter_storage_validate(world: &mut CliWorld) { + let output = run_getter(world, ["storage".to_owned(), "validate".to_owned()]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter repo add for that repository with priority 0")] +fn run_getter_repo_add(world: &mut CliWorld) { + let repo_id = world + .fixture_repo_id + .as_ref() + .expect("fixture repo id exists") + .clone(); + run_getter_repo_add_with_priority(world, &repo_id, 0); +} + +#[when(expr = "I run getter repo add for repository {string} with priority {int}")] +fn run_getter_repo_add_named(world: &mut CliWorld, repo_id: String, priority: i32) { + run_getter_repo_add_with_priority(world, &repo_id, priority); +} + +#[when("I run getter repo eval for that repository")] +fn run_getter_repo_eval(world: &mut CliWorld) { + let repo_id = world + .fixture_repo_id + .as_ref() + .expect("fixture repo id exists"); + let output = run_getter( + world, + ["repo".to_owned(), "eval".to_owned(), repo_id.to_owned()], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter package eval for that fixture package")] +fn run_getter_package_eval(world: &mut CliWorld) { + let repo_id = world + .fixture_repo_id + .as_ref() + .expect("fixture repo id exists"); + let package_id = world + .fixture_package_id + .as_ref() + .expect("fixture package id exists"); + let output = run_getter( + world, + [ + "package".to_owned(), + "eval".to_owned(), + package_id.to_owned(), + "--repo".to_owned(), + repo_id.to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when(expr = "I run getter package eval for package {string}")] +fn run_getter_package_eval_without_repo(world: &mut CliWorld, package_id: String) { + let output = run_getter(world, ["package".to_owned(), "eval".to_owned(), package_id]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter legacy import-room-bundle for that bundle")] +fn run_getter_legacy_import(world: &mut CliWorld) { + let bundle = world.bundle.as_ref().expect("bundle exists"); + let output = run_getter( + world, + [ + "legacy".to_owned(), + "import-room-bundle".to_owned(), + bundle.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[then("the command succeeds")] +fn command_succeeds(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_success(output); +} + +#[then("the command fails with a documented migration error")] +fn command_fails_with_migration_error(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(20)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["command"], "legacy import-room-bundle"); + assert_eq!(json["error"]["code"], "migration.invalid_bundle"); + world.json = Some(json); +} + +#[then("the output is valid JSON")] +fn output_is_valid_json(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + let json = parse_stdout(output); + assert!( + json.is_object(), + "stdout JSON should be an object: {json:?}" + ); + world.json = Some(json); +} + +#[then("the getter data directory is usable")] +fn getter_data_directory_is_usable(world: &mut CliWorld) { + let data_dir = world.data_dir.as_ref().expect("data dir exists"); + assert!(data_dir.join("main.db").is_file(), "main.db should exist"); + assert!(data_dir.join("cache.db").is_file(), "cache.db should exist"); + + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + assert_success(&output); +} + +#[then("the output contains an empty app list")] +fn output_contains_empty_app_list(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "app list"); + assert_eq!(json["data"]["apps"], Value::Array(Vec::new())); +} + +#[then("the output contains an empty hub list")] +fn output_contains_empty_hub_list(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "hub list"); + assert_eq!(json["data"]["hubs"], Value::Array(Vec::new())); +} + +#[then("the output contains an empty repository list")] +fn output_contains_empty_repository_list(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "repo list"); + assert_eq!(json["data"]["repositories"], Value::Array(Vec::new())); +} + +#[then("the output reports valid storage")] +fn output_reports_valid_storage(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "storage validate"); + assert_eq!(json["data"]["valid"], true); + assert!(json["data"]["main_db"].as_str().is_some()); + assert!(json["data"]["cache_db"].as_str().is_some()); +} + +#[then("the output contains the added repository")] +fn output_contains_added_repository(world: &mut CliWorld) { + let repo_id = world + .fixture_repo_id + .as_ref() + .expect("fixture repo id exists") + .clone(); + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "repo add"); + assert_eq!(json["data"]["repository"]["id"], repo_id); +} + +#[then("the output contains the evaluated fixture package")] +fn output_contains_evaluated_fixture_package(world: &mut CliWorld) { + let package_id = world + .fixture_package_id + .as_ref() + .expect("fixture package id exists") + .clone(); + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "repo eval"); + let packages = json["data"]["packages"] + .as_array() + .expect("packages should be an array"); + assert!( + packages + .iter() + .any(|package| package["id"].as_str() == Some(package_id.as_str())), + "repo eval packages should include {package_id}: {packages:?}" + ); +} + +#[then("the output contains the fixture package")] +fn output_contains_fixture_package(world: &mut CliWorld) { + let package_id = world + .fixture_package_id + .as_ref() + .expect("fixture package id exists") + .clone(); + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "package eval"); + assert_eq!(json["data"]["package"]["id"], package_id); + assert_eq!(json["data"]["package"]["name"], "F-Droid"); +} + +#[then(expr = "the output contains package {string} named {string}")] +fn output_contains_named_package(world: &mut CliWorld, package_id: String, package_name: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "package eval"); + assert_eq!(json["data"]["package"]["id"], package_id); + assert_eq!(json["data"]["package"]["name"], package_name); +} + +#[then("no partially usable imported state is created")] +fn no_partially_usable_imported_state(world: &mut CliWorld) { + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + assert_success(&output); + let json = parse_stdout(&output); + assert_eq!(json["data"]["apps"], Value::Array(Vec::new())); +} + +#[then("a sanitized migration report is available")] +fn sanitized_migration_report_available(world: &mut CliWorld) { + let json = current_json(world); + let report_path = json["error"]["report_path"] + .as_str() + .expect("report_path should be a string"); + let report = fs::read_to_string(report_path).expect("report should be readable"); + let report_json: Value = serde_json::from_str(&report).expect("report should be JSON"); + assert_eq!(report_json["ok"], false); + assert_eq!(report_json["imported_records"], 0); + assert!(report_json.get("bundle_file_name").is_some()); + assert!( + report_json.get("raw_bundle").is_none(), + "report must not include raw bundle content" + ); +} + +#[then("the import reports one tracked app")] +fn import_reports_one_tracked_app(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "legacy import-room-bundle"); + assert_eq!(json["data"]["imported_records"], 1); + assert_eq!( + json["data"]["apps"].as_array().expect("apps array").len(), + 1 + ); +} + +#[then(expr = "the app list contains imported package {string}")] +fn app_list_contains_imported_package(world: &mut CliWorld, package_id: String) { + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + assert_success(&output); + let json = parse_stdout(&output); + let apps = json["data"]["apps"].as_array().expect("apps array"); + let app = apps + .iter() + .find(|app| app["id"].as_str() == Some(package_id.as_str())) + .unwrap_or_else(|| panic!("app list should contain {package_id}: {apps:?}")); + assert_eq!(app["favorite"], true); + assert_eq!(app["ignored_version"], "1.20.0"); + assert_eq!(app["package_resolution"], "official_repository_package"); + world.output = Some(output); + world.json = Some(json); +} + +fn current_json(world: &mut CliWorld) -> &Value { + world + .json + .get_or_insert_with(|| parse_stdout(world.output.as_ref().expect("command output exists"))) +} + +fn create_fixture_lua_repository( + world: &mut CliWorld, + repo_id: String, + package_id: String, + package_name: String, +) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let repo_path = temp.path().join(format!("repo-{repo_id}")); + let package_name_path = package_id + .strip_prefix("android/") + .expect("fixture package id should be android package id"); + fs::create_dir_all(repo_path.join("packages/android")).expect("create packages dir"); + fs::create_dir(repo_path.join("lib")).expect("create lib dir"); + fs::create_dir(repo_path.join("templates")).expect("create templates dir"); + fs::write( + repo_path.join("repo.toml"), + format!( + "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" + ), + ) + .expect("write repo.toml"); + fs::write( + repo_path.join(format!("packages/android/{package_name_path}.lua")), + format!( + r#" +return package_def {{ + id = "{package_id}", + name = "{package_name}", + installed = {{ + {{ kind = "android_package", package_name = "{package_name_path}" }}, + }}, +}} +"# + ), + ) + .expect("write package Lua"); + + world.fixture_repo_id = Some(repo_id); + world.fixture_repo_path = Some(repo_path); + world.fixture_package_id = Some(package_id); +} + +fn run_getter_repo_add_with_priority(world: &mut CliWorld, repo_id: &str, priority: i32) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let repo_path = temp.path().join(format!("repo-{repo_id}")); + let output = run_getter( + world, + [ + "repo".to_owned(), + "add".to_owned(), + repo_id.to_owned(), + repo_path.to_string_lossy().to_string(), + "--priority".to_owned(), + priority.to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +fn run_getter(world: &CliWorld, command_args: I) -> std::process::Output +where + I: IntoIterator, +{ + let exe = env!("CARGO_BIN_EXE_getter-cli"); + let data_dir = world.data_dir.as_ref().expect("data dir exists"); + let mut command = Command::new(exe); + command.arg("--data-dir").arg(data_dir); + for arg in command_args { + command.arg(arg); + } + command.output().expect("run getter binary") +} + +fn assert_success(output: &std::process::Output) { + assert_eq!( + output.status.code(), + Some(0), + "stdout:\n{}\nstderr:\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + let json = parse_stdout(output); + assert_eq!(json["ok"], true); +} + +fn parse_stdout(output: &std::process::Output) -> Value { + serde_json::from_slice(&output.stdout).unwrap_or_else(|error| { + panic!( + "stdout should be JSON: {error}\nstdout:\n{}\nstderr:\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ) + }) +} + +#[tokio::main] +async fn main() { + let features = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/features/cli"); + CliWorld::cucumber() + .fail_on_skipped() + .max_concurrent_scenarios(Some(1)) + .run_and_exit(features) + .await; +} diff --git a/crates/getter-cli/tests/features/cli/app_list.feature b/crates/getter-cli/tests/features/cli/app_list.feature new file mode 100644 index 0000000..3baa858 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/app_list.feature @@ -0,0 +1,8 @@ +@getter-cli @smoke +Feature: Getter CLI app listing + Scenario: User lists apps before adding any app records + Given an initialized getter data directory + When I run getter app list for that directory + Then the command succeeds + And the output is valid JSON + And the output contains an empty app list diff --git a/crates/getter-cli/tests/features/cli/hub_list.feature b/crates/getter-cli/tests/features/cli/hub_list.feature new file mode 100644 index 0000000..3e467a2 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/hub_list.feature @@ -0,0 +1,8 @@ +@getter-cli @smoke +Feature: Getter CLI hub listing + Scenario: User lists hubs before adding any hub records + Given an initialized getter data directory + When I run getter hub list for that directory + Then the command succeeds + And the output is valid JSON + And the output contains an empty hub list diff --git a/crates/getter-cli/tests/features/cli/init.feature b/crates/getter-cli/tests/features/cli/init.feature new file mode 100644 index 0000000..51cb38a --- /dev/null +++ b/crates/getter-cli/tests/features/cli/init.feature @@ -0,0 +1,8 @@ +@getter-cli @smoke +Feature: Getter CLI initialization + Scenario: User initializes a new getter data directory + Given an empty getter data directory + When I run getter init for that directory + Then the command succeeds + And the output is valid JSON + And the getter data directory is usable diff --git a/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature b/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature new file mode 100644 index 0000000..864417f --- /dev/null +++ b/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature @@ -0,0 +1,18 @@ +@getter-cli @migration +Feature: Legacy import failure recovery + Scenario: User receives a non-destructive report when legacy import fails + Given an initialized getter data directory + And a corrupted legacy export bundle + When I run getter legacy import-room-bundle for that bundle + Then the command fails with a documented migration error + And no partially usable imported state is created + And a sanitized migration report is available + + Scenario: User imports a valid legacy bundle into tracked app state + Given an initialized getter data directory + And a syntactically valid legacy export bundle with an Android app + When I run getter legacy import-room-bundle for that bundle + Then the command succeeds + And the output is valid JSON + And the import reports one tracked app + And the app list contains imported package "android/org.fdroid.fdroid" diff --git a/crates/getter-cli/tests/features/cli/repo_list.feature b/crates/getter-cli/tests/features/cli/repo_list.feature new file mode 100644 index 0000000..0ffd2d8 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/repo_list.feature @@ -0,0 +1,8 @@ +@getter-cli @smoke +Feature: Getter CLI repository listing + Scenario: User lists repositories before adding any repository records + Given an initialized getter data directory + When I run getter repo list for that directory + Then the command succeeds + And the output is valid JSON + And the output contains an empty repository list diff --git a/crates/getter-cli/tests/features/cli/repository_overlay.feature b/crates/getter-cli/tests/features/cli/repository_overlay.feature new file mode 100644 index 0000000..d5906cb --- /dev/null +++ b/crates/getter-cli/tests/features/cli/repository_overlay.feature @@ -0,0 +1,14 @@ +@getter-cli @repository +Feature: Getter CLI repository overlay resolution + Scenario: Highest-priority repository package wins by package id + Given an initialized getter data directory + And a fixture Lua repository "official" with package "android/org.fdroid.fdroid" named "Official F-Droid" + And a fixture Lua repository "local" with package "android/org.fdroid.fdroid" named "Local F-Droid" + When I run getter repo add for repository "official" with priority 0 + Then the command succeeds + When I run getter repo add for repository "local" with priority 100 + Then the command succeeds + When I run getter package eval for package "android/org.fdroid.fdroid" + Then the command succeeds + And the output is valid JSON + And the output contains package "android/org.fdroid.fdroid" named "Local F-Droid" diff --git a/crates/getter-cli/tests/features/cli/repository_package_eval.feature b/crates/getter-cli/tests/features/cli/repository_package_eval.feature new file mode 100644 index 0000000..4edefc2 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/repository_package_eval.feature @@ -0,0 +1,17 @@ +@getter-cli @repository +Feature: Getter CLI repository and package evaluation + Scenario: User adds and evaluates a fixture Lua repository + Given an initialized getter data directory + And a fixture Lua repository "official" with package "android/org.fdroid.fdroid" + When I run getter repo add for that repository with priority 0 + Then the command succeeds + And the output is valid JSON + And the output contains the added repository + When I run getter repo eval for that repository + Then the command succeeds + And the output is valid JSON + And the output contains the evaluated fixture package + When I run getter package eval for that fixture package + Then the command succeeds + And the output is valid JSON + And the output contains the fixture package diff --git a/crates/getter-cli/tests/features/cli/storage_validate.feature b/crates/getter-cli/tests/features/cli/storage_validate.feature new file mode 100644 index 0000000..815edb3 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/storage_validate.feature @@ -0,0 +1,8 @@ +@getter-cli @smoke +Feature: Getter CLI storage validation + Scenario: User validates initialized getter storage + Given an initialized getter data directory + When I run getter storage validate for that directory + Then the command succeeds + And the output is valid JSON + And the output reports valid storage diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml new file mode 100644 index 0000000..47326c5 --- /dev/null +++ b/crates/getter-core/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "getter-core" +version.workspace = true +edition.workspace = true + +[dependencies] +mlua = { version = "0.10", features = ["lua54", "vendored"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "1" +toml = "0.8" + +[dev-dependencies] +tempfile = "3" diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs new file mode 100644 index 0000000..45a464d --- /dev/null +++ b/crates/getter-core/src/lib.rs @@ -0,0 +1,408 @@ +//! Core domain model for the UpgradeAll getter rewrite. +//! +//! `getter-core` owns product/domain behavior. Flutter and Android code are +//! platform/UI adapters and must not reimplement package, repository, update or +//! storage rules. + +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::fmt; +use std::str::FromStr; + +pub mod lua; +pub mod repository; + +/// Error returned when parsing or constructing a [`PackageId`]. +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum PackageIdError { + #[error("package id is empty")] + Empty, + #[error("package id must be '/'")] + MissingSeparator, + #[error("package id kind is empty")] + EmptyKind, + #[error("package id name is empty")] + EmptyName, + #[error("unsupported package kind '{0}'")] + UnsupportedKind(String), + #[error("package id kind '{0}' contains invalid characters")] + InvalidKind(String), + #[error("package id name '{0}' contains invalid characters")] + InvalidName(String), +} + +/// Known package target kinds in v1. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum PackageKind { + Android, + Magisk, + Generic, +} + +impl PackageKind { + pub fn as_str(self) -> &'static str { + match self { + Self::Android => "android", + Self::Magisk => "magisk", + Self::Generic => "generic", + } + } +} + +impl fmt::Display for PackageKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl FromStr for PackageKind { + type Err = PackageIdError; + + fn from_str(value: &str) -> Result { + match value { + "android" => Ok(Self::Android), + "magisk" => Ok(Self::Magisk), + "generic" => Ok(Self::Generic), + other => Err(PackageIdError::UnsupportedKind(other.to_owned())), + } + } +} + +/// Readable package identifier such as `android/org.fdroid.fdroid`. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(try_from = "String", into = "String")] +pub struct PackageId { + kind: PackageKind, + name: String, +} + +impl PackageId { + pub fn new(kind: PackageKind, name: impl Into) -> Result { + let name = name.into(); + validate_name(&name)?; + Ok(Self { kind, name }) + } + + pub fn kind(&self) -> PackageKind { + self.kind + } + + pub fn name(&self) -> &str { + &self.name + } +} + +impl fmt::Display for PackageId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}/{}", self.kind, self.name) + } +} + +impl FromStr for PackageId { + type Err = PackageIdError; + + fn from_str(value: &str) -> Result { + if value.is_empty() { + return Err(PackageIdError::Empty); + } + let (kind, name) = value + .split_once('/') + .ok_or(PackageIdError::MissingSeparator)?; + if kind.is_empty() { + return Err(PackageIdError::EmptyKind); + } + if name.is_empty() { + return Err(PackageIdError::EmptyName); + } + validate_kind_segment(kind)?; + validate_name(name)?; + Ok(Self { + kind: kind.parse()?, + name: name.to_owned(), + }) + } +} + +impl TryFrom for PackageId { + type Error = PackageIdError; + + fn try_from(value: String) -> Result { + value.parse() + } +} + +impl From for String { + fn from(value: PackageId) -> Self { + value.to_string() + } +} + +/// Repository identifier such as `official`, `community`, `local`, or +/// `local_autogen`. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(try_from = "String", into = "String")] +pub struct RepositoryId(String); + +impl RepositoryId { + pub fn new(value: impl Into) -> Result { + let value = value.into(); + validate_repository_id(&value)?; + Ok(Self(value)) + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl fmt::Display for RepositoryId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } +} + +impl FromStr for RepositoryId { + type Err = RepositoryIdError; + + fn from_str(value: &str) -> Result { + Self::new(value) + } +} + +impl TryFrom for RepositoryId { + type Error = RepositoryIdError; + + fn try_from(value: String) -> Result { + Self::new(value) + } +} + +impl From for String { + fn from(value: RepositoryId) -> Self { + value.0 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum RepositoryIdError { + #[error("repository id is empty")] + Empty, + #[error("repository id '{0}' contains invalid characters")] + Invalid(String), +} + +/// Higher repository priority wins during overlay resolution. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct RepositoryPriority(i32); + +impl RepositoryPriority { + pub const LOCAL: Self = Self(100); + pub const DEFAULT: Self = Self(0); + pub const LOCAL_AUTOGEN: Self = Self(-1); + + pub const fn new(value: i32) -> Self { + Self(value) + } + + pub const fn value(self) -> i32 { + self.0 + } + + pub fn cmp_winner(self, other: Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +impl Default for RepositoryPriority { + fn default() -> Self { + Self::DEFAULT + } +} + +impl Ord for RepositoryPriority { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +impl PartialOrd for RepositoryPriority { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Package metadata after repository resolution and Lua validation. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ResolvedPackage { + pub id: PackageId, + pub repository: RepositoryId, + pub name: String, + #[serde(default)] + pub installed: Vec, + #[serde(default)] + pub permissions: PackagePermissions, + #[serde(default)] + pub source_priority: Vec, +} + +/// Installed target matched by a package, such as an Android package name. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum InstalledTarget { + AndroidPackage { package_name: String }, + MagiskModule { module_id: String }, + Generic { id: String }, +} + +/// Package permission declaration relevant to UI warnings and Lua host APIs. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct PackagePermissions { + #[serde(default)] + pub free_network: bool, +} + +/// Candidate version/update discovered by provider/package logic. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct UpdateCandidate { + pub version: String, + #[serde(default)] + pub channel: Option, + #[serde(default)] + pub source: Option, + #[serde(default)] + pub artifacts: Vec, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct UpdateArtifact { + pub name: String, + pub url: String, + #[serde(default)] + pub file_name: Option, +} + +/// Candidate selected for update after package/user-state policy. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SelectedUpdate { + pub package_id: PackageId, + pub candidate: UpdateCandidate, + #[serde(default)] + pub artifact: Option, +} + +/// Executable update actions generated by the `resolve` lifecycle phase. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum UpdateAction { + Download { url: String, file_name: String }, + Install { installer: String, file: String }, + OpenUrl { url: String }, +} + +fn validate_kind_segment(value: &str) -> Result<(), PackageIdError> { + if value + .chars() + .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' || c == '-') + { + Ok(()) + } else { + Err(PackageIdError::InvalidKind(value.to_owned())) + } +} + +fn validate_name(value: &str) -> Result<(), PackageIdError> { + if value.is_empty() { + return Err(PackageIdError::EmptyName); + } + if value.starts_with('/') || value.ends_with('/') || value.contains("//") { + return Err(PackageIdError::InvalidName(value.to_owned())); + } + if value + .chars() + .all(|c| c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | '-' | '+' | '/')) + { + Ok(()) + } else { + Err(PackageIdError::InvalidName(value.to_owned())) + } +} + +fn validate_repository_id(value: &str) -> Result<(), RepositoryIdError> { + if value.is_empty() { + return Err(RepositoryIdError::Empty); + } + if value + .chars() + .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-' | '.')) + { + Ok(()) + } else { + Err(RepositoryIdError::Invalid(value.to_owned())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_readable_android_package_id() { + let id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + assert_eq!(id.kind(), PackageKind::Android); + assert_eq!(id.name(), "org.fdroid.fdroid"); + assert_eq!(id.to_string(), "android/org.fdroid.fdroid"); + } + + #[test] + fn parses_readable_magisk_package_id() { + let id: PackageId = "magisk/zygisk-next".parse().unwrap(); + assert_eq!(id.kind(), PackageKind::Magisk); + assert_eq!(id.name(), "zygisk-next"); + assert_eq!(id.to_string(), "magisk/zygisk-next"); + } + + #[test] + fn rejects_invalid_package_ids() { + assert_eq!("".parse::(), Err(PackageIdError::Empty)); + assert_eq!( + "android".parse::(), + Err(PackageIdError::MissingSeparator) + ); + assert_eq!( + "/org.fdroid.fdroid".parse::(), + Err(PackageIdError::EmptyKind) + ); + assert_eq!( + "android/".parse::(), + Err(PackageIdError::EmptyName) + ); + assert_eq!( + "hub/org.fdroid.fdroid".parse::(), + Err(PackageIdError::UnsupportedKind("hub".to_owned())) + ); + assert!("android/org fdroid".parse::().is_err()); + } + + #[test] + fn repository_priority_higher_number_wins() { + assert!(RepositoryPriority::LOCAL > RepositoryPriority::DEFAULT); + assert!(RepositoryPriority::DEFAULT > RepositoryPriority::LOCAL_AUTOGEN); + assert_eq!(RepositoryPriority::LOCAL.value(), 100); + assert_eq!(RepositoryPriority::DEFAULT.value(), 0); + assert_eq!(RepositoryPriority::LOCAL_AUTOGEN.value(), -1); + } + + #[test] + fn repository_id_accepts_named_repositories() { + assert_eq!( + RepositoryId::new("local_autogen").unwrap().as_str(), + "local_autogen" + ); + assert_eq!( + RepositoryId::new("community.1").unwrap().to_string(), + "community.1" + ); + assert!(RepositoryId::new("bad/repo").is_err()); + } +} diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs new file mode 100644 index 0000000..fd6348e --- /dev/null +++ b/crates/getter-core/src/lua.rs @@ -0,0 +1,460 @@ +//! Minimal Lua package-file evaluation and Rust validation boundary. + +use crate::repository::RepositoryLayout; +use crate::{InstalledTarget, PackageId, PackagePermissions, ResolvedPackage}; +use mlua::{Lua, Table, Value}; +use serde_json::{Map, Number, Value as JsonValue}; +use std::fs; +use std::path::{Path, PathBuf}; + +#[derive(Debug, thiserror::Error)] +pub enum LuaPackageError { + #[error("failed to read Lua package file {path}: {source}")] + ReadFile { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("Lua runtime error in {path}: {source}")] + Runtime { + path: PathBuf, + #[source] + source: mlua::Error, + }, + #[error("Lua package {path} did not return a table")] + NotATable { path: PathBuf }, + #[error("Lua package {path} returned unsupported value at {location}: {value_type}")] + UnsupportedValue { + path: PathBuf, + location: String, + value_type: &'static str, + }, + #[error("schema validation failed for {path}: {message}")] + Schema { path: PathBuf, message: String }, + #[error("domain validation failed for {path}: {message}")] + Domain { path: PathBuf, message: String }, +} + +/// Evaluate and validate a Lua package file from a repository layout. +pub fn evaluate_package_file( + repository: &RepositoryLayout, + path: impl AsRef, +) -> Result { + let path = path.as_ref(); + let source = fs::read_to_string(path).map_err(|source| LuaPackageError::ReadFile { + path: path.to_path_buf(), + source, + })?; + evaluate_package_source(repository, path, &source) +} + +/// Evaluate package source text. This is public for focused tests and future CLI +/// plumbing; repository callers should normally use [`evaluate_package_file`]. +pub fn evaluate_package_source( + repository: &RepositoryLayout, + path: impl AsRef, + source: &str, +) -> Result { + let path = path.as_ref().to_path_buf(); + let lua = Lua::new(); + configure_package_path(&lua, repository).map_err(|source| LuaPackageError::Runtime { + path: path.clone(), + source, + })?; + install_helpers(&lua).map_err(|source| LuaPackageError::Runtime { + path: path.clone(), + source, + })?; + + let value = lua + .load(source) + .set_name(path.to_string_lossy().as_ref()) + .eval::() + .map_err(|source| LuaPackageError::Runtime { + path: path.clone(), + source, + })?; + let table = match value { + Value::Table(table) => table, + _ => return Err(LuaPackageError::NotATable { path }), + }; + let json = lua_table_to_json(&path, "$", table)?; + validate_package_json(repository, &path, json) +} + +fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Result<()> { + let package: Table = lua.globals().get("package")?; + let current_path: String = package.get("path")?; + let lib_pattern = repository.lib_dir.join("?.lua"); + let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); + let new_path = format!( + "{};{};{}", + lib_pattern.to_string_lossy(), + nested_lib_pattern.to_string_lossy(), + current_path + ); + package.set("path", new_path) +} + +fn install_helpers(lua: &Lua) -> mlua::Result<()> { + let package_fn = lua.create_function(|_, table: Table| Ok(table))?; + lua.globals().set("package_def", package_fn.clone())?; + lua.globals().set("android_app", package_fn.clone())?; + lua.globals().set("magisk_module", package_fn.clone())?; + lua.globals().set("generic_package", package_fn)?; + Ok(()) +} + +fn lua_table_to_json( + path: &Path, + location: &str, + table: Table, +) -> Result { + if is_array_table(&table).map_err(|source| LuaPackageError::Runtime { + path: path.to_path_buf(), + source, + })? { + let mut array = Vec::new(); + for pair in table.sequence_values::() { + let value = pair.map_err(|source| LuaPackageError::Runtime { + path: path.to_path_buf(), + source, + })?; + array.push(lua_value_to_json(path, &format!("{location}[]"), value)?); + } + Ok(JsonValue::Array(array)) + } else { + let mut object = Map::new(); + for pair in table.pairs::() { + let (key, value) = pair.map_err(|source| LuaPackageError::Runtime { + path: path.to_path_buf(), + source, + })?; + let key = match key { + Value::String(value) => value + .to_str() + .map_err(|source| LuaPackageError::Runtime { + path: path.to_path_buf(), + source, + })? + .to_owned(), + Value::Integer(value) => value.to_string(), + _ => { + return Err(LuaPackageError::UnsupportedValue { + path: path.to_path_buf(), + location: format!("{location}."), + value_type: key.type_name(), + }) + } + }; + let child_location = format!("{location}.{key}"); + object.insert(key, lua_value_to_json(path, &child_location, value)?); + } + Ok(JsonValue::Object(object)) + } +} + +fn lua_value_to_json( + path: &Path, + location: &str, + value: Value, +) -> Result { + match value { + Value::Nil => Ok(JsonValue::Null), + Value::Boolean(value) => Ok(JsonValue::Bool(value)), + Value::Integer(value) => Ok(JsonValue::Number(Number::from(value))), + Value::Number(value) => Number::from_f64(value) + .map(JsonValue::Number) + .ok_or_else(|| LuaPackageError::UnsupportedValue { + path: path.to_path_buf(), + location: location.to_owned(), + value_type: "non-finite number", + }), + Value::String(value) => Ok(JsonValue::String( + value + .to_str() + .map_err(|source| LuaPackageError::Runtime { + path: path.to_path_buf(), + source, + })? + .to_owned(), + )), + Value::Table(table) => lua_table_to_json(path, location, table), + _ => Err(LuaPackageError::UnsupportedValue { + path: path.to_path_buf(), + location: location.to_owned(), + value_type: value.type_name(), + }), + } +} + +fn is_array_table(table: &Table) -> mlua::Result { + let len = table.raw_len(); + if len == 0 { + // Empty tables are accepted as objects by default. Package schemas avoid + // ambiguous empty array/object requirements at this boundary. + return Ok(false); + } + let mut count = 0usize; + for pair in table.clone().pairs::() { + let (key, _) = pair?; + match key { + Value::Integer(index) if index >= 1 && (index as usize) <= len => count += 1, + _ => return Ok(false), + } + } + Ok(count == len) +} + +fn validate_package_json( + repository: &RepositoryLayout, + path: &Path, + value: JsonValue, +) -> Result { + let object = value.as_object().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "package value must be an object".to_owned(), + })?; + + let id = required_string(path, object, "id")?; + let id: PackageId = id.parse().map_err(|source| LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("invalid package id: {source}"), + })?; + let path_id = crate::repository::package_id_from_path(&repository.packages_dir, path).map_err( + |source| LuaPackageError::Domain { + path: path.to_path_buf(), + message: format!("failed to derive package id from path: {source}"), + }, + )?; + if id != path_id { + return Err(LuaPackageError::Domain { + path: path.to_path_buf(), + message: format!("package id '{id}' does not match path-derived id '{path_id}'"), + }); + } + + let name = required_string(path, object, "name")?.to_owned(); + let installed = parse_installed_targets(path, object.get("installed"))?; + let permissions = parse_permissions(path, object.get("permissions"))?; + let source_priority = + parse_string_array(path, "source_priority", object.get("source_priority"))?; + + Ok(ResolvedPackage { + id, + repository: repository.metadata.id.clone(), + name, + installed, + permissions, + source_priority, + }) +} + +fn required_string<'a>( + path: &Path, + object: &'a Map, + field: &str, +) -> Result<&'a str, LuaPackageError> { + object + .get(field) + .and_then(JsonValue::as_str) + .ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("required string field '{field}' is missing"), + }) +} + +fn parse_installed_targets( + path: &Path, + value: Option<&JsonValue>, +) -> Result, LuaPackageError> { + let Some(value) = value else { + return Ok(Vec::new()); + }; + let array = value.as_array().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "field 'installed' must be an array".to_owned(), + })?; + array + .iter() + .map(|item| { + let object = item.as_object().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "installed entries must be objects".to_owned(), + })?; + let kind = required_string(path, object, "kind")?; + match kind { + "android_package" => Ok(InstalledTarget::AndroidPackage { + package_name: required_string(path, object, "package_name")?.to_owned(), + }), + "magisk_module" => Ok(InstalledTarget::MagiskModule { + module_id: required_string(path, object, "module_id")?.to_owned(), + }), + "generic" => Ok(InstalledTarget::Generic { + id: required_string(path, object, "id")?.to_owned(), + }), + other => Err(LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("unknown installed target kind '{other}'"), + }), + } + }) + .collect() +} + +fn parse_permissions( + path: &Path, + value: Option<&JsonValue>, +) -> Result { + let Some(value) = value else { + return Ok(PackagePermissions::default()); + }; + let object = value.as_object().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "field 'permissions' must be an object".to_owned(), + })?; + Ok(PackagePermissions { + free_network: object + .get("free_network") + .and_then(JsonValue::as_bool) + .unwrap_or(false), + }) +} + +fn parse_string_array( + path: &Path, + field: &str, + value: Option<&JsonValue>, +) -> Result, LuaPackageError> { + let Some(value) = value else { + return Ok(Vec::new()); + }; + let array = value.as_array().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("field '{field}' must be an array"), + })?; + array + .iter() + .map(|item| { + item.as_str() + .map(str::to_owned) + .ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("field '{field}' must contain only strings"), + }) + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::repository::RepositoryLayout; + use std::fs; + + fn fixture_repo() -> (tempfile::TempDir, RepositoryLayout, PathBuf) { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::write( + root.join("repo.toml"), + r#"id = "official" +name = "UpgradeAll Official" +priority = 0 +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + fs::create_dir(root.join("packages")).unwrap(); + fs::create_dir(root.join("packages/android")).unwrap(); + fs::create_dir(root.join("lib")).unwrap(); + fs::create_dir(root.join("templates")).unwrap(); + let package_path = root.join("packages/android/org.fdroid.fdroid.lua"); + fs::write(&package_path, "return {}").unwrap(); + let layout = RepositoryLayout::load(root).unwrap(); + (temp, layout, package_path) + } + + #[test] + fn evaluates_json_like_lua_package_table() { + let (_temp, layout, package_path) = fixture_repo(); + fs::write( + &package_path, + r#" +return package_def { + id = "android/org.fdroid.fdroid", + name = "F-Droid", + installed = { + { kind = "android_package", package_name = "org.fdroid.fdroid" }, + }, + permissions = { free_network = true }, + source_priority = { "github", "fdroid" }, +} +"#, + ) + .unwrap(); + + let package = evaluate_package_file(&layout, &package_path).unwrap(); + assert_eq!(package.id.to_string(), "android/org.fdroid.fdroid"); + assert_eq!(package.repository.as_str(), "official"); + assert_eq!(package.name, "F-Droid"); + assert_eq!( + package.installed, + vec![InstalledTarget::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned() + }] + ); + assert!(package.permissions.free_network); + assert_eq!(package.source_priority, vec!["github", "fdroid"]); + } + + #[test] + fn rejects_package_id_that_does_not_match_path() { + let (_temp, layout, package_path) = fixture_repo(); + fs::write( + &package_path, + r#"return { id = "android/com.termux", name = "Termux" }"#, + ) + .unwrap(); + + let err = evaluate_package_file(&layout, &package_path).unwrap_err(); + assert!(matches!(err, LuaPackageError::Domain { .. })); + } + + #[test] + fn require_can_load_repository_lib_modules() { + let (_temp, layout, package_path) = fixture_repo(); + fs::write( + layout.lib_dir.join("android.lua"), + r#" +return { + local_app = function(input) + return { + id = input.id, + name = input.name, + installed = { + { kind = "android_package", package_name = input.package_name }, + }, + } + end +} +"#, + ) + .unwrap(); + fs::write( + &package_path, + r#" +local android = require("android") +return android.local_app { + id = "android/org.fdroid.fdroid", + name = "F-Droid", + package_name = "org.fdroid.fdroid", +} +"#, + ) + .unwrap(); + + let package = evaluate_package_file(&layout, &package_path).unwrap(); + assert_eq!(package.name, "F-Droid"); + assert_eq!(package.installed.len(), 1); + } +} diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs new file mode 100644 index 0000000..0bacf47 --- /dev/null +++ b/crates/getter-core/src/repository.rs @@ -0,0 +1,288 @@ +//! Repository layout loading for Lua package repositories. + +use crate::{PackageId, PackageIdError, RepositoryId, RepositoryIdError, RepositoryPriority}; +use serde::Deserialize; +use std::fs; +use std::path::{Path, PathBuf}; + +pub const REPO_API_VERSION_V1: &str = "getter.repo.v1"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryLayout { + pub root: PathBuf, + pub metadata: RepositoryMetadata, + pub packages_dir: PathBuf, + pub lib_dir: PathBuf, + pub templates_dir: PathBuf, + pub packages: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryMetadata { + pub id: RepositoryId, + pub name: String, + pub priority: RepositoryPriority, + pub api_version: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PackageFile { + pub id: PackageId, + pub path: PathBuf, +} + +#[derive(Debug, thiserror::Error)] +pub enum RepositoryLoadError { + #[error("failed to read repo.toml at {path}: {source}")] + ReadRepoToml { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("failed to parse repo.toml at {path}: {source}")] + ParseRepoToml { + path: PathBuf, + #[source] + source: toml::de::Error, + }, + #[error("invalid repository id in repo.toml: {0}")] + RepositoryId(#[from] RepositoryIdError), + #[error("unsupported repository api_version '{0}'")] + UnsupportedApiVersion(String), + #[error("repository path {path} is missing required directory '{directory}'")] + MissingDirectory { + path: PathBuf, + directory: &'static str, + }, + #[error("failed to read packages directory {path}: {source}")] + ReadPackagesDir { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("invalid package path {path}: {reason}")] + InvalidPackagePath { path: PathBuf, reason: String }, + #[error("invalid package id derived from {path}: {source}")] + PackageId { + path: PathBuf, + #[source] + source: PackageIdError, + }, +} + +impl RepositoryLayout { + pub fn load(root: impl AsRef) -> Result { + let root = root.as_ref().to_path_buf(); + let repo_toml_path = root.join("repo.toml"); + let raw = fs::read_to_string(&repo_toml_path).map_err(|source| { + RepositoryLoadError::ReadRepoToml { + path: repo_toml_path.clone(), + source, + } + })?; + let raw_metadata: RawRepositoryMetadata = + toml::from_str(&raw).map_err(|source| RepositoryLoadError::ParseRepoToml { + path: repo_toml_path.clone(), + source, + })?; + let api_version = raw_metadata.api_version; + if api_version != REPO_API_VERSION_V1 { + return Err(RepositoryLoadError::UnsupportedApiVersion(api_version)); + } + let metadata = RepositoryMetadata { + id: RepositoryId::new(raw_metadata.id)?, + name: raw_metadata.name, + priority: RepositoryPriority::new(raw_metadata.priority.unwrap_or_default()), + api_version, + }; + + let packages_dir = root.join("packages"); + let lib_dir = root.join("lib"); + let templates_dir = root.join("templates"); + require_dir(&root, &packages_dir, "packages")?; + require_dir(&root, &lib_dir, "lib")?; + require_dir(&root, &templates_dir, "templates")?; + + let mut packages = Vec::new(); + collect_package_files(&packages_dir, &packages_dir, &mut packages)?; + packages.sort_by(|a, b| a.id.to_string().cmp(&b.id.to_string())); + + Ok(Self { + root, + metadata, + packages_dir, + lib_dir, + templates_dir, + packages, + }) + } + + pub fn package_file(&self, id: &PackageId) -> Option<&PackageFile> { + self.packages.iter().find(|package| &package.id == id) + } +} + +#[derive(Debug, Deserialize)] +struct RawRepositoryMetadata { + id: String, + name: String, + #[serde(default)] + priority: Option, + api_version: String, +} + +fn require_dir( + root: &Path, + path: &Path, + directory: &'static str, +) -> Result<(), RepositoryLoadError> { + if path.is_dir() { + Ok(()) + } else { + Err(RepositoryLoadError::MissingDirectory { + path: root.to_path_buf(), + directory, + }) + } +} + +fn collect_package_files( + packages_root: &Path, + current: &Path, + out: &mut Vec, +) -> Result<(), RepositoryLoadError> { + for entry in fs::read_dir(current).map_err(|source| RepositoryLoadError::ReadPackagesDir { + path: current.to_path_buf(), + source, + })? { + let entry = entry.map_err(|source| RepositoryLoadError::ReadPackagesDir { + path: current.to_path_buf(), + source, + })?; + let path = entry.path(); + let file_type = + entry + .file_type() + .map_err(|source| RepositoryLoadError::ReadPackagesDir { + path: current.to_path_buf(), + source, + })?; + if file_type.is_dir() { + collect_package_files(packages_root, &path, out)?; + } else if file_type.is_file() && path.extension().is_some_and(|ext| ext == "lua") { + let id = package_id_from_path(packages_root, &path)?; + out.push(PackageFile { id, path }); + } + } + Ok(()) +} + +pub fn package_id_from_path( + packages_root: impl AsRef, + path: impl AsRef, +) -> Result { + let packages_root = packages_root.as_ref(); + let path = path.as_ref(); + let relative = + path.strip_prefix(packages_root) + .map_err(|_| RepositoryLoadError::InvalidPackagePath { + path: path.to_path_buf(), + reason: format!("path is not under {}", packages_root.display()), + })?; + if relative.extension().is_none_or(|ext| ext != "lua") { + return Err(RepositoryLoadError::InvalidPackagePath { + path: path.to_path_buf(), + reason: "package file must have .lua extension".to_owned(), + }); + } + let without_extension = relative.with_extension(""); + let mut parts = without_extension.components(); + let kind = parts + .next() + .ok_or_else(|| RepositoryLoadError::InvalidPackagePath { + path: path.to_path_buf(), + reason: "missing package kind directory".to_owned(), + })? + .as_os_str() + .to_string_lossy() + .into_owned(); + let name_path: PathBuf = parts.collect(); + let name = name_path + .to_string_lossy() + .replace(std::path::MAIN_SEPARATOR, "/"); + if name.is_empty() { + return Err(RepositoryLoadError::InvalidPackagePath { + path: path.to_path_buf(), + reason: "missing package name".to_owned(), + }); + } + format!("{kind}/{name}") + .parse() + .map_err(|source| RepositoryLoadError::PackageId { + path: path.to_path_buf(), + source, + }) +} + +pub fn highest_priority<'a, T, F>(items: &'a [T], priority: F) -> Option<&'a T> +where + F: Fn(&T) -> RepositoryPriority, +{ + items.iter().max_by_key(|item| priority(item)) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Write; + + #[test] + fn derives_package_id_from_lua_path() { + let root = PathBuf::from("repo/packages"); + let id = + package_id_from_path(&root, "repo/packages/android/org.fdroid.fdroid.lua").unwrap(); + assert_eq!(id.to_string(), "android/org.fdroid.fdroid"); + } + + #[test] + fn loads_repository_layout_with_required_directories() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::write( + root.join("repo.toml"), + r#"id = "official" +name = "UpgradeAll Official" +priority = 0 +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + fs::create_dir(root.join("packages")).unwrap(); + fs::create_dir(root.join("packages/android")).unwrap(); + fs::create_dir(root.join("lib")).unwrap(); + fs::create_dir(root.join("templates")).unwrap(); + let mut file = + fs::File::create(root.join("packages/android/org.fdroid.fdroid.lua")).unwrap(); + writeln!(file, "return {{ id = 'android/org.fdroid.fdroid' }}").unwrap(); + + let layout = RepositoryLayout::load(root).unwrap(); + assert_eq!(layout.metadata.id.as_str(), "official"); + assert_eq!(layout.metadata.priority, RepositoryPriority::DEFAULT); + assert_eq!(layout.packages.len(), 1); + assert_eq!( + layout.packages[0].id.to_string(), + "android/org.fdroid.fdroid" + ); + } + + #[test] + fn highest_priority_selects_larger_number() { + let priorities = [ + RepositoryPriority::LOCAL_AUTOGEN, + RepositoryPriority::DEFAULT, + RepositoryPriority::LOCAL, + ]; + let selected = highest_priority(&priorities, |priority| *priority).unwrap(); + assert_eq!(*selected, RepositoryPriority::LOCAL); + } +} diff --git a/crates/getter-downloader/Cargo.toml b/crates/getter-downloader/Cargo.toml new file mode 100644 index 0000000..d8b2562 --- /dev/null +++ b/crates/getter-downloader/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "getter-downloader" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } diff --git a/crates/getter-downloader/src/lib.rs b/crates/getter-downloader/src/lib.rs new file mode 100644 index 0000000..4a11c7d --- /dev/null +++ b/crates/getter-downloader/src/lib.rs @@ -0,0 +1,3 @@ +//! getter-downloader rewrite crate skeleton. + +pub use getter_core as core; diff --git a/crates/getter-ffi/Cargo.toml b/crates/getter-ffi/Cargo.toml new file mode 100644 index 0000000..fa1c2cf --- /dev/null +++ b/crates/getter-ffi/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "getter-ffi" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } diff --git a/crates/getter-ffi/src/lib.rs b/crates/getter-ffi/src/lib.rs new file mode 100644 index 0000000..b7dde13 --- /dev/null +++ b/crates/getter-ffi/src/lib.rs @@ -0,0 +1,3 @@ +//! getter-ffi rewrite crate skeleton. + +pub use getter_core as core; diff --git a/crates/getter-plugin-api/Cargo.toml b/crates/getter-plugin-api/Cargo.toml new file mode 100644 index 0000000..4f41ccf --- /dev/null +++ b/crates/getter-plugin-api/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "getter-plugin-api" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } diff --git a/crates/getter-plugin-api/src/lib.rs b/crates/getter-plugin-api/src/lib.rs new file mode 100644 index 0000000..52cd1d1 --- /dev/null +++ b/crates/getter-plugin-api/src/lib.rs @@ -0,0 +1,3 @@ +//! getter-plugin-api rewrite crate skeleton. + +pub use getter_core as core; diff --git a/crates/getter-providers/Cargo.toml b/crates/getter-providers/Cargo.toml new file mode 100644 index 0000000..5a44be9 --- /dev/null +++ b/crates/getter-providers/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "getter-providers" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs new file mode 100644 index 0000000..35371aa --- /dev/null +++ b/crates/getter-providers/src/lib.rs @@ -0,0 +1,3 @@ +//! getter-providers rewrite crate skeleton. + +pub use getter_core as core; diff --git a/crates/getter-rpc/Cargo.toml b/crates/getter-rpc/Cargo.toml new file mode 100644 index 0000000..6ab6be0 --- /dev/null +++ b/crates/getter-rpc/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "getter-rpc" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } diff --git a/crates/getter-rpc/src/lib.rs b/crates/getter-rpc/src/lib.rs new file mode 100644 index 0000000..ceb75d9 --- /dev/null +++ b/crates/getter-rpc/src/lib.rs @@ -0,0 +1,3 @@ +//! getter-rpc rewrite crate skeleton. + +pub use getter_core as core; diff --git a/crates/getter-storage/Cargo.toml b/crates/getter-storage/Cargo.toml new file mode 100644 index 0000000..6a561a4 --- /dev/null +++ b/crates/getter-storage/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "getter-storage" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core" } +rusqlite = { version = "0.32", features = ["bundled"] } +thiserror = "1" + +[dev-dependencies] +tempfile = "3" diff --git a/crates/getter-storage/src/legacy_room.rs b/crates/getter-storage/src/legacy_room.rs new file mode 100644 index 0000000..798809e --- /dev/null +++ b/crates/getter-storage/src/legacy_room.rs @@ -0,0 +1,215 @@ +//! Legacy Room migration mapping tests and pure mapping helpers. +//! +//! This module intentionally contains no Android Room/database reader. It is the +//! TDD boundary for source->target mapping rules before the full migration +//! implementation is added. + +use getter_core::PackageId; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LegacyAppKind { + Android, + Magisk, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LegacyAppRecord { + pub kind: LegacyAppKind, + pub installed_id: String, + pub official_package_available: bool, + pub common_conversion_available: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LegacyExtraAppRecord { + pub ignored_version: Option, + pub favorite: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LegacyAppMapping { + pub package_id: PackageId, + pub package_resolution: LegacyPackageResolution, + pub user_state: LegacyUserStateMapping, + pub warnings: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LegacyPackageResolution { + OfficialRepositoryPackage, + GenerateLocalPackage, + MissingPackageDefinition, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct LegacyUserStateMapping { + pub ignored_version: Option, + pub favorite: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum LegacyMigrationWarning { + MissingPackageDefinition { package_id: PackageId }, +} + +#[derive(Debug, thiserror::Error)] +pub enum LegacyRoomMappingError { + #[error("legacy installed id is empty")] + EmptyInstalledId, + #[error("failed to construct target package id: {0}")] + PackageId(#[from] getter_core::PackageIdError), +} + +pub fn map_legacy_app( + app: &LegacyAppRecord, + extra: Option<&LegacyExtraAppRecord>, +) -> Result { + let package_id = map_legacy_package_id(app.kind, &app.installed_id)?; + let package_resolution = if app.official_package_available { + LegacyPackageResolution::OfficialRepositoryPackage + } else if app.common_conversion_available { + LegacyPackageResolution::GenerateLocalPackage + } else { + LegacyPackageResolution::MissingPackageDefinition + }; + let warnings = match package_resolution { + LegacyPackageResolution::MissingPackageDefinition => { + vec![LegacyMigrationWarning::MissingPackageDefinition { + package_id: package_id.clone(), + }] + } + _ => Vec::new(), + }; + let user_state = LegacyUserStateMapping { + ignored_version: extra.and_then(|extra| extra.ignored_version.clone()), + favorite: extra.is_some_and(|extra| extra.favorite), + }; + + Ok(LegacyAppMapping { + package_id, + package_resolution, + user_state, + warnings, + }) +} + +pub fn map_legacy_package_id( + kind: LegacyAppKind, + installed_id: &str, +) -> Result { + if installed_id.is_empty() { + return Err(LegacyRoomMappingError::EmptyInstalledId); + } + let prefix = match kind { + LegacyAppKind::Android => "android", + LegacyAppKind::Magisk => "magisk", + }; + Ok(format!("{prefix}/{installed_id}").parse()?) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn android_legacy_app_maps_to_readable_android_package_id() { + let mapping = map_legacy_app( + &LegacyAppRecord { + kind: LegacyAppKind::Android, + installed_id: "org.fdroid.fdroid".to_owned(), + official_package_available: true, + common_conversion_available: false, + }, + None, + ) + .unwrap(); + + assert_eq!(mapping.package_id.to_string(), "android/org.fdroid.fdroid"); + assert_eq!( + mapping.package_resolution, + LegacyPackageResolution::OfficialRepositoryPackage + ); + assert!(mapping.warnings.is_empty()); + } + + #[test] + fn magisk_legacy_app_maps_to_readable_magisk_package_id() { + let mapping = map_legacy_app( + &LegacyAppRecord { + kind: LegacyAppKind::Magisk, + installed_id: "zygisk-next".to_owned(), + official_package_available: true, + common_conversion_available: false, + }, + None, + ) + .unwrap(); + + assert_eq!(mapping.package_id.to_string(), "magisk/zygisk-next"); + } + + #[test] + fn common_unofficial_conversion_generates_local_package() { + let mapping = map_legacy_app( + &LegacyAppRecord { + kind: LegacyAppKind::Android, + installed_id: "com.example.private".to_owned(), + official_package_available: false, + common_conversion_available: true, + }, + None, + ) + .unwrap(); + + assert_eq!( + mapping.package_resolution, + LegacyPackageResolution::GenerateLocalPackage + ); + assert!(mapping.warnings.is_empty()); + } + + #[test] + fn unmapped_complex_app_preserves_id_and_records_missing_definition_warning() { + let mapping = map_legacy_app( + &LegacyAppRecord { + kind: LegacyAppKind::Android, + installed_id: "com.example.unmapped".to_owned(), + official_package_available: false, + common_conversion_available: false, + }, + None, + ) + .unwrap(); + + assert_eq!( + mapping.package_resolution, + LegacyPackageResolution::MissingPackageDefinition + ); + assert_eq!( + mapping.warnings, + vec![LegacyMigrationWarning::MissingPackageDefinition { + package_id: "android/com.example.unmapped".parse().unwrap(), + }] + ); + } + + #[test] + fn extra_app_ignore_and_favorite_state_are_preserved_when_present() { + let mapping = map_legacy_app( + &LegacyAppRecord { + kind: LegacyAppKind::Android, + installed_id: "org.fdroid.fdroid".to_owned(), + official_package_available: true, + common_conversion_available: false, + }, + Some(&LegacyExtraAppRecord { + ignored_version: Some("1.2.3".to_owned()), + favorite: true, + }), + ) + .unwrap(); + + assert_eq!(mapping.user_state.ignored_version.as_deref(), Some("1.2.3")); + assert!(mapping.user_state.favorite); + } +} diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs new file mode 100644 index 0000000..6eed591 --- /dev/null +++ b/crates/getter-storage/src/lib.rs @@ -0,0 +1,491 @@ +//! SQLite storage skeleton for getter main/cache databases. + +pub mod legacy_room; + +use getter_core::repository::RepositoryMetadata; +use getter_core::{PackageId, RepositoryId, RepositoryPriority}; +use rusqlite::{params, Connection}; +use std::path::Path; +use std::str::FromStr; + +#[derive(Debug, thiserror::Error)] +pub enum StorageError { + #[error("sqlite error: {0}")] + Sqlite(#[from] rusqlite::Error), + #[error("invalid package id in database: {0}")] + PackageId(#[from] getter_core::PackageIdError), + #[error("invalid repository id in database: {0}")] + RepositoryId(#[from] getter_core::RepositoryIdError), + #[error("invalid package resolution in database: {0}")] + PackageResolution(String), +} + +pub struct MainDb { + conn: Connection, +} + +pub struct CacheDb { + conn: Connection, +} + +impl MainDb { + pub fn open(path: impl AsRef) -> Result { + let conn = Connection::open(path)?; + let db = Self { conn }; + db.migrate()?; + Ok(db) + } + + pub fn open_in_memory() -> Result { + let conn = Connection::open_in_memory()?; + let db = Self { conn }; + db.migrate()?; + Ok(db) + } + + fn migrate(&self) -> Result<(), StorageError> { + self.conn.execute_batch( + r#" +PRAGMA foreign_keys = ON; + +CREATE TABLE IF NOT EXISTS schema_migrations ( + id TEXT PRIMARY KEY, + applied_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); + +CREATE TABLE IF NOT EXISTS repositories ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + priority INTEGER NOT NULL, + api_version TEXT NOT NULL, + path TEXT, + revision TEXT +); + +CREATE TABLE IF NOT EXISTS tracked_packages ( + package_id TEXT PRIMARY KEY, + enabled INTEGER NOT NULL DEFAULT 1, + favorite INTEGER NOT NULL DEFAULT 0, + ignored_version TEXT, + repository_id TEXT, + package_resolution TEXT NOT NULL DEFAULT 'missing_package_definition', + FOREIGN KEY(repository_id) REFERENCES repositories(id) +); + +CREATE TABLE IF NOT EXISTS migration_records ( + id TEXT PRIMARY KEY, + source TEXT NOT NULL, + completed_at_unix INTEGER NOT NULL DEFAULT (unixepoch()), + report_json TEXT NOT NULL DEFAULT '{}' +); +"#, + )?; + self.ensure_column("tracked_packages", "ignored_version", "TEXT")?; + self.ensure_column( + "tracked_packages", + "package_resolution", + "TEXT NOT NULL DEFAULT 'missing_package_definition'", + )?; + self.conn.execute( + "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('main-v1')", + [], + )?; + Ok(()) + } + + fn ensure_column( + &self, + table: &'static str, + column: &'static str, + definition: &'static str, + ) -> Result<(), StorageError> { + let columns = self.table_columns(table)?; + if !columns.iter().any(|existing| existing == column) { + self.conn.execute_batch(&format!( + "ALTER TABLE {table} ADD COLUMN {column} {definition};" + ))?; + } + Ok(()) + } + + fn table_columns(&self, table: &'static str) -> Result, StorageError> { + let mut stmt = self.conn.prepare(&format!("PRAGMA table_info({table})"))?; + let rows = stmt.query_map([], |row| row.get::<_, String>(1))?; + let mut columns = Vec::new(); + for row in rows { + columns.push(row?); + } + Ok(columns) + } + + pub fn upsert_repository( + &self, + metadata: &RepositoryMetadata, + path: Option<&Path>, + revision: Option<&str>, + ) -> Result<(), StorageError> { + self.conn.execute( + r#" +INSERT INTO repositories(id, name, priority, api_version, path, revision) +VALUES (?1, ?2, ?3, ?4, ?5, ?6) +ON CONFLICT(id) DO UPDATE SET + name = excluded.name, + priority = excluded.priority, + api_version = excluded.api_version, + path = excluded.path, + revision = excluded.revision +"#, + params![ + metadata.id.as_str(), + metadata.name, + metadata.priority.value(), + metadata.api_version, + path.map(|p| p.to_string_lossy().to_string()), + revision, + ], + )?; + Ok(()) + } + + pub fn repositories(&self) -> Result, StorageError> { + let mut stmt = self.conn.prepare( + "SELECT id, name, priority, api_version, path, revision FROM repositories ORDER BY priority DESC, id ASC", + )?; + let rows = stmt.query_map([], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, i32>(2)?, + row.get::<_, String>(3)?, + row.get::<_, Option>(4)?, + row.get::<_, Option>(5)?, + )) + })?; + let mut repositories = Vec::new(); + for row in rows { + let (id, name, priority, api_version, path, revision) = row?; + repositories.push(StoredRepository { + id: RepositoryId::new(id)?, + name, + priority: RepositoryPriority::new(priority), + api_version, + path, + revision, + }); + } + Ok(repositories) + } + + pub fn upsert_tracked_package( + &self, + package: &TrackedPackageUpsert, + ) -> Result<(), StorageError> { + self.conn.execute( + r#" +INSERT INTO tracked_packages( + package_id, + enabled, + favorite, + ignored_version, + repository_id, + package_resolution +) +VALUES (?1, ?2, ?3, ?4, ?5, ?6) +ON CONFLICT(package_id) DO UPDATE SET + enabled = excluded.enabled, + favorite = excluded.favorite, + ignored_version = excluded.ignored_version, + repository_id = excluded.repository_id, + package_resolution = excluded.package_resolution +"#, + params![ + package.package_id.to_string(), + bool_to_i64(package.enabled), + bool_to_i64(package.favorite), + package.ignored_version.as_deref(), + package.repository_id.as_ref().map(RepositoryId::as_str), + package.package_resolution.as_str(), + ], + )?; + Ok(()) + } + + pub fn tracked_packages(&self) -> Result, StorageError> { + let mut stmt = self.conn.prepare( + r#" +SELECT package_id, enabled, favorite, ignored_version, repository_id, package_resolution +FROM tracked_packages +ORDER BY package_id ASC +"#, + )?; + let rows = stmt.query_map([], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, i64>(1)?, + row.get::<_, i64>(2)?, + row.get::<_, Option>(3)?, + row.get::<_, Option>(4)?, + row.get::<_, String>(5)?, + )) + })?; + let mut packages = Vec::new(); + for row in rows { + let (package_id, enabled, favorite, ignored_version, repository_id, resolution) = row?; + packages.push(StoredTrackedPackage { + package_id: PackageId::from_str(&package_id)?, + enabled: enabled != 0, + favorite: favorite != 0, + ignored_version, + repository_id: repository_id.map(RepositoryId::new).transpose()?, + package_resolution: StoredPackageResolution::from_str(&resolution)?, + }); + } + Ok(packages) + } + + pub fn insert_migration_record( + &self, + id: &str, + source: &str, + report_json: &str, + ) -> Result<(), StorageError> { + self.conn.execute( + r#" +INSERT INTO migration_records(id, source, report_json) +VALUES (?1, ?2, ?3) +ON CONFLICT(id) DO UPDATE SET + source = excluded.source, + completed_at_unix = unixepoch(), + report_json = excluded.report_json +"#, + params![id, source, report_json], + )?; + Ok(()) + } + + pub fn migration_records(&self) -> Result, StorageError> { + let mut stmt = self.conn.prepare( + "SELECT id, source, report_json FROM migration_records ORDER BY completed_at_unix ASC, id ASC", + )?; + let rows = stmt.query_map([], |row| { + Ok(StoredMigrationRecord { + id: row.get(0)?, + source: row.get(1)?, + report_json: row.get(2)?, + }) + })?; + let mut records = Vec::new(); + for row in rows { + records.push(row?); + } + Ok(records) + } +} + +impl CacheDb { + pub fn open(path: impl AsRef) -> Result { + let conn = Connection::open(path)?; + let db = Self { conn }; + db.migrate()?; + Ok(db) + } + + pub fn open_in_memory() -> Result { + let conn = Connection::open_in_memory()?; + let db = Self { conn }; + db.migrate()?; + Ok(db) + } + + fn migrate(&self) -> Result<(), StorageError> { + self.conn.execute_batch( + r#" +PRAGMA foreign_keys = ON; + +CREATE TABLE IF NOT EXISTS schema_migrations ( + id TEXT PRIMARY KEY, + applied_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); + +CREATE TABLE IF NOT EXISTS evaluated_packages ( + cache_key TEXT PRIMARY KEY, + repository_id TEXT NOT NULL, + package_id TEXT NOT NULL, + package_file_hash TEXT NOT NULL, + schema_version TEXT NOT NULL, + evaluated_json TEXT NOT NULL, + evaluated_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); + +CREATE TABLE IF NOT EXISTS provider_responses ( + cache_key TEXT PRIMARY KEY, + provider TEXT NOT NULL, + response_json TEXT NOT NULL, + fetched_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); +"#, + )?; + self.conn.execute( + "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('cache-v1')", + [], + )?; + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StoredRepository { + pub id: RepositoryId, + pub name: String, + pub priority: RepositoryPriority, + pub api_version: String, + pub path: Option, + pub revision: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TrackedPackageUpsert { + pub package_id: PackageId, + pub enabled: bool, + pub favorite: bool, + pub ignored_version: Option, + pub repository_id: Option, + pub package_resolution: StoredPackageResolution, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StoredTrackedPackage { + pub package_id: PackageId, + pub enabled: bool, + pub favorite: bool, + pub ignored_version: Option, + pub repository_id: Option, + pub package_resolution: StoredPackageResolution, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StoredMigrationRecord { + pub id: String, + pub source: String, + pub report_json: String, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum StoredPackageResolution { + OfficialRepositoryPackage, + GenerateLocalPackage, + MissingPackageDefinition, +} + +impl StoredPackageResolution { + pub const fn as_str(self) -> &'static str { + match self { + Self::OfficialRepositoryPackage => "official_repository_package", + Self::GenerateLocalPackage => "generate_local_package", + Self::MissingPackageDefinition => "missing_package_definition", + } + } +} + +impl FromStr for StoredPackageResolution { + type Err = StorageError; + + fn from_str(value: &str) -> Result { + match value { + "official_repository_package" => Ok(Self::OfficialRepositoryPackage), + "generate_local_package" => Ok(Self::GenerateLocalPackage), + "missing_package_definition" => Ok(Self::MissingPackageDefinition), + other => Err(StorageError::PackageResolution(other.to_owned())), + } + } +} + +fn bool_to_i64(value: bool) -> i64 { + if value { + 1 + } else { + 0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::repository::REPO_API_VERSION_V1; + + #[test] + fn main_db_stores_repository_registry_ordered_by_priority() { + let db = MainDb::open_in_memory().unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::DEFAULT, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + None, + Some("rev1"), + ) + .unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("local").unwrap(), + name: "Local".to_owned(), + priority: RepositoryPriority::LOCAL, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + None, + None, + ) + .unwrap(); + + let repositories = db.repositories().unwrap(); + assert_eq!(repositories[0].id.as_str(), "local"); + assert_eq!(repositories[1].id.as_str(), "official"); + } + + #[test] + fn main_db_stores_tracked_package_user_state() { + let db = MainDb::open_in_memory().unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + enabled: true, + favorite: true, + ignored_version: Some("1.2.3".to_owned()), + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + + let packages = db.tracked_packages().unwrap(); + assert_eq!(packages.len(), 1); + assert_eq!( + packages[0].package_id.to_string(), + "android/org.fdroid.fdroid" + ); + assert!(packages[0].enabled); + assert!(packages[0].favorite); + assert_eq!(packages[0].ignored_version.as_deref(), Some("1.2.3")); + assert_eq!( + packages[0].package_resolution, + StoredPackageResolution::OfficialRepositoryPackage + ); + } + + #[test] + fn main_db_records_migration_completion() { + let db = MainDb::open_in_memory().unwrap(); + db.insert_migration_record("legacy-room-v17", "legacy-room-bundle", r#"{"ok":true}"#) + .unwrap(); + + let records = db.migration_records().unwrap(); + assert_eq!(records.len(), 1); + assert_eq!(records[0].id, "legacy-room-v17"); + assert_eq!(records[0].source, "legacy-room-bundle"); + } + + #[test] + fn cache_db_migrates_schema() { + let _db = CacheDb::open_in_memory().unwrap(); + } +} diff --git a/src/cache.rs b/src/cache.rs deleted file mode 100644 index e96302b..0000000 --- a/src/cache.rs +++ /dev/null @@ -1,34 +0,0 @@ -mod local; -pub mod manager; - -use once_cell::sync::Lazy; -use std::{path::Path, sync::Arc}; -use tokio::sync::Mutex; - -use crate::utils::instance::InstanceContainer; - -use self::manager::CacheManager; - -static INSTANCE_CONTAINER: Lazy> = - Lazy::new(|| InstanceContainer::new(CacheManager::new())); - -pub async fn init_cache_manager(local_cache_dir: &Path) { - get_cache_manager() - .await - .lock() - .await - .set_local_cache_dir(local_cache_dir); -} - -pub async fn init_cache_manager_with_expire(local_cache_path: &Path, expire_time: u64) { - get_cache_manager() - .await - .lock() - .await - .set_local_cache_dir(local_cache_path) - .set_global_expire_time(expire_time); -} - -pub async fn get_cache_manager() -> Arc> { - INSTANCE_CONTAINER.get().await.clone() -} diff --git a/src/cache/local.rs b/src/cache/local.rs deleted file mode 100644 index 22cdcb4..0000000 --- a/src/cache/local.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::io::Result; -use std::path::{Path, PathBuf}; -use std::time::SystemTime; -use tokio::fs::{metadata, read, remove_file, write}; - -use tokio::fs::create_dir_all; - -pub struct LocalCacheItem { - cache_path: PathBuf, -} - -impl LocalCacheItem { - pub fn new(cache_dir: &Path, key: &str) -> Self { - Self { - cache_path: cache_dir.join(key), - } - } - - pub async fn get(&self, decoder: fn(Vec) -> T) -> Result { - read(&self.cache_path).await.map(decoder) - } - - pub async fn save(&self, data: T, encoder: fn(T) -> Vec) -> Result<()> { - let parent = self.cache_path.parent().ok_or_else(|| { - std::io::Error::new(std::io::ErrorKind::NotFound, "cache path not found") - })?; - create_dir_all(parent).await?; - write(&self.cache_path, encoder(data)).await - } - - pub async fn remove(&self) -> Result<()> { - remove_file(&self.cache_path).await - } - - pub async fn get_cache_time(&self) -> Result { - let metadata = metadata(&self.cache_path).await?; - let modified_time = metadata.modified()?; - Ok(modified_time - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap_or_default() - .as_secs()) - } -} - -#[cfg(test)] -mod tests { - use std::fs; - - use super::*; - - #[tokio::test] - async fn test_local_cache_item() { - let key = "test_key"; - let data = "test_data"; - let cache_path = Path::new("./test_cache"); - let cache_item = LocalCacheItem::new(cache_path, key); - cache_item - .save(data, |data| data.as_bytes().to_vec()) - .await - .unwrap(); - let result = cache_item - .get(|data| String::from_utf8(data).unwrap()) - .await - .unwrap(); - assert_eq!(result, data); - let cache_time = cache_item.get_cache_time().await.unwrap(); - assert!( - cache_time - >= SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap_or_default() - .as_secs() - ); - cache_item.remove().await.unwrap(); - fs::remove_dir_all(cache_path).unwrap(); - } -} diff --git a/src/cache/manager.rs b/src/cache/manager.rs deleted file mode 100644 index fbb8bb3..0000000 --- a/src/cache/manager.rs +++ /dev/null @@ -1,265 +0,0 @@ -use std::path::{Path, PathBuf}; - -use bytes::Bytes; - -use super::local::LocalCacheItem; -use crate::utils::time::get_now_unix; - -#[derive(Debug, Eq, Hash, PartialEq)] -pub enum GroupType { - RepoInside, - Api, -} - -pub struct CacheManager { - local_cache_dir: Option, - global_expire_time: Option, -} - -impl CacheManager { - pub fn new() -> Self { - Self { - local_cache_dir: None, - global_expire_time: None, - } - } - - pub fn set_local_cache_dir(&mut self, local_cache_dir: &Path) -> &mut Self { - self.local_cache_dir = Some(local_cache_dir.to_path_buf()); - self - } - - pub fn set_global_expire_time(&mut self, global_expire_time: u64) -> &mut Self { - self.global_expire_time = Some(global_expire_time); - self - } - - fn get_local_cache_key(group: &GroupType, key: &str) -> String { - format!("{:?}_{}", group, key) - } - - async fn get_local( - &self, - group: &GroupType, - key: &str, - expire_time: Option, - ) -> Option { - let local_cache_item = self.get_local_cache_item(group, key).ok()?; - if let Ok(time) = local_cache_item.get_cache_time().await { - if let Some(expire_time) = expire_time.or(self.global_expire_time) { - if time + expire_time < get_now_unix() { - return None; - } - } - if let Ok(data) = local_cache_item.get(|data| data).await { - return Some(Bytes::from(data)); - } - } - None - } - - pub async fn get( - &self, - group: &GroupType, - key: &str, - expire_time: Option, - ) -> Option { - self.get_local(group, key, expire_time).await - } - - pub async fn save( - &mut self, - group: &GroupType, - key: &str, - value: Bytes, - ) -> Result<(), std::io::Error> { - let local_cache_item = self.get_local_cache_item(group, key)?; - local_cache_item.save(value, |data| data.into()).await - } - - #[allow(dead_code)] - pub async fn remove(&mut self, group: &GroupType, key: &str) -> Result<(), std::io::Error> { - let local_cache_item = self.get_local_cache_item(group, key)?; - local_cache_item.remove().await - } - - #[allow(dead_code)] - pub async fn clean(&mut self) -> Result<(), std::io::Error> { - self.clean_local().await - } - - #[allow(dead_code)] - async fn clean_local(&mut self) -> Result<(), std::io::Error> { - let local_cache_dir = self.local_cache_dir.as_ref().ok_or_else(|| { - std::io::Error::new(std::io::ErrorKind::NotFound, "local cache dir not found") - })?; - tokio::fs::remove_dir_all(local_cache_dir).await - } - - fn get_local_cache_item( - &self, - group: &GroupType, - key: &str, - ) -> Result { - let local_cache_key = Self::get_local_cache_key(group, key); - let local_cache_dir = self.local_cache_dir.as_ref().ok_or_else(|| { - std::io::Error::new(std::io::ErrorKind::NotFound, "local cache dir not found") - })?; - Ok(LocalCacheItem::new(local_cache_dir, &local_cache_key)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_cache_manager() { - let mut cache_manager = CacheManager::new(); - cache_manager.set_local_cache_dir(Path::new("./test_cache_manager")); - let group = GroupType::RepoInside; - let key = "test_key"; - let value = Bytes::from("test_value"); - let _ = cache_manager.remove(&group, key).await; - cache_manager - .save(&group, key, value.clone()) - .await - .expect("save failed"); - let data = cache_manager - .get(&group, key, None) - .await - .expect("get failed"); - assert_eq!(data, value); - cache_manager - .remove(&group, key) - .await - .expect("remove failed"); - cache_manager.clean().await.expect("clean failed"); - } - - #[tokio::test] - async fn test_cache_manager_restart() { - let mut cache_manager = CacheManager::new(); - cache_manager.set_local_cache_dir(Path::new("./test_cache_manager_restart")); - let group = GroupType::RepoInside; - let key = "test_key"; - let value = Bytes::from("test_value"); - let _ = cache_manager.remove(&group, key).await; - cache_manager - .save(&group, key, value.clone()) - .await - .expect("save failed"); - let mut _cache_manager = CacheManager::new(); - _cache_manager.set_local_cache_dir(Path::new("./test_cache_manager_restart")); - let data = _cache_manager - .get(&group, key, None) - .await - .expect("get failed"); - assert_eq!(data, value); - cache_manager - .remove(&group, key) - .await - .expect("remove failed"); - cache_manager.clean().await.expect("clean failed"); - } - - #[tokio::test] - async fn test_cache_manager_no_exist() { - let mut cache_manager = CacheManager::new(); - cache_manager.set_local_cache_dir(Path::new("./test_cache_manager_no_exist")); - let group = GroupType::RepoInside; - let key = "test_key_no_exist"; - let data = cache_manager.get(&group, key, None).await; - assert_eq!(data, None); - let clean_result = cache_manager.clean().await; - assert!(clean_result.is_err()); - } - - #[tokio::test] - async fn test_cache_manager_expire_non_global() { - let mut cache_manager = CacheManager::new(); - cache_manager.set_local_cache_dir(Path::new("./test_cache_manager_expire_non_global")); - let group = GroupType::RepoInside; - let key = "test_key_expire"; - let value = Bytes::from("test_value_expire"); - let _ = cache_manager.remove(&group, key).await; - cache_manager - .save(&group, key, value.clone()) - .await - .expect("save failed"); - // sleep 1 second - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; - let data = cache_manager.get(&group, key, Some(0)).await; - assert_eq!(data, None); - let data = cache_manager - .get(&group, key, Some(100)) - .await - .expect("get failed"); - assert_eq!(data, value); - cache_manager - .remove(&group, key) - .await - .expect("remove failed"); - cache_manager.clean().await.expect("clean failed"); - } - - #[tokio::test] - async fn test_cache_manager_expire_non_expire() { - let mut cache_manager = CacheManager::new(); - cache_manager.set_local_cache_dir(Path::new("./test_cache_manager_non_expire")); - let group = GroupType::RepoInside; - let key = "test_key_expire"; - let value = Bytes::from("test_value_expire"); - let _ = cache_manager.remove(&group, key).await; - cache_manager - .save(&group, key, value.clone()) - .await - .expect("save failed"); - // sleep 1 second - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; - let data = cache_manager.get(&group, key, Some(0)).await; - assert_eq!(data, None); - let data = cache_manager - .get(&group, key, None) - .await - .expect("get failed"); - assert_eq!(data, value); - cache_manager - .remove(&group, key) - .await - .expect("remove failed"); - cache_manager.clean().await.expect("clean failed"); - } - - #[tokio::test] - async fn test_cache_manager_global_expire() { - let mut cache_manager = CacheManager::new(); - cache_manager - .set_local_cache_dir(Path::new("./test_cache_manager_global_expire")) - .set_global_expire_time(1); - let group = GroupType::RepoInside; - let key = "test_key_expire"; - let value = Bytes::from("test_value_expire"); - let _ = cache_manager.remove(&group, key).await; - cache_manager - .save(&group, key, value.clone()) - .await - .expect("save failed"); - // sleep 1 second - tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; - let data = cache_manager.get(&group, key, Some(0)).await; - assert_eq!(data, None); - let data = cache_manager.get(&group, key, None).await; - assert_eq!(data, None); - let data = cache_manager - .get(&group, key, Some(100)) - .await - .expect("get failed"); - assert_eq!(data, value); - cache_manager - .remove(&group, key) - .await - .expect("remove failed"); - cache_manager.clean().await.expect("clean failed"); - } -} diff --git a/src/cache/moka.rs b/src/cache/moka.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/core.rs b/src/core.rs deleted file mode 100644 index ef68c36..0000000 --- a/src/core.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod config; diff --git a/src/core/config.rs b/src/core/config.rs deleted file mode 100644 index 33d48eb..0000000 --- a/src/core/config.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod data; -mod utils; -pub mod world; diff --git a/src/core/config/data.rs b/src/core/config/data.rs deleted file mode 100644 index 80675b9..0000000 --- a/src/core/config/data.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod rule_list; diff --git a/src/core/config/data/rule_list.rs b/src/core/config/data/rule_list.rs deleted file mode 100644 index b0dd2d9..0000000 --- a/src/core/config/data/rule_list.rs +++ /dev/null @@ -1,89 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// Configuration lists -/// -/// JSON Schema: -/// ```json -/// { -/// "app_list": ["", ], -/// "hub_list": ["", ] -/// } -/// ``` - -#[derive(Serialize, Deserialize, Debug)] -pub struct RuleList { - #[serde(rename = "app_list")] - pub app_list: Vec, - - #[serde(rename = "hub_list")] - pub hub_list: Vec, -} - -impl RuleList { - pub fn new() -> Self { - RuleList { - app_list: Vec::new(), - hub_list: Vec::new(), - } - } - - pub fn push_app(&mut self, app_name: &str) -> bool { - if self.app_list.contains(&app_name.to_string()) { - false - } else { - self.app_list.push(app_name.to_string()); - true - } - } - - pub fn remove_app(&mut self, app_name: &str) -> bool { - if let Some(index) = self.app_list.iter().position(|x| x == app_name) { - self.app_list.remove(index); - true - } else { - false - } - } - - pub fn push_hub(&mut self, hub_name: &str) -> bool { - if self.hub_list.contains(&hub_name.to_string()) { - false - } else { - self.hub_list.push(hub_name.to_string()); - true - } - } - - pub fn remove_hub(&mut self, hub_name: &str) -> bool { - if let Some(index) = self.hub_list.iter().position(|x| x == hub_name) { - self.hub_list.remove(index); - true - } else { - false - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json; - - #[test] - fn test_config_list() { - let json = r#" -{ - "app_list": ["UpgradeAll", ""], - "hub_list": ["GitHub"] -}"#; - - let config_list: RuleList = serde_json::from_str(json).unwrap(); - - // check app_config_list - assert_eq!(config_list.app_list.len(), 2); - assert_eq!(config_list.app_list[0], "UpgradeAll"); - // check hub_config_list - assert_eq!(config_list.hub_list.len(), 1); - assert_eq!(config_list.hub_list[0], "GitHub"); - } -} diff --git a/src/core/config/utils.rs b/src/core/config/utils.rs deleted file mode 100644 index 15a32dd..0000000 --- a/src/core/config/utils.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::{env, path::PathBuf}; - -use crate::locale::all_dir; - -pub fn get_data_path(sub: &str) -> String { - let data_dir = env::var("DATA_DIR").map(PathBuf::from).unwrap_or_else(|_| { - all_dir() - .expect("Non-support OS, you should set DATA_DIR env arg") - .data_dir - }); - data_dir - .join(sub) - .to_str() - .expect("Invalid config path") - .to_string() -} - -#[cfg(test)] -mod tests { - use super::*; - use std::env; - use std::fs; - - #[test] - fn test_get_data_path_env() { - let path = "/tmp/getter_test"; - env::set_var("DATA_DIR", path); - let path = PathBuf::from(get_data_path("test")); - let _ = fs::remove_file(&path); - let path = path.to_str().unwrap(); - assert_eq!(path, "/tmp/getter_test/test"); - } - - #[test] - fn test_get_data_path_default() { - let path = PathBuf::from(get_data_path("test")); - let _ = fs::remove_file(&path); - let path = path.to_str().unwrap(); - assert!(path.ends_with("test")) - } -} diff --git a/src/core/config/world.rs b/src/core/config/world.rs deleted file mode 100644 index 9ab73ac..0000000 --- a/src/core/config/world.rs +++ /dev/null @@ -1,35 +0,0 @@ -pub mod local_repo; -pub mod world_config_wrapper; -pub mod world_list; - -use once_cell::sync::Lazy; -use std::{path::Path, sync::Arc}; -use tokio::sync::Mutex; - -use crate::{error::Result, utils::instance::InstanceContainer}; - -use self::world_list::WorldList; - -static INSTANCE_CONTAINER: Lazy> = - Lazy::new(|| InstanceContainer::new(WorldList::new())); - -pub async fn init_world_list(world_list_path: &Path) -> Result<()> { - get_world_list().await.lock().await.load(world_list_path)?; - Ok(()) -} - -pub async fn get_world_list() -> Arc> { - INSTANCE_CONTAINER.get().await.clone() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_get_world_list_micro() { - let world_list_path = Path::new("./test_get_world_list_micro"); - init_world_list(world_list_path).await.unwrap(); - let _ = get_world_list().await; - } -} diff --git a/src/core/config/world/local_repo.rs b/src/core/config/world/local_repo.rs deleted file mode 100644 index b277da2..0000000 --- a/src/core/config/world/local_repo.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::error::{GetterError, Result}; -use std::fs; -use std::path::PathBuf; - -const LOCAL_REPO_DIR: &str = "local_repo"; - -pub struct LocalRepo { - path: PathBuf, -} - -impl LocalRepo { - pub fn new(root_dir_path: &str) -> Self { - let path = PathBuf::from(root_dir_path).join(LOCAL_REPO_DIR); - Self { path } - } - - pub fn load(&self, rule_path: &str) -> Result { - let path = PathBuf::from(&self.path).join(rule_path); - let content = fs::read_to_string(&path) - .map_err(|e| GetterError::new("LocalRepo", "load", Box::new(e)))?; - Ok(content) - } - - pub fn save(&self, rule_path: &str, content: &str) -> Result<()> { - let path = PathBuf::from(&self.path).join(rule_path); - let _ = fs::create_dir_all(path.parent().unwrap()); - fs::write(&path, content) - .map_err(|e| GetterError::new("LocalRepo", "save", Box::new(e)))?; - Ok(()) - } -} diff --git a/src/core/config/world/world_config_wrapper.rs b/src/core/config/world/world_config_wrapper.rs deleted file mode 100644 index a4422f3..0000000 --- a/src/core/config/world/world_config_wrapper.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::path::Path; - -use crate::error::{GetterError, Result}; -use crate::utils::json::{json_to_string, string_to_json}; -use crate::websdk::cloud_rules::cloud_rules_wrapper::CloudRules; -use crate::websdk::cloud_rules::data::app_item::AppItem; -use crate::websdk::cloud_rules::data::hub_item::HubItem; - -use super::local_repo::LocalRepo; -use super::world_list::WorldList; - -pub struct WorldConfigWrapper { - pub world_list: WorldList, - pub local_repo: LocalRepo, -} - -impl WorldConfigWrapper { - pub fn new(world_config_path: &Path, local_repo_path: &str) -> Result { - let mut world_list = WorldList::new(); - world_list.load(world_config_path)?; - let local_repo = LocalRepo::new(local_repo_path); - Ok(WorldConfigWrapper { - world_list, - local_repo, - }) - } - - pub fn get_app_rule(&self, app_name: &str) -> Option { - if let Ok(content) = self.local_repo.load(app_name) { - if let Ok(app_item) = string_to_json(&content) { - return Some(app_item); - } - } - None - } - - pub fn get_hub_rule(&self, hub_name: &str) -> Option { - if let Ok(content) = self.local_repo.load(hub_name) { - if let Ok(hub_item) = string_to_json(&content) { - return Some(hub_item); - } - } - None - } - - pub fn download_app_rule(&self, app_name: &str, cloud_rules: &mut CloudRules) -> Result<()> { - let app_item = cloud_rules.get_cloud_app_rules(|x| x.info.name == app_name); - let content = json_to_string(&app_item).map_err(|e| { - GetterError::new("WorldConfigWrapper", "download_app_rule", Box::new(e)) - })?; - self.local_repo.save(app_name, &content)?; - Ok(()) - } - - pub fn download_hub_rule(&self, hub_name: &str, cloud_rules: &mut CloudRules) -> Result<()> { - let hub_item = cloud_rules.get_cloud_hub_rules(|x| x.info.hub_name == hub_name); - let content = json_to_string(&hub_item).map_err(|e| { - GetterError::new("WorldConfigWrapper", "download_hub_rule", Box::new(e)) - })?; - self.local_repo.save(hub_name, &content)?; - Ok(()) - } - - pub fn get_app_rule_list(&self) -> &Vec { - &self.world_list.rule_list.app_list - } - - pub fn get_hub_rule_list(&self) -> &Vec { - &self.world_list.rule_list.hub_list - } -} diff --git a/src/core/config/world/world_list.rs b/src/core/config/world/world_list.rs deleted file mode 100644 index 6dd89a0..0000000 --- a/src/core/config/world/world_list.rs +++ /dev/null @@ -1,100 +0,0 @@ -use std::fs::{create_dir_all, File}; -use std::io::BufReader; -use std::path::{Path, PathBuf}; - -use crate::error::{GetterError, Result}; - -use super::super::data::rule_list::RuleList; - -pub const WORLD_CONFIG_LIST_NAME: &str = "world_config_list.json"; - -pub struct WorldList { - config_path: Option, - pub rule_list: RuleList, -} - -impl WorldList { - pub fn new() -> Self { - Self { - config_path: None, - rule_list: RuleList::new(), - } - } - - pub fn load(&mut self, config_path: &Path) -> Result<&mut Self> { - let rule_list = if let Ok(file) = File::open(config_path) { - let reader = BufReader::new(file); - serde_json::from_reader(reader) - .map_err(|e| GetterError::new("WorldList", "load", Box::new(e)))? - } else { - RuleList::new() - }; - self.config_path = Some(config_path.to_path_buf()); - self.rule_list = rule_list; - Ok(self) - } - - pub fn save(&self) -> Result<()> { - let path = self - .config_path - .as_deref() - .ok_or_else(|| GetterError::new_nobase("WorldList", "save: path not set"))?; - let parent = path - .parent() - .ok_or_else(|| GetterError::new_nobase("WorldList", "save: get parent dir failed"))?; - let _ = create_dir_all(parent); - let file = - File::create(path).map_err(|e| GetterError::new("WorldList", "save", Box::new(e)))?; - serde_json::to_writer(file, &self.rule_list) - .map_err(|e| GetterError::new("WorldList", "save", Box::new(e)))?; - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::fs; - - #[test] - fn test_world_list() { - let path_base = "/tmp/getter_test_world_list"; - let _ = fs::remove_dir_all(path_base); - let config_path = PathBuf::from(path_base).join(WORLD_CONFIG_LIST_NAME); - - let mut world_list = WorldList::new(); - world_list.load(&config_path).unwrap(); - assert!(world_list.rule_list.app_list.is_empty()); - assert!(world_list.rule_list.hub_list.is_empty()); - let value = world_list.save(); - assert!(value.is_ok()); - let content = fs::read_to_string(&config_path).expect("test_world_list: read file failed"); - assert!(!content.is_empty()); - - let mut world_list = WorldList::new(); - world_list.load(&config_path).unwrap(); - world_list.rule_list.app_list.push("UpgradeAll".to_string()); - world_list.rule_list.hub_list.push("GitHub".to_string()); - let value = world_list.save(); - assert!(value.is_ok()); - - let mut world_list = WorldList::new(); - world_list.load(&config_path).unwrap(); - assert_eq!(world_list.rule_list.app_list.len(), 1); - assert_eq!(world_list.rule_list.app_list[0], "UpgradeAll"); - assert_eq!(world_list.rule_list.hub_list.len(), 1); - assert_eq!(world_list.rule_list.hub_list[0], "GitHub"); - - fs::remove_dir_all(path_base).expect("test_world_list: clean failed"); - } - - #[test] - fn test_world_list_only_load() { - let path_base = "/tmp/getter_test_world_list_only_load"; - let _ = fs::remove_dir(path_base); - let path = PathBuf::from(path_base).join(WORLD_CONFIG_LIST_NAME); - - let _ = WorldList::new().load(&path); - assert!(path.try_exists().is_ok_and(|x| !x)); - } -} diff --git a/src/database/mod.rs b/src/database/mod.rs deleted file mode 100644 index dcb6598..0000000 --- a/src/database/mod.rs +++ /dev/null @@ -1,229 +0,0 @@ -pub mod models; -pub mod store; - -use models::{ - app::AppRecord, extra_app::ExtraAppRecord, extra_hub::ExtraHubRecord, hub::HubRecord, -}; -use once_cell::sync::OnceCell; -use std::path::Path; - -use crate::error::Result; -use store::{HasId, JsonlStore}; - -impl HasId for AppRecord { - fn id(&self) -> &str { - &self.id - } -} - -impl HasId for HubRecord { - fn id(&self) -> &str { - &self.uuid - } -} - -impl HasId for ExtraAppRecord { - fn id(&self) -> &str { - &self.id - } -} - -impl HasId for ExtraHubRecord { - fn id(&self) -> &str { - &self.id - } -} - -pub struct Database { - pub apps: JsonlStore, - pub hubs: JsonlStore, - pub extra_apps: JsonlStore, - pub extra_hubs: JsonlStore, -} - -impl Database { - pub fn open(data_dir: &Path) -> Result { - let db = Self { - apps: JsonlStore::new(data_dir.join("apps.jsonl")), - hubs: JsonlStore::new(data_dir.join("hubs.jsonl")), - extra_apps: JsonlStore::new(data_dir.join("extra_apps.jsonl")), - extra_hubs: JsonlStore::new(data_dir.join("extra_hubs.jsonl")), - }; - db.apps.ensure_file()?; - db.hubs.ensure_file()?; - db.extra_apps.ensure_file()?; - db.extra_hubs.ensure_file()?; - Ok(db) - } - - // --- App CRUD --- - - pub fn load_apps(&self) -> Result> { - self.apps.load_all() - } - - pub fn upsert_app(&self, record: &AppRecord) -> Result<()> { - self.apps.upsert(record) - } - - pub fn delete_app(&self, id: &str) -> Result { - self.apps.delete::(id) - } - - pub fn find_app(&self, id: &str) -> Result> { - self.apps.find_by_id(id) - } - - // --- Hub CRUD --- - - pub fn load_hubs(&self) -> Result> { - self.hubs.load_all() - } - - pub fn upsert_hub(&self, record: &HubRecord) -> Result<()> { - self.hubs.upsert(record) - } - - pub fn delete_hub(&self, uuid: &str) -> Result { - self.hubs.delete::(uuid) - } - - pub fn find_hub(&self, uuid: &str) -> Result> { - self.hubs.find_by_id(uuid) - } - - // --- ExtraApp CRUD --- - - pub fn load_extra_apps(&self) -> Result> { - self.extra_apps.load_all() - } - - pub fn upsert_extra_app(&self, record: &ExtraAppRecord) -> Result<()> { - self.extra_apps.upsert(record) - } - - pub fn delete_extra_app(&self, id: &str) -> Result { - self.extra_apps.delete::(id) - } - - /// Find an ExtraApp record by matching its `app_id` map. - pub fn get_extra_app_by_app_id( - &self, - app_id: &std::collections::HashMap>, - ) -> Result> { - let all = self.extra_apps.load_all::()?; - Ok(all.into_iter().find(|r| &r.app_id == app_id)) - } - - // --- ExtraHub CRUD --- - - pub fn load_extra_hubs(&self) -> Result> { - self.extra_hubs.load_all() - } - - pub fn upsert_extra_hub(&self, record: &ExtraHubRecord) -> Result<()> { - self.extra_hubs.upsert(record) - } - - pub fn delete_extra_hub(&self, id: &str) -> Result { - self.extra_hubs.delete::(id) - } - - pub fn find_extra_hub(&self, id: &str) -> Result> { - self.extra_hubs.find_by_id(id) - } -} - -static DB: OnceCell = OnceCell::new(); - -/// Initialize the global database. Must be called once before `get_db()`. -/// Idempotent: if already initialized, returns `Ok(())`. -pub fn init_db(data_dir: &Path) -> Result<()> { - if DB.get().is_some() { - return Ok(()); - } - let db = Database::open(data_dir)?; - let _ = DB.set(db); // Ignore error if another thread beat us to it - Ok(()) -} - -/// Get the global database instance. Panics if `init_db` was not called. -pub fn get_db() -> &'static Database { - DB.get() - .expect("Database not initialized. Call init_db() first.") -} - -#[cfg(test)] -mod tests { - use super::*; - use tempfile::TempDir; - - fn open_test_db() -> (Database, TempDir) { - let dir = tempfile::tempdir().unwrap(); - let db = Database::open(dir.path()).unwrap(); - (db, dir) - } - - #[test] - fn test_open_creates_files() { - let dir = tempfile::tempdir().unwrap(); - Database::open(dir.path()).unwrap(); - assert!(dir.path().join("apps.jsonl").exists()); - assert!(dir.path().join("hubs.jsonl").exists()); - assert!(dir.path().join("extra_apps.jsonl").exists()); - assert!(dir.path().join("extra_hubs.jsonl").exists()); - } - - #[test] - fn test_app_crud() { - let (db, _dir) = open_test_db(); - let app = AppRecord::new( - "TestApp".to_string(), - std::collections::HashMap::from([("owner".to_string(), Some("alice".to_string()))]), - ); - db.upsert_app(&app).unwrap(); - - let apps = db.load_apps().unwrap(); - assert_eq!(apps.len(), 1); - assert_eq!(apps[0].name, "TestApp"); - - let found = db.find_app(&app.id).unwrap(); - assert!(found.is_some()); - - let deleted = db.delete_app(&app.id).unwrap(); - assert!(deleted); - assert!(db.load_apps().unwrap().is_empty()); - } - - #[test] - fn test_hub_crud() { - use crate::websdk::cloud_rules::data::hub_item::{HubItem, Info}; - let (db, _dir) = open_test_db(); - let hub = HubRecord::new( - "fd9b2602-62c5-4d55-bd1e-0d6537714ca0".to_string(), - HubItem { - base_version: 6, - config_version: 1, - uuid: "fd9b2602-62c5-4d55-bd1e-0d6537714ca0".to_string(), - info: Info { - hub_name: "GitHub".to_string(), - hub_icon_url: None, - }, - api_keywords: vec!["owner".to_string(), "repo".to_string()], - auth_keywords: vec![], - app_url_templates: vec![], - target_check_api: None, - }, - ); - db.upsert_hub(&hub).unwrap(); - let hubs = db.load_hubs().unwrap(); - assert_eq!(hubs.len(), 1); - assert_eq!(hubs[0].uuid, "fd9b2602-62c5-4d55-bd1e-0d6537714ca0"); - - let deleted = db - .delete_hub("fd9b2602-62c5-4d55-bd1e-0d6537714ca0") - .unwrap(); - assert!(deleted); - assert!(db.load_hubs().unwrap().is_empty()); - } -} diff --git a/src/database/models/app.rs b/src/database/models/app.rs deleted file mode 100644 index 104e0ce..0000000 --- a/src/database/models/app.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::websdk::cloud_rules::data::app_item::AppItem; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct AppRecord { - /// UUID v4 identifier (replaces Room's auto-increment Long id) - pub id: String, - pub name: String, - pub app_id: HashMap>, - #[serde(skip_serializing_if = "Option::is_none")] - pub invalid_version_number_field_regex: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub include_version_number_field_regex: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub ignore_version_number: Option, - /// Cloud config (AppItem), optional - #[serde(skip_serializing_if = "Option::is_none")] - pub cloud_config: Option, - /// Space-separated hub UUIDs in priority order - #[serde(skip_serializing_if = "Option::is_none")] - pub enable_hub_list: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub star: Option, -} - -impl AppRecord { - pub fn new(name: String, app_id: HashMap>) -> Self { - Self { - id: uuid::Uuid::new_v4().to_string(), - name, - app_id, - invalid_version_number_field_regex: None, - include_version_number_field_regex: None, - ignore_version_number: None, - cloud_config: None, - enable_hub_list: None, - star: None, - } - } - - pub fn get_sorted_hub_uuids(&self) -> Vec { - match &self.enable_hub_list { - Some(s) if !s.is_empty() => s.split(' ').map(String::from).collect(), - _ => vec![], - } - } - - pub fn set_sorted_hub_uuids(&mut self, uuids: &[String]) { - let s = uuids.join(" "); - self.enable_hub_list = if s.is_empty() { None } else { Some(s) }; - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn sample_app() -> AppRecord { - AppRecord { - id: "test-uuid".to_string(), - name: "TestApp".to_string(), - app_id: HashMap::from([("owner".to_string(), Some("alice".to_string()))]), - invalid_version_number_field_regex: None, - include_version_number_field_regex: None, - ignore_version_number: None, - cloud_config: None, - enable_hub_list: Some("hub1 hub2".to_string()), - star: Some(true), - } - } - - #[test] - fn test_serialization_roundtrip() { - let app = sample_app(); - let json = serde_json::to_string(&app).unwrap(); - let decoded: AppRecord = serde_json::from_str(&json).unwrap(); - assert_eq!(app, decoded); - } - - #[test] - fn test_get_sorted_hub_uuids() { - let app = sample_app(); - let uuids = app.get_sorted_hub_uuids(); - assert_eq!(uuids, vec!["hub1", "hub2"]); - } - - #[test] - fn test_set_sorted_hub_uuids() { - let mut app = sample_app(); - app.set_sorted_hub_uuids(&["a".to_string(), "b".to_string(), "c".to_string()]); - assert_eq!(app.enable_hub_list, Some("a b c".to_string())); - } - - #[test] - fn test_empty_hub_list_is_none() { - let mut app = sample_app(); - app.set_sorted_hub_uuids(&[]); - assert_eq!(app.enable_hub_list, None); - } -} diff --git a/src/database/models/extra_app.rs b/src/database/models/extra_app.rs deleted file mode 100644 index 0123376..0000000 --- a/src/database/models/extra_app.rs +++ /dev/null @@ -1,48 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ExtraAppRecord { - /// UUID v4 identifier - pub id: String, - pub app_id: HashMap>, - #[serde(skip_serializing_if = "Option::is_none")] - pub mark_version_number: Option, -} - -impl ExtraAppRecord { - pub fn new(app_id: HashMap>) -> Self { - Self { - id: uuid::Uuid::new_v4().to_string(), - app_id, - mark_version_number: None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_serialization_roundtrip() { - let record = ExtraAppRecord { - id: "test-uuid".to_string(), - app_id: HashMap::from([( - "android_app_package".to_string(), - Some("com.foo".to_string()), - )]), - mark_version_number: Some("1.2.3".to_string()), - }; - let json = serde_json::to_string(&record).unwrap(); - let decoded: ExtraAppRecord = serde_json::from_str(&json).unwrap(); - assert_eq!(record, decoded); - } - - #[test] - fn test_none_fields_skipped() { - let record = ExtraAppRecord::new(HashMap::new()); - let json = serde_json::to_string(&record).unwrap(); - assert!(!json.contains("mark_version_number")); - } -} diff --git a/src/database/models/extra_hub.rs b/src/database/models/extra_hub.rs deleted file mode 100644 index 4366d62..0000000 --- a/src/database/models/extra_hub.rs +++ /dev/null @@ -1,56 +0,0 @@ -use serde::{Deserialize, Serialize}; - -pub const GLOBAL_HUB_ID: &str = "GLOBAL"; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ExtraHubRecord { - /// Hub UUID or GLOBAL_HUB_ID - pub id: String, - #[serde(default)] - pub enable_global: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub url_replace_search: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub url_replace_string: Option, -} - -impl ExtraHubRecord { - pub fn new(id: String) -> Self { - Self { - id, - enable_global: false, - url_replace_search: None, - url_replace_string: None, - } - } - - pub fn global() -> Self { - Self::new(GLOBAL_HUB_ID.to_string()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_serialization_roundtrip() { - let record = ExtraHubRecord { - id: "some-hub-uuid".to_string(), - enable_global: true, - url_replace_search: Some("github.com".to_string()), - url_replace_string: Some("mirror.example.com".to_string()), - }; - let json = serde_json::to_string(&record).unwrap(); - let decoded: ExtraHubRecord = serde_json::from_str(&json).unwrap(); - assert_eq!(record, decoded); - } - - #[test] - fn test_none_fields_skipped() { - let record = ExtraHubRecord::global(); - let json = serde_json::to_string(&record).unwrap(); - assert!(!json.contains("url_replace_search")); - assert!(!json.contains("url_replace_string")); - } -} diff --git a/src/database/models/hub.rs b/src/database/models/hub.rs deleted file mode 100644 index 84e94ce..0000000 --- a/src/database/models/hub.rs +++ /dev/null @@ -1,80 +0,0 @@ -use crate::websdk::cloud_rules::data::hub_item::HubItem; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct HubRecord { - /// Hub UUID — primary key - pub uuid: String, - pub hub_config: HubItem, - pub auth: HashMap, - #[serde(default)] - pub ignore_app_id_list: Vec>>, - /// 0 = disabled, 1 = enabled - #[serde(default)] - pub applications_mode: i32, - #[serde(default)] - pub user_ignore_app_id_list: Vec>>, - /// Lower = higher priority. Default is -(hub list size). - #[serde(default)] - pub sort_point: i32, -} - -impl HubRecord { - pub fn new(uuid: String, hub_config: HubItem) -> Self { - Self { - uuid, - hub_config, - auth: HashMap::new(), - ignore_app_id_list: vec![], - applications_mode: 0, - user_ignore_app_id_list: vec![], - sort_point: 0, - } - } - - pub fn applications_mode_enabled(&self) -> bool { - self.applications_mode == 1 - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::websdk::cloud_rules::data::hub_item::Info; - - fn sample_hub() -> HubRecord { - HubRecord::new( - "fd9b2602-62c5-4d55-bd1e-0d6537714ca0".to_string(), - HubItem { - base_version: 6, - config_version: 3, - uuid: "fd9b2602-62c5-4d55-bd1e-0d6537714ca0".to_string(), - info: Info { - hub_name: "GitHub".to_string(), - hub_icon_url: None, - }, - api_keywords: vec!["owner".to_string(), "repo".to_string()], - auth_keywords: vec![], - app_url_templates: vec!["https://github.com/%owner/%repo/".to_string()], - target_check_api: None, - }, - ) - } - - #[test] - fn test_serialization_roundtrip() { - let hub = sample_hub(); - let json = serde_json::to_string(&hub).unwrap(); - let decoded: HubRecord = serde_json::from_str(&json).unwrap(); - assert_eq!(hub, decoded); - } - - #[test] - fn test_applications_mode() { - let mut hub = sample_hub(); - assert!(!hub.applications_mode_enabled()); - hub.applications_mode = 1; - assert!(hub.applications_mode_enabled()); - } -} diff --git a/src/database/models/mod.rs b/src/database/models/mod.rs deleted file mode 100644 index c4e40bc..0000000 --- a/src/database/models/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod app; -pub mod extra_app; -pub mod extra_hub; -pub mod hub; diff --git a/src/database/store.rs b/src/database/store.rs deleted file mode 100644 index 32d374f..0000000 --- a/src/database/store.rs +++ /dev/null @@ -1,266 +0,0 @@ -use file_locker::FileLock; -use jsonl::ReadError; -use serde::{de::DeserializeOwned, Serialize}; -use std::io::{BufReader, Seek, SeekFrom, Write}; -use std::path::{Path, PathBuf}; - -use crate::error::{Error, Result}; - -pub trait HasId { - fn id(&self) -> &str; -} - -/// A simple JSONL-backed persistent store for a single record type. -/// -/// Each line in the file is a JSON-serialized record. All mutating operations -/// use `file-locker` advisory locking to ensure safe concurrent access. -pub struct JsonlStore { - path: PathBuf, -} - -impl JsonlStore { - pub fn new(path: impl Into) -> Self { - Self { path: path.into() } - } - - pub fn path(&self) -> &Path { - &self.path - } - - /// Ensure the backing file exists (creates parent dirs as needed). - pub fn ensure_file(&self) -> Result<()> { - if !self.path.exists() { - if let Some(parent) = self.path.parent() { - std::fs::create_dir_all(parent)?; - } - std::fs::File::create(&self.path)?; - } - Ok(()) - } - - /// Read all records from the file line by line. - pub fn load_all(&self) -> Result> { - if !self.path.exists() { - return Ok(vec![]); - } - let file = std::fs::File::open(&self.path)?; - let reader = BufReader::new(file); - read_all_from_reader(reader) - } - - fn acquire_write_lock(&self) -> Result { - self.ensure_file()?; - FileLock::new(&self.path) - .blocking(true) - .writeable(true) - .lock() - .map_err(Error::Io) - } - - /// Overwrite the entire file with the given records (must already hold lock). - fn write_all_locked(lock: &mut FileLock, records: &[T]) -> Result<()> { - lock.file.set_len(0)?; - lock.file.seek(SeekFrom::Start(0))?; - for record in records { - jsonl::write(&mut lock.file, record) - .map_err(|e| Error::Other(format!("jsonl write: {e}")))?; - } - lock.file.flush()?; - Ok(()) - } - - /// Insert or replace a record (matched by id). - pub fn upsert(&self, record: &T) -> Result<()> { - let mut lock = self.acquire_write_lock()?; - let mut records: Vec = { - lock.file.seek(SeekFrom::Start(0))?; - read_all_from_reader(BufReader::new(&lock.file))? - }; - - let id = record.id(); - let serialized: T = serde_json::from_str( - &serde_json::to_string(record).map_err(|e| Error::Other(format!("serialize: {e}")))?, - ) - .map_err(|e| Error::Other(format!("deserialize: {e}")))?; - - if let Some(pos) = records.iter().position(|r| r.id() == id) { - records[pos] = serialized; - } else { - records.push(serialized); - } - - Self::write_all_locked(&mut lock, &records) - } - - /// Delete the record with the given id. Returns true if a record was removed. - pub fn delete(&self, id: &str) -> Result { - let mut lock = self.acquire_write_lock()?; - let records: Vec = { - lock.file.seek(SeekFrom::Start(0))?; - read_all_from_reader(BufReader::new(&lock.file))? - }; - - let original_len = records.len(); - let filtered: Vec = records.into_iter().filter(|r| r.id() != id).collect(); - let deleted = filtered.len() < original_len; - Self::write_all_locked(&mut lock, &filtered)?; - Ok(deleted) - } - - /// Find a record by id (read-only, no lock). - pub fn find_by_id(&self, id: &str) -> Result> { - Ok(self.load_all::()?.into_iter().find(|r| r.id() == id)) - } -} - -fn read_all_from_reader(mut reader: R) -> Result> { - let mut records = Vec::new(); - loop { - match jsonl::read::<_, T>(&mut reader) { - Ok(record) => records.push(record), - Err(ReadError::Eof) => break, - Err(ReadError::Deserialize(e)) => { - eprintln!("JsonlStore: skipping malformed line: {e}"); - } - Err(ReadError::Io(e)) => return Err(Error::Io(e)), - } - } - Ok(records) -} - -#[cfg(test)] -mod tests { - use super::*; - use serde::{Deserialize, Serialize}; - use tempfile::NamedTempFile; - - #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] - struct TestRecord { - id: String, - value: String, - } - - impl HasId for TestRecord { - fn id(&self) -> &str { - &self.id - } - } - - fn make_store() -> (JsonlStore, NamedTempFile) { - let tmp = NamedTempFile::new().unwrap(); - let store = JsonlStore::new(tmp.path()); - (store, tmp) - } - - #[test] - fn test_empty_load() { - let (store, _tmp) = make_store(); - let records: Vec = store.load_all().unwrap(); - assert!(records.is_empty()); - } - - #[test] - fn test_upsert_and_load() { - let (store, _tmp) = make_store(); - let r = TestRecord { - id: "1".to_string(), - value: "hello".to_string(), - }; - store.upsert(&r).unwrap(); - let records: Vec = store.load_all().unwrap(); - assert_eq!(records.len(), 1); - assert_eq!(records[0], r); - } - - #[test] - fn test_upsert_updates_existing() { - let (store, _tmp) = make_store(); - store - .upsert(&TestRecord { - id: "1".to_string(), - value: "old".to_string(), - }) - .unwrap(); - store - .upsert(&TestRecord { - id: "1".to_string(), - value: "new".to_string(), - }) - .unwrap(); - let records: Vec = store.load_all().unwrap(); - assert_eq!(records.len(), 1); - assert_eq!(records[0].value, "new"); - } - - #[test] - fn test_multiple_records() { - let (store, _tmp) = make_store(); - for i in 0..5 { - store - .upsert(&TestRecord { - id: i.to_string(), - value: format!("v{i}"), - }) - .unwrap(); - } - let records: Vec = store.load_all().unwrap(); - assert_eq!(records.len(), 5); - } - - #[test] - fn test_delete() { - let (store, _tmp) = make_store(); - store - .upsert(&TestRecord { - id: "1".to_string(), - value: "a".to_string(), - }) - .unwrap(); - store - .upsert(&TestRecord { - id: "2".to_string(), - value: "b".to_string(), - }) - .unwrap(); - let deleted = store.delete::("1").unwrap(); - assert!(deleted); - let records: Vec = store.load_all().unwrap(); - assert_eq!(records.len(), 1); - assert_eq!(records[0].id, "2"); - } - - #[test] - fn test_delete_nonexistent() { - let (store, _tmp) = make_store(); - let deleted = store.delete::("nope").unwrap(); - assert!(!deleted); - } - - #[test] - fn test_find_by_id() { - let (store, _tmp) = make_store(); - store - .upsert(&TestRecord { - id: "42".to_string(), - value: "answer".to_string(), - }) - .unwrap(); - let found: Option = store.find_by_id("42").unwrap(); - assert!(found.is_some()); - assert_eq!(found.unwrap().value, "answer"); - } - - #[test] - fn test_find_by_id_missing() { - let (store, _tmp) = make_store(); - let found: Option = store.find_by_id("999").unwrap(); - assert!(found.is_none()); - } - - #[test] - fn test_file_not_exist_returns_empty() { - let store = JsonlStore::new("/tmp/this_file_does_not_exist_upgradeall.jsonl"); - let records: Vec = store.load_all().unwrap(); - assert!(records.is_empty()); - } -} diff --git a/src/downloader/config.rs b/src/downloader/config.rs deleted file mode 100644 index a4b0e9d..0000000 --- a/src/downloader/config.rs +++ /dev/null @@ -1,199 +0,0 @@ -//! Configuration system for the downloader module - -use serde::{Deserialize, Serialize}; - -/// Downloader backend selection -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] -#[serde(rename_all = "lowercase")] -pub enum DownloaderBackend { - /// Use trauma downloader (default) - #[default] - Trauma, - // Future backends can be added here: - // /// Use reqwest downloader - // Reqwest, - // /// Use custom CLI command - // Custom, -} - -/// Download configuration -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DownloadConfig { - /// Downloader backend to use - pub backend: DownloaderBackend, - - /// Maximum number of concurrent downloads - #[serde(default = "default_max_concurrent")] - pub max_concurrent: usize, - - /// Number of retry attempts for failed downloads - #[serde(default = "default_retries")] - pub retries: usize, - - /// Timeout for each download in seconds - #[serde(default = "default_timeout")] - pub timeout_seconds: u64, - - /// Custom command template for CLI downloader (future use) - /// Example: "wget -O \"${FILE}\" \"${URI}\"" - #[serde(skip_serializing_if = "Option::is_none")] - pub custom_command: Option, -} - -fn default_max_concurrent() -> usize { - 4 -} - -fn default_retries() -> usize { - 3 -} - -fn default_timeout() -> u64 { - 300 // 5 minutes -} - -impl Default for DownloadConfig { - fn default() -> Self { - Self { - backend: DownloaderBackend::default(), - max_concurrent: default_max_concurrent(), - retries: default_retries(), - timeout_seconds: default_timeout(), - custom_command: None, - } - } -} - -impl DownloadConfig { - /// Create a new configuration with default values - pub fn new() -> Self { - Self::default() - } - - /// Set the backend - pub fn with_backend(mut self, backend: DownloaderBackend) -> Self { - self.backend = backend; - self - } - - /// Set max concurrent downloads - pub fn with_max_concurrent(mut self, max: usize) -> Self { - self.max_concurrent = max; - self - } - - /// Set retry count - pub fn with_retries(mut self, retries: usize) -> Self { - self.retries = retries; - self - } - - /// Set timeout in seconds - pub fn with_timeout(mut self, seconds: u64) -> Self { - self.timeout_seconds = seconds; - self - } - - /// Set custom command template - pub fn with_custom_command(mut self, command: impl Into) -> Self { - self.custom_command = Some(command.into()); - self - } - - /// Load configuration from environment variables - /// - /// Supported environment variables: - /// - DOWNLOADER_BACKEND: "trauma" (default) - /// - DOWNLOADER_MAX_CONCURRENT: number (default: 4) - /// - DOWNLOADER_RETRIES: number (default: 3) - /// - DOWNLOADER_TIMEOUT: seconds (default: 300) - /// - FETCHCOMMAND: custom download command (like Portage's FETCHCOMMAND) - pub fn from_env() -> Self { - let mut config = Self::default(); - - if let Ok(backend) = std::env::var("DOWNLOADER_BACKEND") { - config.backend = match backend.to_lowercase().as_str() { - "trauma" => DownloaderBackend::Trauma, - _ => { - eprintln!("Unknown DOWNLOADER_BACKEND: {}, using default", backend); - DownloaderBackend::Trauma - } - }; - } - - if let Ok(max_concurrent) = std::env::var("DOWNLOADER_MAX_CONCURRENT") { - if let Ok(max) = max_concurrent.parse() { - config.max_concurrent = max; - } - } - - if let Ok(retries) = std::env::var("DOWNLOADER_RETRIES") { - if let Ok(retry_count) = retries.parse() { - config.retries = retry_count; - } - } - - if let Ok(timeout) = std::env::var("DOWNLOADER_TIMEOUT") { - if let Ok(timeout_secs) = timeout.parse() { - config.timeout_seconds = timeout_secs; - } - } - - if let Ok(command) = std::env::var("FETCHCOMMAND") { - config.custom_command = Some(command); - } - - config - } - - /// Load configuration from JSON string - pub fn from_json(json: &str) -> Result { - serde_json::from_str(json) - } - - /// Serialize configuration to JSON string - pub fn to_json(&self) -> Result { - serde_json::to_string_pretty(self) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_default_config() { - let config = DownloadConfig::default(); - assert_eq!(config.backend, DownloaderBackend::Trauma); - assert_eq!(config.max_concurrent, 4); - assert_eq!(config.retries, 3); - assert_eq!(config.timeout_seconds, 300); - assert!(config.custom_command.is_none()); - } - - #[test] - fn test_builder_pattern() { - let config = DownloadConfig::new() - .with_backend(DownloaderBackend::Trauma) - .with_max_concurrent(8) - .with_retries(5) - .with_timeout(600) - .with_custom_command("wget -O \"${FILE}\" \"${URI}\""); - - assert_eq!(config.backend, DownloaderBackend::Trauma); - assert_eq!(config.max_concurrent, 8); - assert_eq!(config.retries, 5); - assert_eq!(config.timeout_seconds, 600); - assert!(config.custom_command.is_some()); - } - - #[test] - fn test_json_serialization() { - let config = DownloadConfig::new().with_max_concurrent(8); - let json = config.to_json().unwrap(); - let deserialized: DownloadConfig = DownloadConfig::from_json(&json).unwrap(); - - assert_eq!(config.backend, deserialized.backend); - assert_eq!(config.max_concurrent, deserialized.max_concurrent); - } -} diff --git a/src/downloader/error.rs b/src/downloader/error.rs deleted file mode 100644 index 7384726..0000000 --- a/src/downloader/error.rs +++ /dev/null @@ -1,95 +0,0 @@ -//! Error types for the downloader module - -use std::fmt; - -/// Error type for download operations -#[derive(Debug, Clone)] -pub struct DownloadError { - pub kind: ErrorKind, - pub message: String, -} - -/// Kinds of download errors -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ErrorKind { - /// Network error (connection failed, timeout, etc.) - Network, - /// File system error (permission denied, disk full, etc.) - FileSystem, - /// Invalid input (bad URL, invalid path, etc.) - InvalidInput, - /// Task not found - TaskNotFound, - /// Task already exists - TaskAlreadyExists, - /// Download was cancelled - Cancelled, - /// Operation not supported by this downloader implementation - Unsupported, - /// Unknown error - Unknown, -} - -impl DownloadError { - pub fn new(kind: ErrorKind, message: impl Into) -> Self { - Self { - kind, - message: message.into(), - } - } - - pub fn network(message: impl Into) -> Self { - Self::new(ErrorKind::Network, message) - } - - pub fn file_system(message: impl Into) -> Self { - Self::new(ErrorKind::FileSystem, message) - } - - pub fn invalid_input(message: impl Into) -> Self { - Self::new(ErrorKind::InvalidInput, message) - } - - pub fn task_not_found(task_id: impl fmt::Display) -> Self { - Self::new( - ErrorKind::TaskNotFound, - format!("Task not found: {}", task_id), - ) - } - - pub fn task_already_exists(task_id: impl fmt::Display) -> Self { - Self::new( - ErrorKind::TaskAlreadyExists, - format!("Task already exists: {}", task_id), - ) - } - - pub fn cancelled(message: impl Into) -> Self { - Self::new(ErrorKind::Cancelled, message) - } - - pub fn unsupported(message: impl Into) -> Self { - Self::new(ErrorKind::Unsupported, message) - } - - pub fn unknown(message: impl Into) -> Self { - Self::new(ErrorKind::Unknown, message) - } -} - -impl fmt::Display for DownloadError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}: {}", self.kind, self.message) - } -} - -impl std::error::Error for DownloadError {} - -impl From for DownloadError { - fn from(err: std::io::Error) -> Self { - Self::file_system(err.to_string()) - } -} - -/// Result type for download operations -pub type Result = std::result::Result; diff --git a/src/downloader/external_rpc_impl.rs b/src/downloader/external_rpc_impl.rs deleted file mode 100644 index e98debd..0000000 --- a/src/downloader/external_rpc_impl.rs +++ /dev/null @@ -1,290 +0,0 @@ -//! External RPC-based downloader implementation -//! -//! Delegates download operations to an external service via HTTP JSON-RPC 2.0. -//! The external service (e.g., a Kotlin-side GooglePlayDownloader) must implement -//! the standard downloader RPC protocol: -//! - download_submit(url, dest_path, headers?, cookies?) -> {task_id} -//! - download_get_status(task_id) -> TaskInfo -//! - download_wait_for_change(task_id, timeout_seconds) -> TaskInfo -//! - download_pause(task_id) -> bool -//! - download_resume(task_id) -> bool -//! - download_cancel(task_id) -> bool - -use super::error::{DownloadError, Result}; -use super::traits::{Downloader, DownloaderCapabilities, ProgressCallback, RequestOptions}; -use async_trait::async_trait; -use parking_lot::RwLock; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::path::{Path, PathBuf}; - -/// JSON-RPC 2.0 request structure -#[derive(Serialize)] -struct JsonRpcRequest<'a> { - jsonrpc: &'static str, - method: &'a str, - params: serde_json::Value, - id: u64, -} - -/// JSON-RPC 2.0 response structure -#[derive(Deserialize)] -struct JsonRpcResponse { - #[allow(dead_code)] - jsonrpc: Option, - result: Option, - error: Option, - #[allow(dead_code)] - id: Option, -} - -#[derive(Deserialize)] -struct JsonRpcError { - #[allow(dead_code)] - code: i64, - message: String, -} - -/// Task ID response from external download_submit -#[derive(Deserialize)] -struct ExternalTaskIdResponse { - task_id: String, -} - -/// Task info from external service (subset we care about) -#[derive(Deserialize, Debug)] -struct ExternalTaskInfo { - #[allow(dead_code)] - task_id: String, - state: String, - progress: ExternalProgress, - error: Option, -} - -#[derive(Deserialize, Debug)] -struct ExternalProgress { - downloaded_bytes: u64, - total_bytes: Option, - #[allow(dead_code)] - speed_bytes_per_sec: Option, - #[allow(dead_code)] - eta_seconds: Option, -} - -/// Downloader that delegates all operations to an external JSON-RPC service. -/// -/// The external service is expected to implement the full downloader protocol -/// (submit, status, wait_for_change, pause, resume, cancel). -pub struct ExternalRpcDownloader { - rpc_url: String, - http_client: reqwest::Client, - /// url -> external task_id (for routing cancel/pause/resume by url) - task_mapping: RwLock>, - /// Atomic request ID counter - request_id: std::sync::atomic::AtomicU64, - capabilities: DownloaderCapabilities, -} - -impl ExternalRpcDownloader { - /// Create a new external RPC downloader pointing at the given service URL. - pub fn new(rpc_url: String) -> Self { - Self { - rpc_url, - http_client: reqwest::Client::new(), - task_mapping: RwLock::new(HashMap::new()), - request_id: std::sync::atomic::AtomicU64::new(1), - capabilities: DownloaderCapabilities::all_enabled(), - } - } - - /// Make a JSON-RPC call to the external service. - async fn rpc_call( - &self, - method: &str, - params: serde_json::Value, - ) -> Result { - let id = self - .request_id - .fetch_add(1, std::sync::atomic::Ordering::Relaxed); - - let request = JsonRpcRequest { - jsonrpc: "2.0", - method, - params, - id, - }; - - let response = self - .http_client - .post(&self.rpc_url) - .json(&request) - .send() - .await - .map_err(|e| DownloadError::network(format!("RPC request failed: {}", e)))?; - - let rpc_response: JsonRpcResponse = response - .json() - .await - .map_err(|e| DownloadError::network(format!("RPC response parse failed: {}", e)))?; - - if let Some(err) = rpc_response.error { - return Err(DownloadError::network(format!( - "RPC error: {}", - err.message - ))); - } - - let result = rpc_response - .result - .ok_or_else(|| DownloadError::network("RPC response missing result"))?; - - serde_json::from_value(result) - .map_err(|e| DownloadError::network(format!("RPC result deserialize failed: {}", e))) - } -} - -#[async_trait] -impl Downloader for ExternalRpcDownloader { - async fn download( - &self, - url: &str, - dest: &Path, - progress: Option, - options: Option, - ) -> Result<()> { - // 1. Submit download task to external service - let params = serde_json::json!({ - "url": url, - "dest_path": dest.to_str().unwrap_or(""), - "headers": options.as_ref().and_then(|o| o.headers.clone()), - "cookies": options.as_ref().and_then(|o| o.cookies.clone()), - }); - - let submit_response: ExternalTaskIdResponse = - self.rpc_call("download_submit", params).await?; - let external_task_id = submit_response.task_id; - - // 2. Record mapping for cancel/pause/resume - self.task_mapping - .write() - .insert(url.to_string(), external_task_id.clone()); - - // 3. Poll for status changes until terminal state - loop { - let params = serde_json::json!({ - "task_id": &external_task_id, - "timeout_seconds": 30_u64, - }); - - let task_info: ExternalTaskInfo = - match self.rpc_call("download_wait_for_change", params).await { - Ok(info) => info, - Err(e) => { - // On poll error, try a direct status check - let status_params = serde_json::json!({ - "task_id": &external_task_id, - }); - match self - .rpc_call::("download_get_status", status_params) - .await - { - Ok(info) => info, - Err(_) => { - // Both failed, clean up and return error - self.task_mapping.write().remove(url); - return Err(e); - } - } - } - }; - - // Update progress callback - if let Some(ref cb) = progress { - cb( - task_info.progress.downloaded_bytes, - task_info.progress.total_bytes, - ); - } - - // Check terminal states - match task_info.state.as_str() { - "completed" => { - self.task_mapping.write().remove(url); - return Ok(()); - } - "failed" => { - self.task_mapping.write().remove(url); - let msg = task_info - .error - .unwrap_or_else(|| "External download failed".to_string()); - return Err(DownloadError::network(msg)); - } - "cancelled" => { - self.task_mapping.write().remove(url); - return Err(DownloadError::cancelled("Download was cancelled")); - } - _ => { - // pending, downloading, stopped — continue polling - continue; - } - } - } - } - - async fn download_batch(&self, tasks: Vec<(String, PathBuf)>) -> Vec> { - // Simple sequential implementation for external downloaders - let mut results = Vec::with_capacity(tasks.len()); - for (url, dest) in tasks { - let result = self.download(&url, &dest, None, None).await; - results.push(result); - } - results - } - - fn name(&self) -> &str { - "external_rpc" - } - - fn capabilities(&self) -> &DownloaderCapabilities { - &self.capabilities - } - - async fn cancel(&self, url: &str) -> Result<()> { - let ext_id = self.task_mapping.read().get(url).cloned(); - if let Some(ext_id) = ext_id { - let params = serde_json::json!({"task_id": ext_id}); - let _: bool = self.rpc_call("download_cancel", params).await?; - } - Ok(()) - } - - async fn pause(&self, url: &str) -> Result<()> { - let ext_id = self.task_mapping.read().get(url).cloned(); - if let Some(ext_id) = ext_id { - let params = serde_json::json!({"task_id": ext_id}); - let _: bool = self.rpc_call("download_pause", params).await?; - } - Ok(()) - } - - async fn resume(&self, url: &str) -> Result<()> { - let ext_id = self.task_mapping.read().get(url).cloned(); - if let Some(ext_id) = ext_id { - let params = serde_json::json!({"task_id": ext_id}); - let _: bool = self.rpc_call("download_resume", params).await?; - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_create_external_downloader() { - let dl = ExternalRpcDownloader::new("http://127.0.0.1:12345".to_string()); - assert_eq!(dl.name(), "external_rpc"); - assert!(dl.capabilities().supports_pause); - } -} diff --git a/src/downloader/hub_dispatch.rs b/src/downloader/hub_dispatch.rs deleted file mode 100644 index c6b753b..0000000 --- a/src/downloader/hub_dispatch.rs +++ /dev/null @@ -1,208 +0,0 @@ -//! Hub-based download dispatcher -//! -//! Routes download tasks to registered external downloaders based on hub_uuid. -//! If no external downloader is registered for the given hub_uuid, falls back -//! to the default built-in downloader (TraumaDownloader / HTTP). - -use super::error::Result; -use super::traits::{Downloader, DownloaderCapabilities, ProgressCallback, RequestOptions}; -use async_trait::async_trait; -use parking_lot::RwLock; -use std::collections::HashMap; -use std::path::{Path, PathBuf}; -use std::sync::Arc; - -/// Shared internal state for HubDispatchDownloader. -/// -/// Wrapped in Arc so that clones are cheap and share the same state. -/// This allows the RPC server to hold a reference for register/unregister -/// while DownloadTaskManager holds another reference as Box. -struct HubDispatchState { - /// hub_uuid -> external downloader - external: RwLock>>>, - /// Default downloader for unregistered hub_uuids (TraumaDownloader) - default: Arc>, - /// Active task tracking: url -> hub_uuid (for routing cancel/pause/resume) - active_tasks: RwLock>>, -} - -/// Downloads dispatcher that routes tasks by hub_uuid. -/// -/// - If `hub_uuid` is provided (via `RequestOptions.metadata["hub_uuid"]`) -/// and a downloader is registered for it, the task is routed there. -/// - Otherwise, the default built-in HTTP downloader handles it. -/// -/// This struct is cheaply cloneable (Arc-based shared state). -pub struct HubDispatchDownloader { - state: Arc, -} - -impl HubDispatchDownloader { - /// Create a new dispatcher with the given default downloader. - pub fn new(default: Box) -> Self { - Self { - state: Arc::new(HubDispatchState { - external: RwLock::new(HashMap::new()), - default: Arc::new(default), - active_tasks: RwLock::new(HashMap::new()), - }), - } - } - - /// Register an external downloader for a specific hub_uuid. - pub fn register(&self, hub_uuid: &str, downloader: Box) { - self.state - .external - .write() - .insert(hub_uuid.to_string(), Arc::new(downloader)); - } - - /// Unregister the external downloader for a specific hub_uuid. - pub fn unregister(&self, hub_uuid: &str) { - self.state.external.write().remove(hub_uuid); - } - - /// Check if a downloader is registered for the given hub_uuid. - pub fn has_downloader(&self, hub_uuid: &str) -> bool { - self.state.external.read().contains_key(hub_uuid) - } - - /// Resolve which downloader to use based on hub_uuid. - fn resolve(&self, hub_uuid: Option<&str>) -> Arc> { - if let Some(uuid) = hub_uuid { - if let Some(dl) = self.state.external.read().get(uuid) { - return dl.clone(); - } - } - self.state.default.clone() - } - - /// Extract hub_uuid from RequestOptions metadata. - fn extract_hub_uuid(options: Option<&RequestOptions>) -> Option { - options - .and_then(|o| o.metadata.as_ref()) - .and_then(|m| m.get("hub_uuid")) - .cloned() - } - - /// Record an active task for url -> hub_uuid routing. - fn track_task(&self, url: &str, hub_uuid: Option) { - self.state - .active_tasks - .write() - .insert(url.to_string(), hub_uuid); - } - - /// Remove an active task record. - fn untrack_task(&self, url: &str) { - self.state.active_tasks.write().remove(url); - } - - /// Look up which hub_uuid (if any) an active URL belongs to. - fn lookup_task_hub(&self, url: &str) -> Option> { - self.state.active_tasks.read().get(url).cloned() - } -} - -impl Clone for HubDispatchDownloader { - fn clone(&self) -> Self { - Self { - state: self.state.clone(), - } - } -} - -#[async_trait] -impl Downloader for HubDispatchDownloader { - async fn download( - &self, - url: &str, - dest: &Path, - progress: Option, - options: Option, - ) -> Result<()> { - let hub_uuid = Self::extract_hub_uuid(options.as_ref()); - let downloader = self.resolve(hub_uuid.as_deref()); - - // Track this task so cancel/pause/resume can find the right downloader - self.track_task(url, hub_uuid); - - let result = downloader.download(url, dest, progress, options).await; - - // Clean up tracking on completion (success or failure) - self.untrack_task(url); - - result - } - - async fn download_batch(&self, tasks: Vec<(String, PathBuf)>) -> Vec> { - // Batch downloads go through the default downloader since there's - // no per-task metadata available in the batch interface. - self.state.default.download_batch(tasks).await - } - - fn name(&self) -> &str { - "hub_dispatch" - } - - fn capabilities(&self) -> &DownloaderCapabilities { - // Return default backend capabilities - self.state.default.capabilities() - } - - async fn cancel(&self, url: &str) -> Result<()> { - let hub_uuid = self.lookup_task_hub(url).flatten(); - self.resolve(hub_uuid.as_deref()).cancel(url).await - } - - async fn pause(&self, url: &str) -> Result<()> { - let hub_uuid = self.lookup_task_hub(url).flatten(); - self.resolve(hub_uuid.as_deref()).pause(url).await - } - - async fn resume(&self, url: &str) -> Result<()> { - let hub_uuid = self.lookup_task_hub(url).flatten(); - self.resolve(hub_uuid.as_deref()).resume(url).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::downloader::TraumaDownloader; - - #[test] - fn test_create_dispatcher() { - let default = Box::new(TraumaDownloader::default_settings()); - let dispatcher = HubDispatchDownloader::new(default); - assert_eq!(dispatcher.name(), "hub_dispatch"); - assert!(!dispatcher.has_downloader("some-uuid")); - } - - #[test] - fn test_register_unregister() { - let default = Box::new(TraumaDownloader::default_settings()); - let dispatcher = HubDispatchDownloader::new(default); - - let ext = Box::new(TraumaDownloader::default_settings()); - dispatcher.register("test-uuid", ext); - assert!(dispatcher.has_downloader("test-uuid")); - - dispatcher.unregister("test-uuid"); - assert!(!dispatcher.has_downloader("test-uuid")); - } - - #[test] - fn test_clone_shares_state() { - let default = Box::new(TraumaDownloader::default_settings()); - let dispatcher = HubDispatchDownloader::new(default); - - let clone = dispatcher.clone(); - - let ext = Box::new(TraumaDownloader::default_settings()); - dispatcher.register("shared-uuid", ext); - - // Clone should see the registration - assert!(clone.has_downloader("shared-uuid")); - } -} diff --git a/src/downloader/mod.rs b/src/downloader/mod.rs deleted file mode 100644 index d4f2233..0000000 --- a/src/downloader/mod.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! Downloader module providing pluggable download functionality -//! -//! This module implements a flexible, trait-based downloader system that supports: -//! - Multiple backend implementations (trauma, reqwest, custom CLI, etc.) -//! - Task state management with progress tracking -//! - Long-polling for download status updates -//! - Configuration-driven backend selection -//! - JSON-RPC integration for remote control - -mod config; -mod error; -mod external_rpc_impl; -mod hub_dispatch; -mod state; -mod task_manager; -mod traits; -mod trauma_impl; - -pub use config::{DownloadConfig, DownloaderBackend}; -pub use error::{DownloadError, Result}; -pub use external_rpc_impl::ExternalRpcDownloader; -pub use hub_dispatch::HubDispatchDownloader; -pub use state::{DownloadProgress, DownloadState, SpeedCalculator, TaskInfo}; -pub use task_manager::DownloadTaskManager; -pub use traits::Downloader; -pub use trauma_impl::TraumaDownloader; - -/// Create a downloader instance based on the provided configuration -pub fn create_downloader(config: &DownloadConfig) -> Box { - match config.backend { - DownloaderBackend::Trauma => Box::new(TraumaDownloader::new( - config.max_concurrent, - config.retries, - config.timeout_seconds, - )), - // Future implementations can be added here - // DownloaderBackend::Reqwest => Box::new(ReqwestDownloader::new(config)), - // DownloaderBackend::Custom => Box::new(CliDownloader::new(config)), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_create_trauma_downloader() { - let config = DownloadConfig::default(); - let downloader = create_downloader(&config); - assert_eq!(downloader.name(), "reqwest"); - - // Test that capabilities are accessible - let caps = downloader.capabilities(); - assert!(caps.supports_pause); - assert!(caps.supports_resume); - assert!(caps.supports_cancellation); - } -} diff --git a/src/downloader/state.rs b/src/downloader/state.rs deleted file mode 100644 index 718b33b..0000000 --- a/src/downloader/state.rs +++ /dev/null @@ -1,532 +0,0 @@ -//! Download state and progress tracking types - -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::collections::VecDeque; -use std::time::{Instant, SystemTime, UNIX_EPOCH}; -use uuid::Uuid; - -/// Speed calculator using sliding window for smooth speed measurement -/// -/// Keeps track of download samples over a time window and calculates -/// average speed to avoid fluctuations. -#[derive(Debug, Clone)] -pub struct SpeedCalculator { - /// Samples: (timestamp, downloaded_bytes) - samples: VecDeque<(Instant, u64)>, - /// Window size in seconds - window_secs: u64, - /// Maximum number of samples to keep - max_samples: usize, - /// Start time for calculating speed when only one sample exists - start_time: Option, -} - -impl SpeedCalculator { - /// Create a new speed calculator with specified window size - pub fn new(window_secs: u64) -> Self { - Self { - samples: VecDeque::with_capacity(64), - window_secs, - max_samples: 64, - start_time: None, - } - } - - /// Create with default 5-second window - pub fn default_window() -> Self { - Self::new(5) - } - - /// Record a new sample - pub fn record(&mut self, downloaded_bytes: u64) { - let now = Instant::now(); - - // Initialize start time on first record - if self.start_time.is_none() { - self.start_time = Some(now); - } - - // Remove samples outside the window - let cutoff = now - std::time::Duration::from_secs(self.window_secs); - while let Some((time, _)) = self.samples.front() { - if *time < cutoff { - self.samples.pop_front(); - } else { - break; - } - } - - // Add new sample - self.samples.push_back((now, downloaded_bytes)); - - // Limit max samples - while self.samples.len() > self.max_samples { - self.samples.pop_front(); - } - } - - /// Calculate average speed in bytes per second - pub fn speed_bytes_per_sec(&self) -> Option { - let (last_time, last_bytes) = self.samples.back()?; - - // Use sliding window if we have multiple samples - if self.samples.len() >= 2 { - let (first_time, first_bytes) = self.samples.front()?; - let duration = last_time.duration_since(*first_time); - let duration_secs = duration.as_secs_f64().max(0.001); - let bytes_diff = last_bytes.saturating_sub(*first_bytes); - return Some((bytes_diff as f64 / duration_secs) as u64); - } - - // For single sample, calculate from start time - if let Some(start) = self.start_time { - let duration = last_time.duration_since(start); - let duration_secs = duration.as_secs_f64().max(0.001); - return Some((*last_bytes as f64 / duration_secs) as u64); - } - - None - } - - /// Reset the calculator - pub fn reset(&mut self) { - self.samples.clear(); - self.start_time = None; - } -} - -/// Serialize SystemTime as Unix timestamp in milliseconds -fn serialize_system_time(time: &SystemTime, serializer: S) -> Result -where - S: Serializer, -{ - let millis = time - .duration_since(UNIX_EPOCH) - .map(|d| d.as_millis() as i64) - .unwrap_or(0); - serializer.serialize_i64(millis) -} - -/// Serialize Option as Unix timestamp in milliseconds -fn serialize_option_system_time( - time: &Option, - serializer: S, -) -> Result -where - S: Serializer, -{ - match time { - Some(t) => { - let millis = t - .duration_since(UNIX_EPOCH) - .map(|d| d.as_millis() as i64) - .unwrap_or(0); - serializer.serialize_some(&millis) - } - None => serializer.serialize_none(), - } -} - -/// Deserialize Unix timestamp in milliseconds to SystemTime -fn deserialize_system_time<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let millis = i64::deserialize(deserializer)?; - Ok(UNIX_EPOCH + std::time::Duration::from_millis(millis as u64)) -} - -/// Deserialize Unix timestamp in milliseconds to Option -fn deserialize_option_system_time<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let opt: Option = Option::deserialize(deserializer)?; - Ok(opt.map(|millis| UNIX_EPOCH + std::time::Duration::from_millis(millis as u64))) -} - -/// Download task state -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum DownloadState { - /// Task is queued but not started yet - Pending, - /// Download is in progress - Downloading, - /// Download is paused (can be resumed) - Stopped, - /// Download completed successfully - Completed, - /// Download failed - Failed, - /// Download was cancelled - Cancelled, -} - -impl DownloadState { - /// Check if the task is in a terminal state (completed, failed, or cancelled) - pub fn is_terminal(&self) -> bool { - matches!( - self, - DownloadState::Completed | DownloadState::Failed | DownloadState::Cancelled - ) - } - - /// Check if the task is active (pending, downloading, or stopped/paused) - pub fn is_active(&self) -> bool { - matches!( - self, - DownloadState::Pending | DownloadState::Downloading | DownloadState::Stopped - ) - } - - /// Check if the task can be resumed - pub fn is_resumable(&self) -> bool { - matches!(self, DownloadState::Stopped | DownloadState::Failed) - } - - /// Check if the task can be paused - pub fn is_pausable(&self) -> bool { - matches!(self, DownloadState::Downloading) - } -} - -/// Download progress information -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct DownloadProgress { - /// Number of bytes downloaded so far - pub downloaded_bytes: u64, - - /// Total size in bytes (None if unknown) - pub total_bytes: Option, - - /// Download speed in bytes per second (average) - pub speed_bytes_per_sec: Option, - - /// Estimated time remaining in seconds (None if unknown) - pub eta_seconds: Option, -} - -impl DownloadProgress { - /// Create a new progress with downloaded bytes - pub fn new(downloaded_bytes: u64, total_bytes: Option) -> Self { - Self { - downloaded_bytes, - total_bytes, - speed_bytes_per_sec: None, - eta_seconds: None, - } - } - - /// Create a new progress with speed and ETA - pub fn with_speed( - downloaded_bytes: u64, - total_bytes: Option, - speed_bytes_per_sec: Option, - ) -> Self { - let eta_seconds = match (total_bytes, speed_bytes_per_sec) { - (Some(total), Some(speed)) if speed > 0 && total > downloaded_bytes => { - Some((total - downloaded_bytes) / speed) - } - _ => None, - }; - - Self { - downloaded_bytes, - total_bytes, - speed_bytes_per_sec, - eta_seconds, - } - } - - /// Calculate progress percentage (0-100) - pub fn percentage(&self) -> Option { - self.total_bytes.map(|total| { - if total == 0 { - 0.0 - } else { - (self.downloaded_bytes as f64 / total as f64) * 100.0 - } - }) - } - - /// Check if download is complete - pub fn is_complete(&self) -> bool { - if let Some(total) = self.total_bytes { - self.downloaded_bytes >= total - } else { - false - } - } -} - -/// Complete information about a download task -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct TaskInfo { - /// Unique task identifier - pub task_id: String, - - /// Source URL - pub url: String, - - /// Destination file path - pub dest_path: String, - - /// Current state - pub state: DownloadState, - - /// Progress information - pub progress: DownloadProgress, - - /// Resume offset in bytes (for resuming paused downloads) - pub resume_offset: u64, - - /// Whether the server supports HTTP Range requests (None if not tested yet) - pub supports_range: Option, - - /// Error message (if state is Failed) - pub error: Option, - - /// Task creation timestamp (Unix timestamp in milliseconds) - #[serde( - serialize_with = "serialize_system_time", - deserialize_with = "deserialize_system_time" - )] - pub created_at: SystemTime, - - /// Task start timestamp (Unix timestamp in milliseconds, None if not started yet) - #[serde( - serialize_with = "serialize_option_system_time", - deserialize_with = "deserialize_option_system_time" - )] - pub started_at: Option, - - /// Task completion timestamp (Unix timestamp in milliseconds, None if not completed) - #[serde( - serialize_with = "serialize_option_system_time", - deserialize_with = "deserialize_option_system_time" - )] - pub completed_at: Option, - - /// Task last paused timestamp (Unix timestamp in milliseconds, None if never paused) - #[serde( - serialize_with = "serialize_option_system_time", - deserialize_with = "deserialize_option_system_time" - )] - pub paused_at: Option, - - /// HTTP headers to include in requests - #[serde(default)] - pub headers: Option>, - - /// HTTP cookies to include in requests - #[serde(default)] - pub cookies: Option>, - - /// Hub UUID for dispatching to registered external downloaders. - /// When set, the download task manager routes this task to the - /// external downloader registered for this hub_uuid. - /// When None, the default built-in downloader is used. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub hub_uuid: Option, -} - -impl TaskInfo { - /// Create a new task info - pub fn new(url: impl Into, dest_path: impl Into) -> Self { - Self { - task_id: Uuid::new_v4().to_string(), - url: url.into(), - dest_path: dest_path.into(), - state: DownloadState::Pending, - progress: DownloadProgress::default(), - resume_offset: 0, - supports_range: None, - error: None, - created_at: SystemTime::now(), - started_at: None, - completed_at: None, - paused_at: None, - headers: None, - cookies: None, - hub_uuid: None, - } - } - - /// Create with a specific task ID - pub fn with_id( - task_id: impl Into, - url: impl Into, - dest_path: impl Into, - ) -> Self { - Self { - task_id: task_id.into(), - url: url.into(), - dest_path: dest_path.into(), - state: DownloadState::Pending, - progress: DownloadProgress::default(), - resume_offset: 0, - supports_range: None, - error: None, - created_at: SystemTime::now(), - started_at: None, - completed_at: None, - paused_at: None, - headers: None, - cookies: None, - hub_uuid: None, - } - } - - /// Create with headers, cookies, and optional hub_uuid - pub fn with_options( - task_id: impl Into, - url: impl Into, - dest_path: impl Into, - headers: Option>, - cookies: Option>, - hub_uuid: Option, - ) -> Self { - Self { - task_id: task_id.into(), - url: url.into(), - dest_path: dest_path.into(), - state: DownloadState::Pending, - progress: DownloadProgress::default(), - resume_offset: 0, - supports_range: None, - error: None, - created_at: SystemTime::now(), - started_at: None, - completed_at: None, - paused_at: None, - headers, - cookies, - hub_uuid, - } - } - - /// Mark task as started - pub fn mark_started(&mut self) { - self.state = DownloadState::Downloading; - self.started_at = Some(SystemTime::now()); - } - - /// Mark task as completed - pub fn mark_completed(&mut self) { - self.state = DownloadState::Completed; - self.completed_at = Some(SystemTime::now()); - } - - /// Mark task as failed with error message - pub fn mark_failed(&mut self, error: impl Into) { - self.state = DownloadState::Failed; - self.error = Some(error.into()); - self.completed_at = Some(SystemTime::now()); - } - - /// Mark task as cancelled - pub fn mark_cancelled(&mut self) { - self.state = DownloadState::Cancelled; - self.completed_at = Some(SystemTime::now()); - } - - /// Mark task as stopped/paused - pub fn mark_stopped(&mut self) { - self.state = DownloadState::Stopped; - self.resume_offset = self.progress.downloaded_bytes; - self.paused_at = Some(SystemTime::now()); - } - - /// Mark task as resumed from stopped state - pub fn mark_resumed(&mut self) { - self.state = DownloadState::Downloading; - self.paused_at = None; - } - - /// Update progress - pub fn update_progress(&mut self, progress: DownloadProgress) { - self.progress = progress; - } - - /// Set whether the server supports Range requests - pub fn set_range_support(&mut self, supports: bool) { - self.supports_range = Some(supports); - } - - /// Calculate elapsed time in seconds - pub fn elapsed_seconds(&self) -> Option { - self.started_at.and_then(|start| { - SystemTime::now() - .duration_since(start) - .ok() - .map(|d| d.as_secs()) - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_download_state() { - assert!(DownloadState::Completed.is_terminal()); - assert!(DownloadState::Failed.is_terminal()); - assert!(DownloadState::Cancelled.is_terminal()); - assert!(!DownloadState::Pending.is_terminal()); - assert!(!DownloadState::Downloading.is_terminal()); - assert!(!DownloadState::Stopped.is_terminal()); - - assert!(DownloadState::Pending.is_active()); - assert!(DownloadState::Downloading.is_active()); - assert!(DownloadState::Stopped.is_active()); - assert!(!DownloadState::Completed.is_active()); - - assert!(DownloadState::Downloading.is_pausable()); - assert!(!DownloadState::Stopped.is_pausable()); - assert!(!DownloadState::Completed.is_pausable()); - - assert!(DownloadState::Stopped.is_resumable()); - assert!(DownloadState::Failed.is_resumable()); - assert!(!DownloadState::Downloading.is_resumable()); - assert!(!DownloadState::Completed.is_resumable()); - } - - #[test] - fn test_download_progress() { - let mut progress = DownloadProgress::new(50, Some(100)); - assert_eq!(progress.percentage(), Some(50.0)); - assert!(!progress.is_complete()); - - progress.downloaded_bytes = 100; - assert_eq!(progress.percentage(), Some(100.0)); - assert!(progress.is_complete()); - } - - #[test] - fn test_task_info() { - let mut task = TaskInfo::new("http://example.com/file", "/tmp/file"); - assert_eq!(task.state, DownloadState::Pending); - assert!(task.started_at.is_none()); - assert!(task.completed_at.is_none()); - - task.mark_started(); - assert_eq!(task.state, DownloadState::Downloading); - assert!(task.started_at.is_some()); - - task.mark_completed(); - assert_eq!(task.state, DownloadState::Completed); - assert!(task.completed_at.is_some()); - } - - #[test] - fn test_task_info_failure() { - let mut task = TaskInfo::new("http://example.com/file", "/tmp/file"); - task.mark_failed("Network error"); - - assert_eq!(task.state, DownloadState::Failed); - assert_eq!(task.error, Some("Network error".to_string())); - assert!(task.completed_at.is_some()); - } -} diff --git a/src/downloader/task_manager.rs b/src/downloader/task_manager.rs deleted file mode 100644 index 4750517..0000000 --- a/src/downloader/task_manager.rs +++ /dev/null @@ -1,1009 +0,0 @@ -//! Download task manager with state tracking and long-polling support - -use super::config::DownloadConfig; -use super::error::{DownloadError, Result}; -use super::state::{DownloadProgress, DownloadState, SpeedCalculator, TaskInfo}; -use super::traits::{Downloader, DownloaderCapabilities}; -use parking_lot::RwLock; -use std::collections::HashMap; -use std::path::PathBuf; -use std::sync::Arc; -use std::time::{Duration, SystemTime}; -use tokio::sync::Notify; -use uuid::Uuid; - -/// Download task manager -/// -/// Manages all download tasks, tracks their state, and provides long-polling support -/// for status updates. -pub struct DownloadTaskManager { - /// Task storage: task_id -> TaskInfo - tasks: Arc>>, - - /// Downloader implementation - downloader: Arc>, - - /// Notification system for state changes (used for long-polling) - notifier: Arc, - - /// Downloader capabilities (cached from downloader at initialization) - capabilities: DownloaderCapabilities, -} - -impl DownloadTaskManager { - /// Create a new task manager with the given downloader - pub fn new(downloader: Box) -> Self { - // Cache capabilities from downloader at initialization - let capabilities = downloader.capabilities().clone(); - - Self { - tasks: Arc::new(RwLock::new(HashMap::new())), - downloader: Arc::new(downloader), - notifier: Arc::new(Notify::new()), - capabilities, - } - } - - /// Create a task manager from configuration - pub fn from_config(config: &DownloadConfig) -> Self { - let downloader = super::create_downloader(config); - Self::new(downloader) - } - - /// Submit a new download task - /// - /// # Arguments - /// * `url` - The URL to download from - /// * `dest_path` - The destination file path - /// - /// # Returns - /// * `Ok(task_id)` - The unique task ID - /// * `Err(DownloadError)` - If the task could not be created - pub fn submit_task( - &self, - url: impl Into, - dest_path: impl Into, - ) -> Result { - let task_id = Uuid::new_v4().to_string(); - let task = TaskInfo::with_id(task_id.clone(), url, dest_path); - - { - let mut tasks = self.tasks.write(); - if tasks.contains_key(&task_id) { - return Err(DownloadError::task_already_exists(&task_id)); - } - tasks.insert(task_id.clone(), task); - } - - // Notify listeners about new task - self.notifier.notify_waiters(); - - // Start download in background - let manager = self.clone_for_task(); - let task_id_clone = task_id.clone(); - tokio::spawn(async move { - manager.execute_task(&task_id_clone).await; - }); - - Ok(task_id) - } - - /// Submit a new download task with optional headers, cookies, and hub_uuid - /// - /// # Arguments - /// * `url` - The URL to download from - /// * `dest_path` - The destination file path - /// * `headers` - Optional HTTP headers - /// * `cookies` - Optional HTTP cookies - /// * `hub_uuid` - Optional hub UUID for routing to external downloaders - /// - /// # Returns - /// * `Ok(task_id)` - The unique task ID - /// * `Err(DownloadError)` - If the task could not be created - pub fn submit_task_with_options( - &self, - url: impl Into, - dest_path: impl Into, - headers: Option>, - cookies: Option>, - hub_uuid: Option, - ) -> Result { - let task_id = Uuid::new_v4().to_string(); - let task = - TaskInfo::with_options(task_id.clone(), url, dest_path, headers, cookies, hub_uuid); - - { - let mut tasks = self.tasks.write(); - if tasks.contains_key(&task_id) { - return Err(DownloadError::task_already_exists(&task_id)); - } - tasks.insert(task_id.clone(), task); - } - - // Notify listeners about new task - self.notifier.notify_waiters(); - - // Start download in background - let manager = self.clone_for_task(); - let task_id_clone = task_id.clone(); - tokio::spawn(async move { - manager.execute_task(&task_id_clone).await; - }); - - Ok(task_id) - } - - /// Submit multiple download tasks - /// - /// # Returns - /// Vector of task IDs for each submitted task - pub fn submit_batch(&self, tasks: Vec<(String, String)>) -> Result> { - let mut task_ids = Vec::new(); - - for (url, dest_path) in tasks { - let task_id = self.submit_task(url, dest_path)?; - task_ids.push(task_id); - } - - Ok(task_ids) - } - - /// Get task information by ID - pub fn get_task(&self, task_id: &str) -> Result { - let tasks = self.tasks.read(); - tasks - .get(task_id) - .cloned() - .ok_or_else(|| DownloadError::task_not_found(task_id)) - } - - /// Get all tasks - pub fn get_all_tasks(&self) -> Vec { - let tasks = self.tasks.read(); - tasks.values().cloned().collect() - } - - /// Get tasks by state - pub fn get_tasks_by_state(&self, state: DownloadState) -> Vec { - let tasks = self.tasks.read(); - tasks - .values() - .filter(|task| task.state == state) - .cloned() - .collect() - } - - /// Get active tasks (pending or downloading) - pub fn get_active_tasks(&self) -> Vec { - let tasks = self.tasks.read(); - tasks - .values() - .filter(|task| task.state.is_active()) - .cloned() - .collect() - } - - /// Get downloader capabilities - /// - /// Returns the capabilities of the underlying downloader implementation. - /// These capabilities are determined at initialization time. - pub fn get_capabilities(&self) -> &DownloaderCapabilities { - &self.capabilities - } - - /// Cancel a task - pub fn cancel_task(&self, task_id: &str) -> Result<()> { - let mut tasks = self.tasks.write(); - let task = tasks - .get_mut(task_id) - .ok_or_else(|| DownloadError::task_not_found(task_id))?; - - if task.state.is_terminal() { - return Err(DownloadError::invalid_input(format!( - "Task {} is already in terminal state: {:?}", - task_id, task.state - ))); - } - - task.mark_cancelled(); - self.notifier.notify_waiters(); - - Ok(()) - } - - /// Pause a download task - /// - /// # Arguments - /// * `task_id` - The task ID to pause - /// - /// # Returns - /// * `Ok(())` - Task paused successfully - /// * `Err(DownloadError)` - If task not found or cannot be paused - pub async fn pause_task(&self, task_id: &str) -> Result<()> { - // Check if downloader supports pause - if !self.capabilities.supports_pause { - return Err(DownloadError::unsupported( - "This downloader does not support pause functionality", - )); - } - - // Get task URL and verify state - let url = { - let tasks = self.tasks.read(); - let task = tasks - .get(task_id) - .ok_or_else(|| DownloadError::task_not_found(task_id))?; - - if !task.state.is_pausable() { - return Err(DownloadError::invalid_input(format!( - "Task {} cannot be paused in state: {:?}", - task_id, task.state - ))); - } - - task.url.clone() - }; - - // Call downloader pause - self.downloader.pause(&url).await?; - - // Update task state - { - let mut tasks = self.tasks.write(); - if let Some(task) = tasks.get_mut(task_id) { - task.mark_stopped(); - } - } - - self.notifier.notify_waiters(); - Ok(()) - } - - /// Resume a paused download task - /// - /// # Arguments - /// * `task_id` - The task ID to resume - /// - /// # Returns - /// * `Ok(())` - Task resumed successfully - /// * `Err(DownloadError)` - If task not found or cannot be resumed - pub async fn resume_task(&self, task_id: &str) -> Result<()> { - // Check if downloader supports resume - if !self.capabilities.supports_resume { - return Err(DownloadError::unsupported( - "This downloader does not support resume functionality", - )); - } - - // Get task details and verify state - let (url, dest_path, headers, cookies, hub_uuid) = { - let tasks = self.tasks.read(); - let task = tasks - .get(task_id) - .ok_or_else(|| DownloadError::task_not_found(task_id))?; - - if !task.state.is_resumable() { - return Err(DownloadError::invalid_input(format!( - "Task {} cannot be resumed from state: {:?}", - task_id, task.state - ))); - } - - ( - task.url.clone(), - task.dest_path.clone(), - task.headers.clone(), - task.cookies.clone(), - task.hub_uuid.clone(), - ) - }; - - // Mark as resumed - { - let mut tasks = self.tasks.write(); - if let Some(task) = tasks.get_mut(task_id) { - task.mark_resumed(); - } - } - - self.notifier.notify_waiters(); - - // Restart download in background - let manager = self.clone_for_task(); - let task_id_clone = task_id.to_string(); - tokio::spawn(async move { - // Create request options with headers, cookies, and hub_uuid metadata - let mut metadata = std::collections::HashMap::new(); - if let Some(uuid) = &hub_uuid { - metadata.insert("hub_uuid".to_string(), uuid.clone()); - } - - let options = if headers.is_some() || cookies.is_some() || !metadata.is_empty() { - Some(super::traits::RequestOptions { - headers, - cookies, - metadata: if metadata.is_empty() { - None - } else { - Some(metadata) - }, - }) - } else { - None - }; - - // Create progress callback with speed calculator - let tasks_clone = manager.tasks.clone(); - let task_id_for_callback = task_id_clone.clone(); - let notifier_clone = manager.notifier.clone(); - let speed_calc = Arc::new(RwLock::new(SpeedCalculator::default_window())); - - let progress_callback = Box::new(move |downloaded: u64, total: Option| { - // Record sample and calculate speed - let speed = { - let mut calc = speed_calc.write(); - calc.record(downloaded); - calc.speed_bytes_per_sec() - }; - - // Update task progress - let mut tasks = tasks_clone.write(); - if let Some(task) = tasks.get_mut(&task_id_for_callback) { - task.update_progress(DownloadProgress::with_speed(downloaded, total, speed)); - notifier_clone.notify_waiters(); - } - }); - - // Resume download - let result = manager - .downloader - .download( - &url, - &PathBuf::from(&dest_path), - Some(progress_callback), - options, - ) - .await; - - // Update task state - { - let mut tasks = manager.tasks.write(); - if let Some(task) = tasks.get_mut(&task_id_clone) { - match result { - Ok(_) => task.mark_completed(), - Err(e) => task.mark_failed(e.message), - } - } - } - - manager.notifier.notify_waiters(); - }); - - Ok(()) - } - - /// Wait for task state change (long-polling support) - /// - /// # Arguments - /// * `task_id` - The task ID to monitor - /// * `timeout` - Maximum time to wait for a change - /// - /// # Returns - /// * `Ok(TaskInfo)` - The updated task info - /// * `Err(DownloadError)` - If task not found or timeout occurred - pub async fn wait_for_change(&self, task_id: &str, timeout: Duration) -> Result { - let initial_state = { - let tasks = self.tasks.read(); - let task = tasks - .get(task_id) - .ok_or_else(|| DownloadError::task_not_found(task_id))?; - task.state - }; - - // If already in terminal state, return immediately - if initial_state.is_terminal() { - return self.get_task(task_id); - } - - // Wait for notification with timeout - let notifier = self.notifier.clone(); - let result = tokio::time::timeout(timeout, async { - loop { - notifier.notified().await; - - let tasks = self.tasks.read(); - if let Some(task) = tasks.get(task_id) { - if task.state != initial_state { - return Ok(task.clone()); - } - } else { - return Err(DownloadError::task_not_found(task_id)); - } - } - }) - .await; - - match result { - Ok(task_result) => task_result, - Err(_) => { - // Timeout - return current state - self.get_task(task_id) - } - } - } - - /// Remove completed/failed tasks older than the specified duration - pub fn cleanup_old_tasks(&self, max_age: Duration) { - let mut tasks = self.tasks.write(); - let now = SystemTime::now(); - - tasks.retain(|_, task| { - if !task.state.is_terminal() { - return true; // Keep active tasks - } - - if let Some(completed_at) = task.completed_at { - if let Ok(age) = now.duration_since(completed_at) { - return age < max_age; - } - } - - true - }); - - self.notifier.notify_waiters(); - } - - /// Remove a specific task - pub fn remove_task(&self, task_id: &str) -> Result<()> { - let mut tasks = self.tasks.write(); - let task = tasks - .get(task_id) - .ok_or_else(|| DownloadError::task_not_found(task_id))?; - - if !task.state.is_terminal() { - return Err(DownloadError::invalid_input(format!( - "Cannot remove active task: {}", - task_id - ))); - } - - tasks.remove(task_id); - self.notifier.notify_waiters(); - - Ok(()) - } - - /// Execute a download task - async fn execute_task(&self, task_id: &str) { - // Mark task as started - { - let mut tasks = self.tasks.write(); - if let Some(task) = tasks.get_mut(task_id) { - if task.state == DownloadState::Cancelled { - return; // Task was cancelled before it started - } - task.mark_started(); - } else { - return; // Task not found - } - } - - self.notifier.notify_waiters(); - - // Get task details - let (url, dest_path, headers, cookies, hub_uuid) = { - let tasks = self.tasks.read(); - if let Some(task) = tasks.get(task_id) { - ( - task.url.clone(), - task.dest_path.clone(), - task.headers.clone(), - task.cookies.clone(), - task.hub_uuid.clone(), - ) - } else { - return; - } - }; - - // Create request options with headers, cookies, and hub_uuid metadata - let mut metadata = std::collections::HashMap::new(); - if let Some(uuid) = &hub_uuid { - metadata.insert("hub_uuid".to_string(), uuid.clone()); - } - - let options = if headers.is_some() || cookies.is_some() || !metadata.is_empty() { - Some(super::traits::RequestOptions { - headers, - cookies, - metadata: if metadata.is_empty() { - None - } else { - Some(metadata) - }, - }) - } else { - None - }; - - // Create progress callback with speed calculator - let tasks_clone = self.tasks.clone(); - let task_id_clone = task_id.to_string(); - let notifier_clone = self.notifier.clone(); - let speed_calc = Arc::new(RwLock::new(SpeedCalculator::default_window())); - - let progress_callback = Box::new(move |downloaded: u64, total: Option| { - // Record sample and calculate speed - let speed = { - let mut calc = speed_calc.write(); - calc.record(downloaded); - calc.speed_bytes_per_sec() - }; - - // Update task progress - let mut tasks = tasks_clone.write(); - if let Some(task) = tasks.get_mut(&task_id_clone) { - task.update_progress(DownloadProgress::with_speed(downloaded, total, speed)); - notifier_clone.notify_waiters(); - } - }); - - // Perform download - let result = self - .downloader - .download( - &url, - &PathBuf::from(&dest_path), - Some(progress_callback), - options, - ) - .await; - - // Update task state - { - let mut tasks = self.tasks.write(); - if let Some(task) = tasks.get_mut(task_id) { - match result { - Ok(_) => task.mark_completed(), - Err(e) => task.mark_failed(e.message), - } - } - } - - self.notifier.notify_waiters(); - } - - /// Clone for task execution - fn clone_for_task(&self) -> Self { - Self { - tasks: self.tasks.clone(), - downloader: self.downloader.clone(), - notifier: self.notifier.clone(), - capabilities: self.capabilities.clone(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::downloader::TraumaDownloader; - - #[test] - fn test_create_manager() { - let downloader = Box::new(TraumaDownloader::default_settings()); - let manager = DownloadTaskManager::new(downloader); - - let all_tasks = manager.get_all_tasks(); - assert!(all_tasks.is_empty()); - } - - #[tokio::test] - async fn test_submit_task() { - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - let task_id = manager - .submit_task("http://example.com/file", "/tmp/file") - .unwrap(); - assert!(!task_id.is_empty()); - - let task = manager.get_task(&task_id).unwrap(); - assert_eq!(task.url, "http://example.com/file"); - assert_eq!(task.dest_path, "/tmp/file"); - } - - #[tokio::test] - async fn test_cancel_task() { - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - let task_id = manager - .submit_task("http://example.com/file", "/tmp/file") - .unwrap(); - - // Wait a bit for task to potentially start - tokio::time::sleep(Duration::from_millis(100)).await; - - let result = manager.cancel_task(&task_id); - // May succeed or fail depending on task state - let _ = result; - } - - #[tokio::test] - async fn test_get_tasks_by_state() { - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - let _ = manager.submit_task("http://example.com/file1", "/tmp/file1"); - let _ = manager.submit_task("http://example.com/file2", "/tmp/file2"); - - let pending_tasks = manager.get_tasks_by_state(DownloadState::Pending); - assert!(pending_tasks.len() <= 2); // May have started downloading - } - - // ======================================================================== - // Integration Tests - TaskManager with Mock HTTP Server - // ======================================================================== - - /// Test Scenario 5: Headers and Cookies transmission to downloader - /// Verifies: Custom headers and cookies are correctly sent to server - #[tokio::test] - async fn test_custom_headers_and_cookies_transmission() { - use mockito::Server; - use std::collections::HashMap; - use tempfile::tempdir; - - // Create Mock HTTP server - let mut server = Server::new_async().await; - - // Mock verifies custom headers and cookies - // Note: Cookie order is not guaranteed due to HashMap iteration - let mock = server - .mock("GET", "/protected-file.txt") - .match_header("authorization", "Bearer test-token-123") - .match_header("x-custom-header", "custom-value") - .match_header( - "cookie", - mockito::Matcher::Regex(".*session_id=abc123.*".to_string()), - ) - .match_header( - "cookie", - mockito::Matcher::Regex(".*user_id=456.*".to_string()), - ) - .with_status(200) - .with_body(b"Protected content") - .create(); - - // Prepare custom headers and cookies - let mut headers = HashMap::new(); - headers.insert( - "Authorization".to_string(), - "Bearer test-token-123".to_string(), - ); - headers.insert("X-Custom-Header".to_string(), "custom-value".to_string()); - - let mut cookies = HashMap::new(); - cookies.insert("session_id".to_string(), "abc123".to_string()); - cookies.insert("user_id".to_string(), "456".to_string()); - - // Create task manager - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - // Prepare download destination - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("protected-file.txt"); - let url = format!("{}/protected-file.txt", server.url()); - - // Submit task with headers and cookies - let task_id = manager - .submit_task_with_options( - &url, - dest.to_str().unwrap(), - Some(headers), - Some(cookies), - None, - ) - .unwrap(); - - // Wait for download to complete - tokio::time::sleep(Duration::from_millis(500)).await; - - // Verify task status - let task_info = manager.get_task(&task_id).unwrap(); - assert!( - task_info.state == DownloadState::Completed - || task_info.state == DownloadState::Downloading, - "Task should be completed or downloading" - ); - - // Wait for task to fully complete - for _ in 0..20 { - tokio::time::sleep(Duration::from_millis(100)).await; - let task_info = manager.get_task(&task_id).unwrap(); - if task_info.state == DownloadState::Completed { - break; - } - } - - // Verify mock was called (headers and cookies transmitted correctly) - mock.assert(); - - // Verify file downloaded successfully - assert!(dest.exists(), "File should be downloaded"); - let content = std::fs::read_to_string(&dest).unwrap(); - assert_eq!(content, "Protected content"); - } - - /// Test Scenario 6: Task lifecycle state tracking - /// Verifies: Complete flow from Pending → Downloading → Completed - #[tokio::test] - async fn test_task_lifecycle_and_state_transitions() { - use mockito::Server; - use tempfile::tempdir; - - // Create Mock HTTP server - let mut server = Server::new_async().await; - let test_data = b"Task lifecycle test data"; - - let mock = server - .mock("GET", "/lifecycle-test.txt") - .with_status(200) - .with_header("content-length", &test_data.len().to_string()) - .with_body(test_data.as_slice()) - .create(); - - // Create task manager - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - // Prepare download destination - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("lifecycle-test.txt"); - let url = format!("{}/lifecycle-test.txt", server.url()); - - // Submit task - let task_id = manager.submit_task(&url, dest.to_str().unwrap()).unwrap(); - - // Track state changes - let mut states_observed = Vec::new(); - - // Initial state should be Pending - let initial_task = manager.get_task(&task_id).unwrap(); - states_observed.push(initial_task.state); - assert_eq!( - initial_task.state, - DownloadState::Pending, - "Initial state should be Pending" - ); - - // Monitor state transitions (max 3 seconds) - for _ in 0..30 { - tokio::time::sleep(Duration::from_millis(100)).await; - - if let Ok(task_info) = manager.get_task(&task_id) { - let current_state = task_info.state; - - // Record new state - if states_observed.last() != Some(¤t_state) { - states_observed.push(current_state); - } - - // If completed, verify file exists - if current_state == DownloadState::Completed { - assert!(dest.exists(), "File should exist when completed"); - let content = std::fs::read(&dest).unwrap(); - assert_eq!(content, test_data); - break; - } - - // If failed, record error - if current_state == DownloadState::Failed { - if let Some(error) = &task_info.error { - eprintln!("Download failed: {}", error); - } - panic!("Download should not fail in this test"); - } - } - } - - // Verify state transition sequence - assert!( - states_observed.contains(&DownloadState::Pending), - "Should have observed Pending state" - ); - assert!( - states_observed.contains(&DownloadState::Downloading) - || states_observed.contains(&DownloadState::Completed), - "Should have observed Downloading or Completed state" - ); - - // Final state should be Completed - let final_task = manager.get_task(&task_id).unwrap(); - assert_eq!( - final_task.state, - DownloadState::Completed, - "Final state should be Completed. States observed: {:?}", - states_observed - ); - - // Verify progress information - assert_eq!( - final_task.progress.downloaded_bytes, - test_data.len() as u64, - "Downloaded bytes should match content length" - ); - assert_eq!( - final_task.progress.total_bytes, - Some(test_data.len() as u64), - "Total bytes should be known" - ); - - mock.assert(); - } - - /// Test Scenario 7: Long-polling wait_for_change mechanism - /// Verifies: Correct notification on state change, timeout mechanism works - #[tokio::test] - async fn test_wait_for_change_notification() { - use mockito::Server; - use tempfile::tempdir; - - // Create Mock HTTP server with slow response - let mut server = Server::new_async().await; - let test_data = vec![b'W'; 2048]; // 2KB data - - let mock = server - .mock("GET", "/slow-file.bin") - .with_status(200) - .with_header("content-length", &test_data.len().to_string()) - .with_chunked_body(move |w| { - // Send chunks slowly - for chunk in test_data.chunks(512) { - std::thread::sleep(Duration::from_millis(200)); - w.write_all(chunk)?; - } - Ok(()) - }) - .create(); - - // Create task manager - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - // Prepare download destination - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("slow-file.bin"); - let url = format!("{}/slow-file.bin", server.url()); - - // Submit task - let task_id = manager.submit_task(&url, dest.to_str().unwrap()).unwrap(); - - // Test 1: Wait for state change (should succeed) - let wait_result = manager - .wait_for_change(&task_id, Duration::from_secs(2)) - .await; - assert!(wait_result.is_ok(), "Wait for change should succeed"); - - let task_after_change = wait_result.unwrap(); - assert!( - task_after_change.state == DownloadState::Downloading - || task_after_change.state == DownloadState::Completed, - "State should have changed from Pending" - ); - - // Test 2: Short timeout test (should timeout when state is stable) - // Wait for task to complete or enter stable state - tokio::time::sleep(Duration::from_secs(1)).await; - - // Test 3: Verify task eventually completes - for _ in 0..15 { - tokio::time::sleep(Duration::from_millis(300)).await; - let task_info = manager.get_task(&task_id).unwrap(); - - if task_info.state == DownloadState::Completed { - // Verify download success - assert!(dest.exists(), "File should exist"); - let file_size = std::fs::metadata(&dest).unwrap().len(); - assert_eq!(file_size, 2048, "File size should match"); - break; - } - } - - mock.assert(); - } - - /// Test Scenario 8: Batch task submission and management - /// Verifies: Batch submission, deduplication, concurrency control - #[tokio::test] - async fn test_batch_task_submission_and_management() { - use mockito::Server; - use tempfile::tempdir; - - // Create Mock HTTP server - let mut server = Server::new_async().await; - - // Create 3 different files - let files = vec![ - ("batch1.txt", b"Batch file 1".to_vec()), - ("batch2.txt", b"Batch file 2".to_vec()), - ("batch3.txt", b"Batch file 3".to_vec()), - ]; - - let mut mocks = Vec::new(); - for (filename, content) in &files { - let mock = server - .mock("GET", format!("/{}", filename).as_str()) - .with_status(200) - .with_header("content-length", &content.len().to_string()) - .with_body(content.as_slice()) - .create(); - mocks.push(mock); - } - - // Create task manager - let config = DownloadConfig::default(); - let manager = DownloadTaskManager::from_config(&config); - - // Prepare batch tasks - let temp_dir = tempdir().unwrap(); - let mut tasks = Vec::new(); - - for (filename, _) in &files { - let url = format!("{}/{}", server.url(), filename); - let dest = temp_dir.path().join(filename); - tasks.push((url, dest.to_string_lossy().to_string())); - } - - // Submit batch tasks - let task_ids = manager.submit_batch(tasks).unwrap(); - assert_eq!(task_ids.len(), 3, "Should submit 3 tasks"); - - // Wait for all tasks to complete - for task_id in &task_ids { - for _ in 0..30 { - tokio::time::sleep(Duration::from_millis(100)).await; - - if let Ok(task_info) = manager.get_task(task_id) { - if task_info.state == DownloadState::Completed { - break; - } - if task_info.state == DownloadState::Failed { - panic!("Task {} failed: {:?}", task_id, task_info.error); - } - } - } - } - - // Verify all files downloaded successfully - for (filename, expected_content) in &files { - let dest = temp_dir.path().join(filename); - assert!(dest.exists(), "File {} should exist", filename); - - let content = std::fs::read(&dest).unwrap(); - assert_eq!( - content, *expected_content, - "Content of {} should match", - filename - ); - } - - // Verify all mocks were called - for mock in mocks { - mock.assert(); - } - - // Verify get_all_tasks returns all tasks - let all_tasks = manager.get_all_tasks(); - assert!(all_tasks.len() >= 3, "Should have at least 3 tasks"); - - // Verify get_tasks_by_state - let completed_tasks = manager.get_tasks_by_state(DownloadState::Completed); - assert!( - completed_tasks.len() >= 3, - "Should have at least 3 completed tasks" - ); - } -} diff --git a/src/downloader/traits.rs b/src/downloader/traits.rs deleted file mode 100644 index ba8cdde..0000000 --- a/src/downloader/traits.rs +++ /dev/null @@ -1,262 +0,0 @@ -//! Core trait definitions for the downloader system - -use super::error::Result; -use async_trait::async_trait; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::path::Path; - -/// Progress callback function type -/// Parameters: (downloaded_bytes, total_bytes_optional) -pub type ProgressCallback = Box) + Send + Sync>; - -/// HTTP request options for downloads -#[derive(Debug, Clone, Default)] -pub struct RequestOptions { - /// HTTP headers to include in the request - pub headers: Option>, - /// HTTP cookies to include in the request - pub cookies: Option>, - /// Extra metadata for dispatch (e.g., hub_uuid for routing to external downloaders) - pub metadata: Option>, -} - -/// Downloader capability information -/// -/// This struct defines what features a downloader implementation supports. -/// These capabilities are determined at initialization time and remain constant -/// throughout the downloader's lifetime. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] -pub struct DownloaderCapabilities { - /// Whether the downloader supports pausing downloads - pub supports_pause: bool, - /// Whether the downloader supports resuming paused downloads - pub supports_resume: bool, - /// Whether the downloader supports cancelling downloads - pub supports_cancellation: bool, - /// Whether the downloader supports HTTP Range requests for breakpoint resume - pub supports_range_requests: bool, - /// Whether the downloader supports batch download operations - pub supports_batch_download: bool, -} - -impl DownloaderCapabilities { - /// Create a new DownloaderCapabilities with all features enabled - pub fn all_enabled() -> Self { - Self { - supports_pause: true, - supports_resume: true, - supports_cancellation: true, - supports_range_requests: true, - supports_batch_download: true, - } - } - - /// Create a new DownloaderCapabilities with all features disabled - pub fn all_disabled() -> Self { - Self::default() - } -} - -/// Core downloader trait - all downloader implementations must implement this -/// -/// This trait provides a pluggable interface for different download backends, -/// allowing easy switching between implementations (trauma, reqwest, CLI tools, etc.) -#[async_trait] -pub trait Downloader: Send + Sync { - /// Download a single file from URL to destination path - /// - /// # Arguments - /// * `url` - The URL to download from - /// * `dest` - The destination file path - /// * `progress` - Optional progress callback (downloaded_bytes, total_bytes) - /// * `options` - Optional request options (headers, cookies) - /// - /// # Returns - /// * `Ok(())` - Download completed successfully - /// * `Err(DownloadError)` - Download failed - async fn download( - &self, - url: &str, - dest: &Path, - progress: Option, - options: Option, - ) -> Result<()>; - - /// Download multiple files concurrently - /// - /// # Arguments - /// * `tasks` - Vector of (url, destination_path) tuples - /// - /// # Returns - /// * `Ok(Vec>)` - Vector of results for each download task - async fn download_batch(&self, tasks: Vec<(String, std::path::PathBuf)>) -> Vec>; - - /// Get the name of this downloader implementation - fn name(&self) -> &str; - - /// Get the capabilities of this downloader implementation - /// - /// This method returns a reference to the capability information that was - /// determined at initialization time. Capabilities define what features - /// the downloader supports (pause, resume, cancellation, etc.). - /// - /// # Returns - /// A reference to the DownloaderCapabilities struct - fn capabilities(&self) -> &DownloaderCapabilities; - - /// Cancel an ongoing download (if supported by the implementation) - /// - /// Default implementation does nothing and returns Ok - async fn cancel(&self, _url: &str) -> Result<()> { - Ok(()) - } - - /// Pause an ongoing download (if supported by the implementation) - /// - /// # Arguments - /// * `url` - The URL of the download to pause - /// - /// # Returns - /// * `Ok(())` - Download paused successfully - /// * `Err(DownloadError)` - Failed to pause download - /// - /// Default implementation returns an error - async fn pause(&self, _url: &str) -> Result<()> { - Err(super::error::DownloadError::unsupported( - "Pause not supported by this downloader", - )) - } - - /// Resume a paused download (if supported by the implementation) - /// - /// # Arguments - /// * `url` - The URL of the download to resume - /// - /// # Returns - /// * `Ok(())` - Download resumed successfully - /// * `Err(DownloadError)` - Failed to resume download - /// - /// Default implementation returns an error - async fn resume(&self, _url: &str) -> Result<()> { - Err(super::error::DownloadError::unsupported( - "Resume not supported by this downloader", - )) - } - - /// Check if this downloader supports cancellation - /// - /// # Deprecated - /// Use `capabilities().supports_cancellation` instead. - /// This method will be removed in a future version. - #[deprecated( - since = "0.2.0", - note = "Use capabilities().supports_cancellation instead" - )] - fn supports_cancellation(&self) -> bool { - self.capabilities().supports_cancellation - } - - /// Check if this downloader supports pause/resume - /// - /// # Deprecated - /// Use `capabilities().supports_pause` instead. - /// This method will be removed in a future version. - #[deprecated(since = "0.2.0", note = "Use capabilities().supports_pause instead")] - fn supports_pause(&self) -> bool { - self.capabilities().supports_pause - } - - /// Check if this downloader supports resume/partial downloads - /// - /// # Deprecated - /// Use `capabilities().supports_resume` instead. - /// This method will be removed in a future version. - #[deprecated(since = "0.2.0", note = "Use capabilities().supports_resume instead")] - fn supports_resume(&self) -> bool { - self.capabilities().supports_resume - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::path::PathBuf; - - // Mock downloader for testing - struct MockDownloader { - capabilities: DownloaderCapabilities, - } - - impl MockDownloader { - fn new() -> Self { - Self { - capabilities: DownloaderCapabilities::all_disabled(), - } - } - } - - #[async_trait] - impl Downloader for MockDownloader { - async fn download( - &self, - _url: &str, - _dest: &Path, - _progress: Option, - _options: Option, - ) -> Result<()> { - Ok(()) - } - - async fn download_batch(&self, tasks: Vec<(String, PathBuf)>) -> Vec> { - tasks.into_iter().map(|_| Ok(())).collect() - } - - fn name(&self) -> &str { - "mock" - } - - fn capabilities(&self) -> &DownloaderCapabilities { - &self.capabilities - } - } - - #[tokio::test] - async fn test_mock_downloader() { - let downloader = MockDownloader::new(); - assert_eq!(downloader.name(), "mock"); - - // Test new capabilities() method - let caps = downloader.capabilities(); - assert!(!caps.supports_cancellation); - assert!(!caps.supports_pause); - assert!(!caps.supports_resume); - assert!(!caps.supports_range_requests); - assert!(!caps.supports_batch_download); - - // Test deprecated methods (should still work) - #[allow(deprecated)] - { - assert!(!downloader.supports_cancellation()); - assert!(!downloader.supports_pause()); - assert!(!downloader.supports_resume()); - } - - let result = downloader - .download( - "http://example.com/file", - Path::new("/tmp/file"), - None, - None, - ) - .await; - assert!(result.is_ok()); - - // Test default pause/resume implementations - let pause_result = downloader.pause("http://example.com/file").await; - assert!(pause_result.is_err()); - - let resume_result = downloader.resume("http://example.com/file").await; - assert!(resume_result.is_err()); - } -} diff --git a/src/downloader/trauma_impl.rs b/src/downloader/trauma_impl.rs deleted file mode 100644 index c350617..0000000 --- a/src/downloader/trauma_impl.rs +++ /dev/null @@ -1,751 +0,0 @@ -//! Reqwest-based downloader implementation with pause/resume support - -use super::error::{DownloadError, Result}; -use super::traits::{Downloader, DownloaderCapabilities, ProgressCallback, RequestOptions}; -use async_trait::async_trait; -use futures::StreamExt; -use parking_lot::RwLock; -use std::collections::HashMap; -use std::path::{Path, PathBuf}; -use std::sync::Arc; -use tokio::fs::{File, OpenOptions}; -use tokio::io::AsyncWriteExt; -use tokio_util::sync::CancellationToken; - -/// Task state for managing pause/resume -#[derive(Clone)] -struct TaskState { - url: String, - dest: PathBuf, - cancel_token: CancellationToken, - supports_range: Option, -} - -/// Reqwest-based downloader implementation -/// -/// This implementation uses reqwest with rustls for secure, concurrent downloads -/// and supports pause/resume with HTTP Range requests -pub struct TraumaDownloader { - max_concurrent: usize, - retries: usize, - timeout_seconds: u64, - client: Arc, - // Track active download tasks for pause/resume - tasks: Arc>>, - // Capabilities of this downloader - capabilities: DownloaderCapabilities, -} - -impl TraumaDownloader { - /// Create a new TraumaDownloader with specified parameters - /// - /// # Arguments - /// * `max_concurrent` - Maximum number of concurrent downloads - /// * `retries` - Number of retry attempts for failed downloads - /// * `timeout_seconds` - Timeout for each download in seconds - pub fn new(max_concurrent: usize, retries: usize, timeout_seconds: u64) -> Self { - let client = reqwest::Client::builder() - .timeout(std::time::Duration::from_secs(timeout_seconds)) - .build() - .expect("Failed to create HTTP client"); - - Self { - max_concurrent, - retries, - timeout_seconds, - client: Arc::new(client), - tasks: Arc::new(RwLock::new(HashMap::new())), - capabilities: DownloaderCapabilities::all_enabled(), - } - } - - /// Create a downloader with default settings - pub fn default_settings() -> Self { - Self::new(4, 3, 300) - } - - /// Test if a server supports HTTP Range requests - async fn test_range_support(client: &reqwest::Client, url: &str) -> bool { - // Try HEAD request first - if let Ok(response) = client.head(url).send().await { - if let Some(accept_ranges) = response.headers().get("accept-ranges") { - if let Ok(value) = accept_ranges.to_str() { - return value.to_lowercase() == "bytes"; - } - } - } - - // Fallback: try a fake range request - if let Ok(response) = client.get(url).header("Range", "bytes=0-0").send().await { - return response.status() == reqwest::StatusCode::PARTIAL_CONTENT - || response.status() == reqwest::StatusCode::OK; - } - - false - } - - /// Download a file with retry and pause support - async fn download_with_retry( - &self, - url: &str, - dest: &Path, - progress: Option<&ProgressCallback>, - cancel_token: &CancellationToken, - options: Option<&RequestOptions>, - ) -> Result<()> { - let mut last_error = None; - - for attempt in 0..=self.retries { - if cancel_token.is_cancelled() { - return Err(DownloadError::cancelled("Download was paused")); - } - - // For retries, we don't pass progress callback to avoid multiple progress reports - let current_progress = if attempt == 0 { progress } else { None }; - - match self - .download_once(url, dest, current_progress, cancel_token, options) - .await - { - Ok(()) => return Ok(()), - Err(e) => { - if cancel_token.is_cancelled() { - return Err(DownloadError::cancelled("Download was paused")); - } - last_error = Some(e); - if attempt < self.retries { - tokio::time::sleep(std::time::Duration::from_secs(1 << attempt)).await; - } - } - } - } - - Err(last_error.unwrap_or_else(|| DownloadError::network("Download failed".to_string()))) - } - - /// Download a file once (no retry), with pause support - async fn download_once( - &self, - url: &str, - dest: &Path, - progress: Option<&ProgressCallback>, - cancel_token: &CancellationToken, - options: Option<&RequestOptions>, - ) -> Result<()> { - // Check if temp file exists (resume case) - let temp_dest = dest.with_extension("tmp"); - let existing_size = if temp_dest.exists() { - tokio::fs::metadata(&temp_dest) - .await - .ok() - .map(|m| m.len()) - .unwrap_or(0) - } else { - 0 - }; - - // Test range support if not already known - let supports_range = if existing_size > 0 { - Self::test_range_support(&self.client, url).await - } else { - false - }; - - // Build request with Range header if resuming - let mut request = self.client.get(url); - if existing_size > 0 && supports_range { - request = request.header("Range", format!("bytes={}-", existing_size)); - } - - // Apply custom headers if provided - if let Some(opts) = options { - if let Some(headers) = &opts.headers { - for (key, value) in headers { - request = request.header(key, value); - } - } - // Apply cookies if provided - if let Some(cookies) = &opts.cookies { - let cookie_string = cookies - .iter() - .map(|(k, v)| format!("{}={}", k, v)) - .collect::>() - .join("; "); - if !cookie_string.is_empty() { - request = request.header("Cookie", cookie_string); - } - } - } - - // Send request - let response = request - .send() - .await - .map_err(|e| DownloadError::network(format!("Failed to send request: {}", e)))?; - - // Check status - let status = response.status(); - if !status.is_success() && status != reqwest::StatusCode::PARTIAL_CONTENT { - return Err(DownloadError::network(format!( - "HTTP error {}: {}", - status, url - ))); - } - - // Get content length - let content_length = response.content_length(); - let total_size = if status == reqwest::StatusCode::PARTIAL_CONTENT { - // For partial content, add existing size to content length - content_length.map(|len| len + existing_size) - } else { - content_length - }; - - // Open file (append mode if resuming, create mode otherwise) - let mut file = if existing_size > 0 && status == reqwest::StatusCode::PARTIAL_CONTENT { - OpenOptions::new() - .append(true) - .open(&temp_dest) - .await - .map_err(|e| DownloadError::file_system(format!("Failed to open file: {}", e)))? - } else { - File::create(&temp_dest) - .await - .map_err(|e| DownloadError::file_system(format!("Failed to create file: {}", e)))? - }; - - // Stream download with progress tracking - let mut stream = response.bytes_stream(); - let mut downloaded: u64 = existing_size; - - while let Some(chunk_result) = stream.next().await { - // Check for pause signal - if cancel_token.is_cancelled() { - // Flush and close file before pausing - file.flush().await.ok(); - drop(file); - return Err(DownloadError::cancelled("Download paused")); - } - - let chunk = chunk_result - .map_err(|e| DownloadError::network(format!("Failed to read chunk: {}", e)))?; - - file.write_all(&chunk) - .await - .map_err(|e| DownloadError::file_system(format!("Failed to write chunk: {}", e)))?; - - downloaded += chunk.len() as u64; - - // Call progress callback - if let Some(callback) = progress { - callback(downloaded, total_size); - } - } - - // Flush and close file - file.flush() - .await - .map_err(|e| DownloadError::file_system(format!("Failed to flush file: {}", e)))?; - drop(file); - - // Rename temp file to final destination - tokio::fs::rename(&temp_dest, dest).await.map_err(|e| { - DownloadError::file_system(format!("Failed to rename downloaded file: {}", e)) - })?; - - Ok(()) - } - - /// Register a download task - fn register_task(&self, url: String, dest: PathBuf) -> CancellationToken { - let cancel_token = CancellationToken::new(); - let state = TaskState { - url: url.clone(), - dest, - cancel_token: cancel_token.clone(), - supports_range: None, - }; - self.tasks.write().insert(url, state); - cancel_token - } - - /// Unregister a download task - fn unregister_task(&self, url: &str) { - self.tasks.write().remove(url); - } - - /// Get task cancel token - fn get_cancel_token(&self, url: &str) -> Option { - self.tasks - .read() - .get(url) - .map(|state| state.cancel_token.clone()) - } -} - -#[async_trait] -impl Downloader for TraumaDownloader { - async fn download( - &self, - url: &str, - dest: &Path, - progress: Option, - options: Option, - ) -> Result<()> { - // Validate inputs - if url.is_empty() { - return Err(DownloadError::invalid_input("URL cannot be empty")); - } - - // Ensure parent directory exists - if let Some(parent) = dest.parent() { - tokio::fs::create_dir_all(parent).await.map_err(|e| { - DownloadError::file_system(format!("Failed to create directory: {}", e)) - })?; - } - - // Register task - let cancel_token = self.register_task(url.to_string(), dest.to_path_buf()); - - // Start download - let result = self - .download_with_retry( - url, - dest, - progress.as_ref(), - &cancel_token, - options.as_ref(), - ) - .await; - - // Unregister task - self.unregister_task(url); - - result - } - - async fn download_batch(&self, tasks: Vec<(String, PathBuf)>) -> Vec> { - if tasks.is_empty() { - return vec![]; - } - - // Use semaphore to limit concurrent downloads - let semaphore = Arc::new(tokio::sync::Semaphore::new(self.max_concurrent)); - let mut handles = vec![]; - - for (url, dest) in tasks { - let sem = semaphore.clone(); - let cancel_token = self.register_task(url.clone(), dest.clone()); - let downloader = self.clone_for_task(); - let url_for_handle = url.clone(); - - let handle = tokio::spawn(async move { - let _permit = sem.acquire().await.unwrap(); - - // Ensure parent directory exists - if let Some(parent) = dest.parent() { - if let Err(e) = tokio::fs::create_dir_all(parent).await { - return Err(DownloadError::file_system(format!( - "Failed to create directory: {}", - e - ))); - } - } - - // Download with retry - let result = downloader - .download_with_retry(&url, &dest, None, &cancel_token, None) - .await; - - result - }); - - handles.push((url_for_handle, handle)); - } - - // Wait for all downloads to complete - let mut results = vec![]; - for (url, handle) in handles { - match handle.await { - Ok(result) => { - self.unregister_task(&url); - results.push(result); - } - Err(e) => { - self.unregister_task(&url); - results.push(Err(DownloadError::network(format!( - "Task join error: {}", - e - )))); - } - } - } - - results - } - - fn name(&self) -> &str { - "reqwest" - } - - fn capabilities(&self) -> &DownloaderCapabilities { - &self.capabilities - } - - async fn pause(&self, url: &str) -> Result<()> { - if let Some(cancel_token) = self.get_cancel_token(url) { - cancel_token.cancel(); - Ok(()) - } else { - Err(DownloadError::task_not_found(url)) - } - } - - async fn resume(&self, url: &str) -> Result<()> { - // Get task state - let state = self.tasks.read().get(url).cloned(); - - if let Some(task_state) = state { - // Create new cancel token for resumed download - let new_cancel_token = - self.register_task(task_state.url.clone(), task_state.dest.clone()); - - // Start download from where it left off - let result = self - .download_with_retry( - &task_state.url, - &task_state.dest, - None, - &new_cancel_token, - None, - ) - .await; - - if result.is_err() { - self.unregister_task(url); - } - - result - } else { - Err(DownloadError::task_not_found(url)) - } - } -} - -impl TraumaDownloader { - /// Clone essential fields for spawned tasks - fn clone_for_task(&self) -> Self { - Self { - max_concurrent: self.max_concurrent, - retries: self.retries, - timeout_seconds: self.timeout_seconds, - client: self.client.clone(), - tasks: self.tasks.clone(), - capabilities: self.capabilities.clone(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use tempfile::tempdir; - - #[test] - fn test_create_trauma_downloader() { - let downloader = TraumaDownloader::new(8, 5, 600); - assert_eq!(downloader.name(), "reqwest"); - assert_eq!(downloader.max_concurrent, 8); - assert_eq!(downloader.retries, 5); - - // Test capabilities - let caps = downloader.capabilities(); - assert!(caps.supports_pause); - assert!(caps.supports_resume); - assert!(caps.supports_cancellation); - } - - #[tokio::test] - async fn test_download_invalid_url() { - let downloader = TraumaDownloader::default_settings(); - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("test.txt"); - - let result = downloader.download("", &dest, None, None).await; - assert!(result.is_err()); - - if let Err(e) = result { - assert_eq!(e.kind, super::super::error::ErrorKind::InvalidInput); - } - } - - #[tokio::test] - async fn test_download_batch_empty() { - let downloader = TraumaDownloader::default_settings(); - let results = downloader.download_batch(vec![]).await; - assert!(results.is_empty()); - } - - #[tokio::test] - async fn test_pause_nonexistent_task() { - let downloader = TraumaDownloader::default_settings(); - let result = downloader.pause("http://nonexistent.com/file").await; - assert!(result.is_err()); - } - - // ======================================================================== - // Integration Tests with Mock HTTP Server - // ======================================================================== - - /// Test Scenario 1: Complete file download flow - /// Verifies: File downloaded correctly, content matches, temp file cleanup - #[tokio::test] - async fn test_download_complete_file() { - use mockito::Server; - - // Create Mock HTTP server - let mut server = Server::new_async().await; - let test_data = b"Hello, this is test data for download!"; - - let mock = server - .mock("GET", "/test-file.txt") - .with_status(200) - .with_header("content-length", &test_data.len().to_string()) - .with_body(test_data.as_slice()) - .create(); - - // Prepare download destination - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("downloaded.txt"); - - // Execute download - let downloader = TraumaDownloader::default_settings(); - let url = format!("{}/test-file.txt", server.url()); - let result = downloader.download(&url, &dest, None, None).await; - - // Verify results - assert!(result.is_ok(), "Download should succeed"); - assert!(dest.exists(), "Downloaded file should exist"); - - let downloaded_content = std::fs::read(&dest).unwrap(); - assert_eq!(downloaded_content, test_data, "Content should match"); - - // Verify temporary file was cleaned up - let temp_file = dest.with_extension("tmp"); - assert!(!temp_file.exists(), "Temporary file should be cleaned up"); - - mock.assert(); - } - - /// Test Scenario 2: Concurrent downloads of multiple files - /// Verifies: Parallel execution, concurrency control, all files downloaded - #[tokio::test] - async fn test_concurrent_downloads() { - use mockito::Server; - - // Create Mock HTTP server - let mut server = Server::new_async().await; - - // Prepare 5 different test files - let test_files = vec![ - ("file1.txt", b"Content of file 1".to_vec()), - ("file2.txt", b"Content of file 2".to_vec()), - ("file3.txt", b"Content of file 3".to_vec()), - ("file4.txt", b"Content of file 4".to_vec()), - ("file5.txt", b"Content of file 5".to_vec()), - ]; - - // Create mock for each file - let mut mocks = Vec::new(); - for (filename, content) in &test_files { - let mock = server - .mock("GET", format!("/{}", filename).as_str()) - .with_status(200) - .with_header("content-length", &content.len().to_string()) - .with_body(content.as_slice()) - .create(); - mocks.push(mock); - } - - // Prepare download tasks - let temp_dir = tempdir().unwrap(); - let mut tasks = Vec::new(); - - for (filename, _) in &test_files { - let url = format!("{}/{}", server.url(), filename); - let dest = temp_dir.path().join(filename); - tasks.push((url, dest)); - } - - // Execute concurrent downloads (max 3 concurrent) - let downloader = TraumaDownloader::new(3, 2, 30); - let results = downloader.download_batch(tasks.clone()).await; - - // Verify all downloads succeeded - assert_eq!(results.len(), 5, "Should have 5 results"); - for (i, result) in results.iter().enumerate() { - assert!(result.is_ok(), "Download {} should succeed", i); - } - - // Verify file contents - for (i, (filename, expected_content)) in test_files.iter().enumerate() { - let dest = temp_dir.path().join(filename); - assert!(dest.exists(), "File {} should exist", filename); - - let content = std::fs::read(&dest).unwrap(); - assert_eq!( - content, *expected_content, - "Content of file {} should match", - i - ); - } - - // Verify all mocks were called - for mock in mocks { - mock.assert(); - } - } - - /// Test Scenario 3: Pause and resume during download - /// Verifies: Pause mechanism, temp file retention, successful continuation - #[tokio::test] - async fn test_pause_and_resume_download() { - use mockito::Server; - use std::time::Duration; - - // Create Mock HTTP server - simulate large file with chunked response - let mut server = Server::new_async().await; - let test_data = vec![b'X'; 10 * 1024]; // 10KB data - - let mock = server - .mock("GET", "/large-file.bin") - .with_status(200) - .with_header("content-length", &test_data.len().to_string()) - .with_chunked_body(move |w| { - // Simulate slow download to allow time for pause - for chunk in test_data.chunks(1024) { - std::thread::sleep(Duration::from_millis(50)); - w.write_all(chunk)?; - } - Ok(()) - }) - .create(); - - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("large-file.bin"); - let url = format!("{}/large-file.bin", server.url()); - - let downloader = TraumaDownloader::default_settings(); - - // Start download and pause quickly - let url_clone = url.clone(); - let dest_clone = dest.clone(); - let downloader_clone = downloader.clone_for_task(); - - let download_handle = tokio::spawn(async move { - downloader_clone - .download(&url_clone, &dest_clone, None, None) - .await - }); - - // Wait for download to start - tokio::time::sleep(Duration::from_millis(100)).await; - - // Pause download - let pause_result = downloader.pause(&url).await; - assert!(pause_result.is_ok(), "Pause should succeed"); - - // Wait for download task to respond to pause signal - let download_result = download_handle.await.unwrap(); - assert!(download_result.is_err(), "Download should be cancelled"); - - // Verify temporary file exists (partially downloaded data) - let temp_file = dest.with_extension("tmp"); - assert!( - temp_file.exists(), - "Temporary file should exist after pause" - ); - - mock.assert(); - } - - /// Test Scenario 4: Resume from breakpoint - /// Verifies: Range request, resume from breakpoint, complete file download - #[tokio::test] - async fn test_resume_from_breakpoint() { - use mockito::Server; - - let temp_dir = tempdir().unwrap(); - let dest = temp_dir.path().join("resume-test.bin"); - let temp_file = dest.with_extension("tmp"); - - // Prepare complete test data - let full_data = vec![b'A'; 5000]; // 5KB - let partial_size = 2000; // First 2KB already downloaded - - // Simulate partially downloaded data - std::fs::write(&temp_file, &full_data[..partial_size]).unwrap(); - assert_eq!( - std::fs::metadata(&temp_file).unwrap().len(), - partial_size as u64, - "Partial file should exist" - ); - - // Create Mock HTTP server with Range support - let mut server = Server::new_async().await; - - // First Mock: HEAD request to detect Range support - let _head_mock = server - .mock("HEAD", "/resume-file.bin") - .with_status(200) - .with_header("accept-ranges", "bytes") - .with_header("content-length", &full_data.len().to_string()) - .create(); - - // Second Mock: GET request with Range support - let range_mock = server - .mock("GET", "/resume-file.bin") - .match_header("range", format!("bytes={}-", partial_size).as_str()) - .with_status(206) // Partial Content - .with_header( - "content-range", - format!( - "bytes {}-{}/{}", - partial_size, - full_data.len() - 1, - full_data.len() - ) - .as_str(), - ) - .with_header( - "content-length", - &(full_data.len() - partial_size).to_string(), - ) - .with_body(&full_data[partial_size..]) - .create(); - - // Execute resume download - let downloader = TraumaDownloader::default_settings(); - let url = format!("{}/resume-file.bin", server.url()); - let result = downloader.download(&url, &dest, None, None).await; - - // Verify download succeeded - assert!(result.is_ok(), "Resume download should succeed"); - assert!(dest.exists(), "Final file should exist"); - assert!(!temp_file.exists(), "Temporary file should be removed"); - - // Verify complete file content - let downloaded_data = std::fs::read(&dest).unwrap(); - assert_eq!( - downloaded_data.len(), - full_data.len(), - "File size should match" - ); - assert_eq!( - downloaded_data, full_data, - "Content should match completely" - ); - - range_mock.assert(); - } -} diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 313146e..0000000 --- a/src/error.rs +++ /dev/null @@ -1,82 +0,0 @@ -use std::result; - -#[derive(Debug)] -pub enum Error { - Io(std::io::Error), - Other(String), - Getter(GetterError), -} - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Error::Io(e) => write!(f, "IO error: {e}"), - Error::Other(msg) => write!(f, "{msg}"), - Error::Getter(e) => write!(f, "{e}"), - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::Io(e) => Some(e), - Error::Getter(e) => e.source(), - Error::Other(_) => None, - } - } -} - -impl From for Error { - fn from(e: std::io::Error) -> Self { - Error::Io(e) - } -} - -impl From for Error { - fn from(e: GetterError) -> Self { - Error::Getter(e) - } -} - -pub type Result = result::Result; - -#[derive(Debug)] -pub struct GetterError { - pub tag: String, - pub message: String, - pub err: Option>, -} - -impl GetterError { - pub fn new_nobase(tag: &str, message: &str) -> Self { - Self { - tag: tag.to_string(), - message: message.to_string(), - err: None, - } - } - - pub fn new(tag: &str, message: &str, err: Box) -> Self { - Self { - tag: tag.to_string(), - message: message.to_string(), - err: Some(err), - } - } -} - -impl std::fmt::Display for GetterError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}: {}", self.tag, self.message) - } -} - -impl std::error::Error for GetterError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match &self.err { - Some(err) => Some(err.as_ref()), - None => None, - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 4c1491e..4ffc0b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,64 @@ -mod cache; -mod core; -pub mod database; -pub mod downloader; -mod error; -mod locale; -pub mod manager; -pub mod rpc; -mod utils; -mod websdk; - -// rustls-platform-verifier -#[cfg(feature = "rustls-platform-verifier-android")] +//! Root getter library facade for the UpgradeAll rewrite. +//! +//! The root crate is intentionally small during the rewrite. Product/domain +//! behavior lives in split crates such as `getter-core` and `getter-storage`; +//! Android/Flutter hosts embed this crate through a stable facade. + +pub use getter_core as core; +pub use getter_storage as storage; + +#[cfg(feature = "rustls-platform-verifier")] pub use rustls_platform_verifier; + +pub mod rpc { + pub mod server { + use std::future::pending; + + /// Error returned by the temporary in-process RPC server facade. + #[derive(Debug, thiserror::Error)] + pub enum RpcServerError { + #[error("failed to bind RPC server at {address}: {source}")] + Bind { + address: String, + #[source] + source: std::io::Error, + }, + #[error("failed to read local RPC server address: {0}")] + LocalAddress(#[source] std::io::Error), + #[error("RPC server startup callback failed")] + StartupCallback, + } + + /// Start a placeholder local RPC endpoint and keep it alive. + /// + /// This preserves the Android `api_proxy` dependency target while the + /// full getter RPC surface is rewritten. The function binds a real local + /// TCP listener so callers receive a concrete URL, then parks forever. + pub async fn run_server_hanging( + address: &str, + on_listening: F, + ) -> Result<(), RpcServerError> + where + F: FnOnce(&str) -> Result<(), RpcServerError> + Send + 'static, + { + let listener = tokio::net::TcpListener::bind(address) + .await + .map_err(|source| RpcServerError::Bind { + address: address.to_owned(), + source, + })?; + let local_addr = listener + .local_addr() + .map_err(RpcServerError::LocalAddress)?; + let url = format!("ws://{local_addr}"); + on_listening(&url)?; + + // Keep the listener alive until the host process stops. The full + // JSON-RPC implementation is added in a later behavior slice. + let _listener = listener; + pending::<()>().await; + #[allow(unreachable_code)] + Ok(()) + } + } +} diff --git a/src/locale.rs b/src/locale.rs deleted file mode 100644 index 82b257f..0000000 --- a/src/locale.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::env; -use std::io::{self, ErrorKind}; -use std::path::PathBuf; - -pub struct DataDir { - pub cache_dir: PathBuf, - pub data_dir: PathBuf, -} - -#[cfg(all( - target_family = "unix", - not(target_os = "macos"), - not(target_os = "android") -))] -pub fn all_dir() -> Result { - let home_dir = env::var("HOME") - .map_err(|_| io::Error::new(ErrorKind::NotFound, "HOME not found")) - .map(PathBuf::from)?; - let cache_dir = home_dir.join(".cache/upa/"); - let data_dir = home_dir.join(".local/share/upa/"); - Ok(DataDir { - cache_dir, - data_dir, - }) -} - -#[cfg(target_os = "macos")] -pub fn all_dir() -> Result { - let home_dir = env::var("HOME") - .map_err(|_| io::Error::new(ErrorKind::NotFound, "HOME not found")) - .map(|home| PathBuf::from(home))?; - let cache_dir = home_dir.join("Library/Caches/upa/"); - let data_dir = home_dir.join("Library/Application Support/upa/"); - Ok(DataDir { - cache_dir, - data_dir, - }) -} - -#[cfg(target_family = "windows")] -pub fn all_dir() -> Result { - let home_dir = env::var("APPDATA") - .map_err(|_| io::Error::new(ErrorKind::NotFound, "APPDATA not found")) - .map(|home| PathBuf::from(home))?; - let cache_dir = home_dir.join("upa/cache/"); - let data_dir = home_dir.join("upa/data/"); - Ok(DataDir { - cache_dir, - data_dir, - }) -} - -#[cfg(target_os = "android")] -pub fn all_dir() -> Result { - let home_dir = env::var("HOME") - .map_err(|_| io::Error::new(ErrorKind::NotFound, "HOME not found")) - .map(|home| PathBuf::from(home))?; - let cache_dir = home_dir.join(".upa/cache/"); - let data_dir = home_dir.join(".upa/data/"); - Ok(DataDir { - cache_dir, - data_dir, - }) -} diff --git a/src/main.rs b/src/main.rs index e7a11a9..c9bfeea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,17 @@ +#[cfg(feature = "cli")] fn main() { - println!("Hello, world!"); + let output = getter_cli::run(std::env::args()); + if !output.stdout.is_empty() { + print!("{}", output.stdout); + } + if !output.stderr.is_empty() { + eprint!("{}", output.stderr); + } + std::process::exit(output.exit_code.code()); +} + +#[cfg(not(feature = "cli"))] +fn main() { + eprintln!("getter binary requires the 'cli' feature"); + std::process::exit(2); } diff --git a/src/manager/android_api.rs b/src/manager/android_api.rs deleted file mode 100644 index 5e39948..0000000 --- a/src/manager/android_api.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::collections::HashMap; - -use jsonrpsee::core::client::ClientT; -use jsonrpsee::http_client::HttpClientBuilder; -use jsonrpsee::rpc_params; -use once_cell::sync::OnceCell; -use serde::{Deserialize, Serialize}; - -use crate::database::models::app::AppRecord; - -/// Global Android API client, registered via `register_android_api` RPC. -static ANDROID_API: OnceCell = OnceCell::new(); - -pub fn set_android_api(url: String) { - let _ = ANDROID_API.set(AndroidApi { url }); -} - -pub fn get_android_api() -> Option<&'static AndroidApi> { - ANDROID_API.get() -} - -/// Rust-side HTTP JSON-RPC client that calls back into the Kotlin -/// KotlinHubRpcServer for Android-specific functionality. -/// -/// Kotlin exposes `get_local_version` and `get_installed_apps` methods -/// on the same Ktor HTTP server that handles hub provider calls. -pub struct AndroidApi { - url: String, -} - -#[derive(Serialize, Deserialize, Debug)] -struct GetLocalVersionParams { - app_id: HashMap>, -} - -#[derive(Serialize, Deserialize, Debug)] -struct GetInstalledAppsParams { - ignore_system: bool, -} - -impl AndroidApi { - /// Query Kotlin for the locally-installed version of an app. - /// - /// Calls `get_local_version({app_id})` on the Kotlin Ktor server. - /// Returns `None` if the app is not installed or the call fails. - pub async fn get_local_version( - &self, - app_id: &HashMap>, - ) -> Option { - let client = HttpClientBuilder::default().build(&self.url).ok()?; - let params = rpc_params!(GetLocalVersionParams { - app_id: app_id.clone(), - }); - client - .request::, _>("get_local_version", params) - .await - .unwrap_or(None) - } - - /// Query Kotlin for all installed Android apps and Magisk modules. - /// - /// Calls `get_installed_apps({ignore_system})` on the Kotlin Ktor server. - /// Returns an empty list if the call fails. - pub async fn get_installed_apps(&self, ignore_system: bool) -> Vec { - let client = match HttpClientBuilder::default().build(&self.url) { - Ok(c) => c, - Err(_) => return vec![], - }; - let params = rpc_params!(GetInstalledAppsParams { ignore_system }); - client - .request::, _>("get_installed_apps", params) - .await - .unwrap_or_default() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_get_local_version_params_serialization() { - let app_id: HashMap> = HashMap::from([( - "android_app_package".to_string(), - Some("com.example.app".to_string()), - )]); - let params = GetLocalVersionParams { - app_id: app_id.clone(), - }; - let json = serde_json::to_string(¶ms).unwrap(); - assert!(json.contains("android_app_package")); - assert!(json.contains("com.example.app")); - } - - #[test] - fn test_get_installed_apps_params_serialization() { - let params = GetInstalledAppsParams { - ignore_system: true, - }; - let json = serde_json::to_string(¶ms).unwrap(); - assert!(json.contains("ignore_system")); - assert!(json.contains("true")); - } -} diff --git a/src/manager/app_manager.rs b/src/manager/app_manager.rs deleted file mode 100644 index 12b0b32..0000000 --- a/src/manager/app_manager.rs +++ /dev/null @@ -1,462 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; -use tokio::sync::{RwLock, Semaphore}; - -use super::android_api; -use super::app_status::AppStatus; -use super::data_getter::DataGetter; -use super::notification::{notify_if_registered, ManagerEvent}; -use super::updater::get_release_status; -use super::version_map::VersionMap; -use crate::database::get_db; -use crate::database::models::app::AppRecord; -use crate::error::Result; - -pub type UpdateCallback = Arc; - -/// Manages all tracked apps and their version data. -/// -/// Mirrors Kotlin's `AppManager`. -pub struct AppManager { - /// Saved apps from the database (keyed by record id) - apps: Arc>>, - /// Virtual apps from Android installed packages (not persisted) - virtual_apps: Arc>>, - /// In-memory version maps keyed by app record id - version_maps: Arc>>, - data_getter: Arc, -} - -impl AppManager { - pub fn load() -> Result { - let records = get_db().load_apps()?; - let apps = records.into_iter().map(|a| (a.id.clone(), a)).collect(); - Ok(Self { - apps: Arc::new(RwLock::new(apps)), - virtual_apps: Arc::new(RwLock::new(vec![])), - version_maps: Arc::new(RwLock::new(HashMap::new())), - data_getter: Arc::new(DataGetter::new()), - }) - } - - /// Replace the virtual app list (installed Android packages). - pub async fn set_virtual_apps(&self, apps: Vec) { - *self.virtual_apps.write().await = apps; - } - - pub async fn get_all_apps(&self) -> Vec { - let mut result: Vec = self.apps.read().await.values().cloned().collect(); - result.extend(self.virtual_apps.read().await.clone()); - result - } - - pub async fn get_saved_apps(&self) -> Vec { - self.apps.read().await.values().cloned().collect() - } - - pub async fn find_app_by_id( - &self, - app_id: &HashMap>, - ) -> Option { - self.apps - .read() - .await - .values() - .find(|a| &a.app_id == app_id) - .cloned() - } - - pub async fn get_app(&self, record_id: &str) -> Option { - self.apps.read().await.get(record_id).cloned() - } - - /// Persist (insert or update) an app record. - pub async fn save_app(&self, mut record: AppRecord) -> Result { - if record.id.is_empty() { - record.id = uuid::Uuid::new_v4().to_string(); - } - get_db().upsert_app(&record)?; - self.apps - .write() - .await - .insert(record.id.clone(), record.clone()); - Ok(record) - } - - /// Remove a saved app. - pub async fn remove_app(&self, record_id: &str) -> Result { - let deleted = get_db().delete_app(record_id)?; - self.apps.write().await.remove(record_id); - self.version_maps.write().await.remove(record_id); - Ok(deleted) - } - - /// Return the current AppStatus for an app. - /// - /// Queries Kotlin for the locally-installed version via `AndroidApi.get_local_version()` - /// if a callback URL has been registered; otherwise local_version is `None`. - pub async fn get_app_status(&self, record_id: &str) -> AppStatus { - let app = match self.apps.read().await.get(record_id).cloned() { - Some(a) => a, - None => return AppStatus::AppInactive, - }; - - // Query local version from Android via registered callback - let local_version: Option = match android_api::get_android_api() { - Some(api) => api.get_local_version(&app.app_id).await, - None => None, - }; - - let mut maps = self.version_maps.write().await; - let vm = maps.entry(record_id.to_string()).or_insert_with(|| { - VersionMap::new( - app.invalid_version_number_field_regex.clone(), - app.include_version_number_field_regex.clone(), - ) - }); - get_release_status( - vm, - local_version.as_deref(), - app.ignore_version_number.as_deref(), - true, - ) - } - - /// Refresh version data for all saved apps. - /// - /// Uses a semaphore (max 10 concurrent hub requests) matching Kotlin's logic. - /// Apps that have version-filter regexes (`need_complete_version`) skip the batch - /// API entirely and go straight to the full release-list path, mirroring Kotlin's - /// `simpleMap` / `completeMap` split in `AppManager.renewAppList()`. - /// Fires `RenewProgress` notifications to Kotlin UI as each app completes. - pub async fn renew_all( - &self, - hubs: &[crate::database::models::hub::HubRecord], - progress_cb: Option, - ) { - let apps = self.get_saved_apps().await; - let total = apps.len(); - let semaphore = Arc::new(Semaphore::new(10)); - - // Group apps by hub, splitting into simple (batch-eligible) vs complete-only. - let mut hub_simple_map: HashMap> = HashMap::new(); - let mut hub_complete_map: HashMap> = HashMap::new(); - for app in &apps { - let sorted_hubs = app.get_sorted_hub_uuids(); - let effective_hubs: Vec<&str> = if sorted_hubs.is_empty() { - hubs.iter().map(|h| h.uuid.as_str()).collect() - } else { - sorted_hubs.iter().map(String::as_str).collect() - }; - let dest = if need_complete_version(app) { - &mut hub_complete_map - } else { - &mut hub_simple_map - }; - for hub_uuid in effective_hubs { - dest.entry(hub_uuid.to_string()) - .or_default() - .push(app.clone()); - } - } - - let completed = Arc::new(std::sync::atomic::AtomicUsize::new(0)); - let mut handles = vec![]; - - for hub in hubs { - let simple_apps = hub_simple_map.get(&hub.uuid).cloned().unwrap_or_default(); - let complete_apps = hub_complete_map.get(&hub.uuid).cloned().unwrap_or_default(); - if simple_apps.is_empty() && complete_apps.is_empty() { - continue; - } - - let hub = hub.clone(); - let getter = self.data_getter.clone(); - let version_maps = self.version_maps.clone(); - let sem = semaphore.clone(); - let completed = completed.clone(); - let cb = progress_cb.clone(); - - let handle = tokio::spawn(async move { - let _permit = sem.acquire().await.unwrap(); - - // --- Batch path (simple apps only) --- - let mut need_full: Vec = complete_apps; // complete-only apps go straight here - if !simple_apps.is_empty() { - let app_ids: Vec>> = - simple_apps.iter().map(|a| a.app_id.clone()).collect(); - let latest_results = getter.get_latest_releases(&hub, &app_ids).await; - - for (app, (_, maybe_release)) in simple_apps.iter().zip(latest_results.iter()) { - if let Some(release) = maybe_release { - let new_status = { - let mut maps = version_maps.write().await; - let vm = maps.entry(app.id.clone()).or_insert_with(|| { - VersionMap::new( - app.invalid_version_number_field_regex.clone(), - app.include_version_number_field_regex.clone(), - ) - }); - vm.add_single_release(&hub.uuid, release.clone()); - let local_version: Option = - match android_api::get_android_api() { - Some(api) => api.get_local_version(&app.app_id).await, - None => None, - }; - get_release_status( - vm, - local_version.as_deref(), - app.ignore_version_number.as_deref(), - true, - ) - }; - let done = - completed.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1; - if let Some(ref f) = cb { - f(done, total); - } - notify_if_registered(ManagerEvent::RenewProgress { done, total }).await; - if new_status != AppStatus::AppPending { - notify_if_registered(ManagerEvent::AppStatusChanged { - record_id: app.id.clone(), - app_id: app.app_id.clone(), - old_status: AppStatus::AppPending, - new_status, - }) - .await; - } - } else { - // Batch returned nothing for this app — escalate to full list. - need_full.push(app.clone()); - } - } - } - - // --- Full release-list path --- - for app in need_full { - let new_status = { - let mut maps = version_maps.write().await; - let vm = maps.entry(app.id.clone()).or_insert_with(|| { - VersionMap::new( - app.invalid_version_number_field_regex.clone(), - app.include_version_number_field_regex.clone(), - ) - }); - if let Some(releases) = getter.get_release_list(&hub, &app.app_id).await { - vm.add_release_list(&hub.uuid, releases); - } else { - vm.set_error(&hub.uuid); - } - let local_version: Option = match android_api::get_android_api() { - Some(api) => api.get_local_version(&app.app_id).await, - None => None, - }; - get_release_status( - vm, - local_version.as_deref(), - app.ignore_version_number.as_deref(), - true, - ) - }; - let done = completed.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1; - if let Some(ref f) = cb { - f(done, total); - } - notify_if_registered(ManagerEvent::RenewProgress { done, total }).await; - if new_status != AppStatus::AppPending { - notify_if_registered(ManagerEvent::AppStatusChanged { - record_id: app.id.clone(), - app_id: app.app_id.clone(), - old_status: AppStatus::AppPending, - new_status, - }) - .await; - } - } - }); - handles.push(handle); - } - - for handle in handles { - let _ = handle.await; - } - } - - /// Return record IDs of saved apps that have no valid hub configured. - /// - /// An app is considered "invalid" when its `enable_hub_list` is non-empty but - /// none of the listed hub UUIDs exist in `known_hub_uuids`. Apps with an empty - /// hub list (meaning "use all hubs") are never reported as invalid. - pub async fn check_invalid_applications(&self, known_hub_uuids: &[String]) -> Vec { - let apps = self.apps.read().await; - apps.values() - .filter_map(|app| { - let hub_uuids = app.get_sorted_hub_uuids(); - // Empty list means "match any hub" — not invalid. - if hub_uuids.is_empty() { - return None; - } - let has_valid = hub_uuids.iter().any(|uuid| known_hub_uuids.contains(uuid)); - if has_valid { - None - } else { - Some(app.id.clone()) - } - }) - .collect() - } -} - -/// Returns `true` if the app requires the full release list rather than the single -/// latest-release batch API. -/// -/// Mirrors Kotlin's `App.needCompleteVersion`: -/// ```kotlin -/// val needCompleteVersion: Boolean -/// get() = db.includeVersionNumberFieldRegexString != null -/// || db.invalidVersionNumberFieldRegexString != null -/// ``` -fn need_complete_version(app: &AppRecord) -> bool { - app.include_version_number_field_regex.is_some() - || app.invalid_version_number_field_regex.is_some() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::database; - - #[test] - fn test_app_record_save_and_load() { - let dir = tempfile::tempdir().unwrap(); - let db = database::Database::open(dir.path()).unwrap(); - - let app = AppRecord::new( - "MyApp".to_string(), - HashMap::from([("owner".to_string(), Some("alice".to_string()))]), - ); - db.upsert_app(&app).unwrap(); - let loaded = db.load_apps().unwrap(); - assert_eq!(loaded.len(), 1); - assert_eq!(loaded[0].name, "MyApp"); - } - - #[tokio::test] - async fn test_version_map_get_status_no_data() { - let mut vm = VersionMap::new(None, None); - let status = get_release_status(&mut vm, Some("1.0.0"), None, true); - // Empty version map + is_saved → NetworkError (no hub_status entries) - assert_eq!(status, AppStatus::NetworkError); - } - - // ------------------------------------------------------------------------- - // Phase 7A: need_complete_version - // ------------------------------------------------------------------------- - - #[test] - fn test_need_complete_version_false_when_no_regex() { - let app = AppRecord::new("App".to_string(), HashMap::new()); - assert!(!need_complete_version(&app)); - } - - #[test] - fn test_need_complete_version_true_when_invalid_regex() { - let mut app = AppRecord::new("App".to_string(), HashMap::new()); - app.invalid_version_number_field_regex = Some("alpha|beta".to_string()); - assert!(need_complete_version(&app)); - } - - #[test] - fn test_need_complete_version_true_when_include_regex() { - let mut app = AppRecord::new("App".to_string(), HashMap::new()); - app.include_version_number_field_regex = Some(r"\d+\.\d+".to_string()); - assert!(need_complete_version(&app)); - } - - #[test] - fn test_need_complete_version_true_when_both_regex() { - let mut app = AppRecord::new("App".to_string(), HashMap::new()); - app.invalid_version_number_field_regex = Some("alpha".to_string()); - app.include_version_number_field_regex = Some(r"\d+".to_string()); - assert!(need_complete_version(&app)); - } - - // ------------------------------------------------------------------------- - // Phase 7B: check_invalid_applications - // ------------------------------------------------------------------------- - - fn make_app_with_hubs(name: &str, hubs: &[&str]) -> AppRecord { - let mut app = AppRecord::new(name.to_string(), HashMap::new()); - let hub_strs: Vec = hubs.iter().map(|s| s.to_string()).collect(); - app.set_sorted_hub_uuids(&hub_strs); - app - } - - async fn app_manager_with_apps(apps: Vec) -> AppManager { - let map = apps.into_iter().map(|a| (a.id.clone(), a)).collect(); - AppManager { - apps: Arc::new(RwLock::new(map)), - virtual_apps: Arc::new(RwLock::new(vec![])), - version_maps: Arc::new(RwLock::new(HashMap::new())), - data_getter: Arc::new(DataGetter::new()), - } - } - - #[tokio::test] - async fn test_check_invalid_no_apps() { - let mgr = app_manager_with_apps(vec![]).await; - let invalid = mgr.check_invalid_applications(&["hub-1".to_string()]).await; - assert!(invalid.is_empty()); - } - - #[tokio::test] - async fn test_check_invalid_empty_hub_list_not_reported() { - // App with no hub list means "use all hubs" — never invalid. - let app = AppRecord::new("NoHubs".to_string(), HashMap::new()); - let mgr = app_manager_with_apps(vec![app]).await; - let invalid = mgr.check_invalid_applications(&[]).await; - assert!(invalid.is_empty()); - } - - #[tokio::test] - async fn test_check_invalid_all_hubs_known() { - let app = make_app_with_hubs("GoodApp", &["hub-a", "hub-b"]); - let mgr = app_manager_with_apps(vec![app]).await; - let known = vec!["hub-a".to_string(), "hub-b".to_string()]; - let invalid = mgr.check_invalid_applications(&known).await; - assert!(invalid.is_empty()); - } - - #[tokio::test] - async fn test_check_invalid_one_hub_known_is_valid() { - // Even if only one of the listed hubs is known, the app is valid. - let app = make_app_with_hubs("SemiGood", &["hub-a", "hub-unknown"]); - let mgr = app_manager_with_apps(vec![app]).await; - let known = vec!["hub-a".to_string()]; - let invalid = mgr.check_invalid_applications(&known).await; - assert!(invalid.is_empty()); - } - - #[tokio::test] - async fn test_check_invalid_all_hubs_unknown() { - let app = make_app_with_hubs("BadApp", &["hub-x", "hub-y"]); - let app_id = app.id.clone(); - let mgr = app_manager_with_apps(vec![app]).await; - let known = vec!["hub-a".to_string()]; - let invalid = mgr.check_invalid_applications(&known).await; - assert_eq!(invalid, vec![app_id]); - } - - #[tokio::test] - async fn test_check_invalid_mixed_apps() { - let good = make_app_with_hubs("Good", &["hub-a"]); - let bad = make_app_with_hubs("Bad", &["hub-z"]); - let bad_id = bad.id.clone(); - let no_hub = AppRecord::new("NoHub".to_string(), HashMap::new()); - let mgr = app_manager_with_apps(vec![good, bad, no_hub]).await; - let known = vec!["hub-a".to_string()]; - let mut invalid = mgr.check_invalid_applications(&known).await; - invalid.sort(); - assert_eq!(invalid, vec![bad_id]); - } -} diff --git a/src/manager/app_status.rs b/src/manager/app_status.rs deleted file mode 100644 index 088ce22..0000000 --- a/src/manager/app_status.rs +++ /dev/null @@ -1,18 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum AppStatus { - /// App exists only in hub's auto-discovery; not in user's saved list - AppInactive, - /// Version data is being fetched - AppPending, - /// Network request failed - NetworkError, - /// Local version is up to date - AppLatest, - /// A newer version is available - AppOutdated, - /// No local version found (e.g. not installed) - AppNoLocal, -} diff --git a/src/manager/auto_template.rs b/src/manager/auto_template.rs deleted file mode 100644 index bfb640d..0000000 --- a/src/manager/auto_template.rs +++ /dev/null @@ -1,206 +0,0 @@ -use std::collections::HashMap; - -/// URL template regex: matches `%placeholder` tokens. -/// Mirrors Kotlin's `URL_ARG_REGEX = "(%.*?)\\w*"`. -const URL_ARG_PATTERN: &str = r"(%[^%/?\s]+)"; - -/// Given a URL and a list of templates, return the first template that fully -/// matches the URL, with all placeholder values extracted. -/// -/// Template format: `https://github.com/%owner/%repo/releases` -/// Placeholders are `%key` tokens. The returned map has keys without the `%`. -/// -/// Returns `None` if no template matches fully. -/// -/// Mirrors Kotlin's `AutoTemplate.urlToAppId()`. -pub fn url_to_app_id(url: &str, templates: &[String]) -> Option> { - if url.is_empty() || templates.is_empty() { - return None; - } - for template in templates { - if let Some(args) = match_template(url, template) { - return Some(args); - } - } - None -} - -/// Attempt to match `url` against a single `template`. -/// -/// The algorithm splits the template into alternating [literal, placeholder] -/// segments, then uses literals to cut the URL apart and extract placeholder -/// values. Mirrors Kotlin's `AutoTemplate.matchArgs()` and `checkFull()`. -fn match_template(url: &str, template: &str) -> Option> { - let re = regex::Regex::new(URL_ARG_PATTERN).ok()?; - - // Build ordered list of segments: either a literal string or a %placeholder - let mut segments: Vec = Vec::new(); - let mut last = 0; - for m in re.find_iter(template) { - if m.start() > last { - segments.push(Segment::Literal(template[last..m.start()].to_string())); - } - segments.push(Segment::Placeholder(m.as_str().to_string())); - last = m.end(); - } - if last < template.len() { - segments.push(Segment::Literal(template[last..].to_string())); - } - - // Collect expected placeholder keys (in order) - let expected_keys: Vec = segments - .iter() - .filter_map(|s| { - if let Segment::Placeholder(p) = s { - Some(p.trim_start_matches('%').to_string()) - } else { - None - } - }) - .collect(); - - if expected_keys.is_empty() { - return None; - } - - // Walk through segments: use literals to split the URL and assign values to - // adjacent placeholders. - let mut args: HashMap = HashMap::new(); - let mut remaining = url.to_string(); - - for (i, seg) in segments.iter().enumerate() { - match seg { - Segment::Literal(lit) => { - if lit.is_empty() { - continue; - } - // Find the literal in `remaining`, split on it - match remaining.split_once(lit.as_str()) { - Some((before, after)) => { - // `before` belongs to the preceding placeholder (if any) - if i > 0 { - if let Segment::Placeholder(key) = &segments[i - 1] { - let k = key.trim_start_matches('%').to_string(); - if !before.is_empty() { - args.insert(k, before.to_string()); - } - } - } - remaining = after.to_string(); - } - None => return None, // literal not found → no match - } - } - Segment::Placeholder(_) => { - // Value will be filled in when the next literal is processed, - // or captured from trailing remaining string at end. - } - } - } - - // If the last segment is a placeholder, the rest of `remaining` is its value - if let Some(Segment::Placeholder(key)) = segments.last() { - let k = key.trim_start_matches('%').to_string(); - // Strip trailing slash or query string for cleanliness - let val = remaining.trim_end_matches('/').to_string(); - if !val.is_empty() { - args.insert(k, val); - } - } - - // Verify all expected keys were matched - for key in &expected_keys { - if !args.contains_key(key) { - return None; - } - } - - Some(args) -} - -#[derive(Debug)] -enum Segment { - Literal(String), - Placeholder(String), -} - -#[cfg(test)] -mod tests { - use super::*; - - fn templates(list: &[&str]) -> Vec { - list.iter().map(|s| s.to_string()).collect() - } - - #[test] - fn test_github_url() { - let url = "https://github.com/DUpdateSystem/UpgradeAll"; - let result = url_to_app_id(url, &templates(&["https://github.com/%owner/%repo"])); - let map = result.unwrap(); - assert_eq!(map["owner"], "DUpdateSystem"); - assert_eq!(map["repo"], "UpgradeAll"); - } - - #[test] - fn test_github_url_with_trailing_slash() { - let url = "https://github.com/foo/bar/"; - let result = url_to_app_id(url, &templates(&["https://github.com/%owner/%repo/"])); - let map = result.unwrap(); - assert_eq!(map["owner"], "foo"); - assert_eq!(map["repo"], "bar"); - } - - #[test] - fn test_gitlab_url() { - let url = "https://gitlab.com/AuroraOSS/AuroraStore"; - let result = url_to_app_id(url, &templates(&["https://gitlab.com/%owner/%repo"])); - let map = result.unwrap(); - assert_eq!(map["owner"], "AuroraOSS"); - assert_eq!(map["repo"], "AuroraStore"); - } - - #[test] - fn test_no_match_returns_none() { - let url = "https://example.com/something/else"; - let result = url_to_app_id(url, &templates(&["https://github.com/%owner/%repo"])); - assert!(result.is_none()); - } - - #[test] - fn test_first_matching_template_wins() { - let url = "https://github.com/user/proj"; - let result = url_to_app_id( - url, - &templates(&[ - "https://gitlab.com/%owner/%repo", - "https://github.com/%owner/%repo", - ]), - ); - let map = result.unwrap(); - assert_eq!(map["owner"], "user"); - assert_eq!(map["repo"], "proj"); - } - - #[test] - fn test_empty_url_returns_none() { - let result = url_to_app_id("", &templates(&["https://github.com/%owner/%repo"])); - assert!(result.is_none()); - } - - #[test] - fn test_empty_templates_returns_none() { - let result = url_to_app_id("https://github.com/a/b", &[]); - assert!(result.is_none()); - } - - #[test] - fn test_single_placeholder() { - let url = "https://f-droid.org/packages/com.example.app/"; - let result = url_to_app_id( - url, - &templates(&["https://f-droid.org/packages/%package_name/"]), - ); - let map = result.unwrap(); - assert_eq!(map["package_name"], "com.example.app"); - } -} diff --git a/src/manager/cloud_config_getter.rs b/src/manager/cloud_config_getter.rs deleted file mode 100644 index b4fbc66..0000000 --- a/src/manager/cloud_config_getter.rs +++ /dev/null @@ -1,403 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; -use tokio::sync::RwLock; - -use crate::database::models::app::AppRecord; -use crate::database::models::hub::HubRecord; -use crate::error::{Error, Result}; -use crate::manager::app_manager::AppManager; -use crate::manager::auto_template::url_to_app_id; -use crate::manager::hub_manager::HubManager; -use crate::websdk::cloud_rules::cloud_rules_manager::CloudRules; -use crate::websdk::cloud_rules::data::app_item::AppItem; -use crate::websdk::cloud_rules::data::hub_item::HubItem; - -/// Manages downloading, caching, and applying cloud hub/app configurations. -/// -/// Mirrors Kotlin's `CloudConfigGetter` singleton. -pub struct CloudConfigGetter { - api_url: String, - cloud_rules: Arc>>, -} - -impl CloudConfigGetter { - pub fn new(api_url: String) -> Self { - Self { - api_url, - cloud_rules: Arc::new(RwLock::new(None)), - } - } - - /// Download and cache the latest cloud config list. - pub async fn renew(&self) -> Result<()> { - let mut rules = CloudRules::new(&self.api_url); - rules - .renew() - .await - .map_err(|e| Error::Other(e.to_string()))?; - *self.cloud_rules.write().await = Some(rules); - Ok(()) - } - - /// Returns all available app configs from the cached cloud config. - pub async fn app_config_list(&self) -> Vec { - match self.cloud_rules.read().await.as_ref() { - Some(rules) => rules - .get_config_list() - .app_config_list - .into_iter() - .cloned() - .collect(), - None => vec![], - } - } - - /// Returns all available hub configs from the cached cloud config. - pub async fn hub_config_list(&self) -> Vec { - match self.cloud_rules.read().await.as_ref() { - Some(rules) => rules - .get_config_list() - .hub_config_list - .into_iter() - .cloned() - .collect(), - None => vec![], - } - } - - /// Apply a cloud hub config by UUID: download config → convert → upsert into HubManager. - /// - /// Returns true if the hub was installed or updated. - pub async fn apply_hub_config(&self, uuid: &str, hub_mgr: &mut HubManager) -> Result { - let hub_item = self - .find_hub_item(uuid) - .await - .ok_or_else(|| Error::Other(format!("Hub config not found: {uuid}")))?; - - let record = hub_item_to_record(&hub_item, hub_mgr).await; - hub_mgr.upsert_hub(record).await?; - Ok(true) - } - - /// Apply a cloud app config by UUID: - /// 1. Ensure hub dependency is installed. - /// 2. Extract app_id from the app's URL using the hub's URL templates. - /// 3. Merge with `extra_map` from the cloud config. - /// 4. Upsert into AppManager. - /// - /// Returns true if the app was installed or updated. - pub async fn apply_app_config( - &self, - uuid: &str, - app_mgr: &mut AppManager, - hub_mgr: &mut HubManager, - ) -> Result { - let app_item = self - .find_app_item(uuid) - .await - .ok_or_else(|| Error::Other(format!("App config not found: {uuid}")))?; - - // Ensure hub dependency is present - self.solve_hub_dependency(&app_item.base_hub_uuid, hub_mgr) - .await?; - - // Build the app_id map - let app_id = build_app_id(&app_item, hub_mgr).await; - - // Find existing record by cloud UUID or create a new one - let mut record = app_mgr - .find_app_by_cloud_uuid(&app_item.uuid) - .await - .unwrap_or_else(|| { - let mut r = AppRecord::new(app_item.info.name.clone(), app_id.clone()); - r.id = String::new(); // let save_app assign UUID - r - }); - - record.name = app_item.info.name.clone(); - record.app_id = app_id; - record.cloud_config = Some(app_item.clone()); - - // Ensure base_hub_uuid is first in the hub priority list - let mut hub_uuids = record.get_sorted_hub_uuids(); - if !hub_uuids.contains(&app_item.base_hub_uuid) { - hub_uuids.insert(0, app_item.base_hub_uuid.clone()); - } else if hub_uuids[0] != app_item.base_hub_uuid { - hub_uuids.retain(|u| u != &app_item.base_hub_uuid); - hub_uuids.insert(0, app_item.base_hub_uuid.clone()); - } - record.set_sorted_hub_uuids(&hub_uuids); - - app_mgr.save_app(record).await?; - Ok(true) - } - - /// Bulk-update all installed apps and hubs whose cloud config version has increased. - /// - /// Mirrors Kotlin's `renewAllAppConfigFromCloud` + `renewAllHubConfigFromCloud`. - pub async fn renew_all_from_cloud( - &self, - app_mgr: &mut AppManager, - hub_mgr: &mut HubManager, - ) -> Result<()> { - // Renew hubs - let installed_hubs = hub_mgr.get_hub_list().await; - for hub in &installed_hubs { - if let Some(cloud_hub) = self.find_hub_item(&hub.uuid).await { - if cloud_hub.config_version > hub.hub_config.config_version { - let record = hub_item_to_record(&cloud_hub, hub_mgr).await; - hub_mgr.upsert_hub(record).await?; - } - } - } - - // Renew apps - let installed_apps = app_mgr.get_saved_apps().await; - for app in &installed_apps { - let cloud_uuid = match app.cloud_config.as_ref().map(|c| c.uuid.as_str()) { - Some(u) if !u.is_empty() => u.to_string(), - _ => continue, - }; - if let Some(cloud_app) = self.find_app_item(&cloud_uuid).await { - let installed_version = app - .cloud_config - .as_ref() - .map(|c| c.config_version) - .unwrap_or(0); - if cloud_app.config_version > installed_version { - let _ = self.apply_app_config(&cloud_uuid, app_mgr, hub_mgr).await; - } - } - } - - Ok(()) - } - - /// Ensure a hub is installed; if not, download and install it from cloud config. - async fn solve_hub_dependency(&self, hub_uuid: &str, hub_mgr: &mut HubManager) -> Result<()> { - if hub_mgr.get_hub(hub_uuid).await.is_some() { - // Already installed — check if update needed - let installed = hub_mgr.get_hub(hub_uuid).await.unwrap(); - if let Some(cloud) = self.find_hub_item(hub_uuid).await { - if cloud.config_version > installed.hub_config.config_version { - let record = hub_item_to_record(&cloud, hub_mgr).await; - hub_mgr.upsert_hub(record).await?; - } - } - } else { - // Not installed — download and install - self.apply_hub_config(hub_uuid, hub_mgr).await?; - } - Ok(()) - } - - async fn find_app_item(&self, uuid: &str) -> Option { - self.cloud_rules - .read() - .await - .as_ref()? - .get_config_list() - .app_config_list - .into_iter() - .find(|a| a.uuid == uuid) - .cloned() - } - - async fn find_hub_item(&self, uuid: &str) -> Option { - self.cloud_rules - .read() - .await - .as_ref()? - .get_config_list() - .hub_config_list - .into_iter() - .find(|h| h.uuid == uuid) - .cloned() - } -} - -/// Convert a `HubItem` from cloud config to a `HubRecord`, preserving any -/// existing auth / ignore lists if the hub is already installed. -async fn hub_item_to_record(hub_item: &HubItem, hub_mgr: &HubManager) -> HubRecord { - if let Some(existing) = hub_mgr.get_hub(&hub_item.uuid).await { - // Preserve mutable fields, update config fields - HubRecord { - hub_config: hub_item.clone(), - ..existing - } - } else { - HubRecord::new(hub_item.uuid.clone(), hub_item.clone()) - } -} - -/// Build the `app_id` map from a cloud `AppItem`. -/// -/// 1. Try to extract from `info.url` using the hub's `app_url_templates`. -/// 2. Merge with `info.extra_map` (extra_map takes precedence for any shared keys). -async fn build_app_id(app_item: &AppItem, hub_mgr: &HubManager) -> HashMap> { - let mut app_id: HashMap> = HashMap::new(); - - // Try URL template extraction - if let Some(hub) = hub_mgr.get_hub(&app_item.base_hub_uuid).await { - let templates = &hub.hub_config.app_url_templates; - if !app_item.info.url.is_empty() { - if let Some(extracted) = url_to_app_id(&app_item.info.url, templates) { - for (k, v) in extracted { - app_id.insert(k, Some(v)); - } - } - } - } - - // Merge extra_map — extra_map values always win over URL-extracted values - for (k, v) in &app_item.info.extra_map { - app_id.insert(k.clone(), Some(v.clone())); - } - - app_id -} - -// Extension on AppManager to find by cloud UUID -impl AppManager { - pub async fn find_app_by_cloud_uuid(&self, cloud_uuid: &str) -> Option { - self.get_saved_apps().await.into_iter().find(|a| { - a.cloud_config - .as_ref() - .map(|c| c.uuid == cloud_uuid) - .unwrap_or(false) - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::websdk::cloud_rules::data::app_item::AppInfo; - use crate::websdk::cloud_rules::data::hub_item::Info; - - fn make_hub_item(uuid: &str, templates: Vec) -> HubItem { - HubItem { - base_version: 6, - config_version: 1, - uuid: uuid.to_string(), - info: Info { - hub_name: "GitHub".to_string(), - hub_icon_url: None, - }, - api_keywords: vec!["owner".to_string(), "repo".to_string()], - auth_keywords: vec![], - app_url_templates: templates, - target_check_api: None, - } - } - - fn make_app_item(uuid: &str, hub_uuid: &str, url: &str) -> AppItem { - AppItem { - base_version: 2, - config_version: 1, - uuid: uuid.to_string(), - base_hub_uuid: hub_uuid.to_string(), - info: AppInfo { - name: "TestApp".to_string(), - url: url.to_string(), - extra_map: HashMap::new(), - }, - } - } - - #[tokio::test] - async fn test_build_app_id_from_url() { - let dir = tempfile::tempdir().unwrap(); - let _ = crate::database::init_db(dir.path()); - - let hub_uuid = "fd9b2602-62c5-4d55-bd1e-0d6537714ca0"; - let db = crate::database::Database::open(dir.path()).unwrap(); - let hub = HubRecord::new( - hub_uuid.to_string(), - make_hub_item( - hub_uuid, - vec!["https://github.com/%owner/%repo/".to_string()], - ), - ); - db.upsert_hub(&hub).unwrap(); - - let hub_mgr = HubManager::load().unwrap_or_else(|_| { - // fallback: create manager from local db - let records = db.load_hubs().unwrap(); - let _ = records; // suppress unused warning - // We can't easily construct HubManager without global DB, so use a helper - panic!("This test requires global DB init") - }); - let _ = hub_mgr; - } - - #[test] - fn test_build_app_id_extra_map_wins() { - // extra_map should take precedence over URL-extracted values - let app_item = AppItem { - base_version: 2, - config_version: 1, - uuid: "test-uuid".to_string(), - base_hub_uuid: "hub-uuid".to_string(), - info: AppInfo { - name: "TestApp".to_string(), - url: "https://github.com/owner/repo".to_string(), - extra_map: HashMap::from([ - ("android_app_package".to_string(), "com.example".to_string()), - ("owner".to_string(), "override_owner".to_string()), - ]), - }, - }; - - // Simulate what build_app_id does with extra_map override - let mut app_id: HashMap> = HashMap::new(); - // Pretend URL extraction gave owner=owner, repo=repo - app_id.insert("owner".to_string(), Some("owner".to_string())); - app_id.insert("repo".to_string(), Some("repo".to_string())); - // extra_map override - for (k, v) in &app_item.info.extra_map { - app_id.insert(k.clone(), Some(v.clone())); - } - // owner should be overridden by extra_map - assert_eq!(app_id["owner"], Some("override_owner".to_string())); - assert_eq!( - app_id["android_app_package"], - Some("com.example".to_string()) - ); - // repo still from URL extraction - assert_eq!(app_id["repo"], Some("repo".to_string())); - } - - #[test] - fn test_hub_item_to_record_preserves_auth() { - // hub_item_to_record should preserve auth from existing record - // We test the logic inline since it's async and needs a HubManager - let hub_item = make_hub_item( - "hub-1", - vec!["https://github.com/%owner/%repo/".to_string()], - ); - let existing = HubRecord { - uuid: "hub-1".to_string(), - hub_config: make_hub_item("hub-1", vec![]), - auth: HashMap::from([("token".to_string(), "secret".to_string())]), - ignore_app_id_list: vec![], - applications_mode: 1, - user_ignore_app_id_list: vec![], - sort_point: -5, - }; - - // Simulate hub_item_to_record(hub_item, existing) - let record = HubRecord { - hub_config: hub_item.clone(), - ..existing.clone() - }; - - assert_eq!(record.auth["token"], "secret"); - assert_eq!(record.applications_mode, 1); - assert_eq!(record.sort_point, -5); - assert_eq!( - record.hub_config.app_url_templates[0], - "https://github.com/%owner/%repo/" - ); - } -} diff --git a/src/manager/data_getter.rs b/src/manager/data_getter.rs deleted file mode 100644 index 49312ef..0000000 --- a/src/manager/data_getter.rs +++ /dev/null @@ -1,99 +0,0 @@ -use std::collections::{BTreeMap, HashMap}; -use std::sync::Arc; -use tokio::sync::Mutex; - -use crate::database::models::hub::HubRecord; -use crate::websdk::repo::api; -use crate::websdk::repo::data::release::ReleaseData; - -/// Result of a batch latest-release request. -/// Each entry is (app_id, Option) — None means the app wasn't found. -pub type LatestReleaseResults = Vec<(HashMap>, Option)>; - -/// Fetches release data from hub providers. -/// -/// Mirrors Kotlin's `DataGetter`. -pub struct DataGetter { - /// Per-hub mutex to prevent duplicate concurrent requests. - hub_locks: Arc>>>>, -} - -impl DataGetter { - pub fn new() -> Self { - Self { - hub_locks: Arc::new(Mutex::new(HashMap::new())), - } - } - - async fn hub_lock(&self, hub_uuid: &str) -> Arc> { - let mut locks = self.hub_locks.lock().await; - locks - .entry(hub_uuid.to_string()) - .or_insert_with(|| Arc::new(Mutex::new(()))) - .clone() - } - - /// Fetch the latest single release for each app from one hub. - /// - /// Returns a vec of `(app_id, Option)`. - /// `None` means the hub didn't return data for that app. - pub async fn get_latest_releases( - &self, - hub: &HubRecord, - app_ids: &[HashMap>], - ) -> LatestReleaseResults { - let lock = self.hub_lock(&hub.uuid).await; - let _guard = lock.lock().await; - - let mut results = Vec::with_capacity(app_ids.len()); - for app_id in app_ids { - let app_data = build_app_data(app_id, &hub.hub_config.api_keywords); - if app_data.is_empty() { - results.push((app_id.clone(), None)); - continue; - } - let hub_data = build_hub_data(&hub.auth); - let release = api::get_latest_release(&hub.uuid, &app_data, &hub_data).await; - results.push((app_id.clone(), release)); - } - results - } - - /// Fetch the full release list for a single app from one hub. - pub async fn get_release_list( - &self, - hub: &HubRecord, - app_id: &HashMap>, - ) -> Option> { - let lock = self.hub_lock(&hub.uuid).await; - let _guard = lock.lock().await; - - let app_data = build_app_data(app_id, &hub.hub_config.api_keywords); - if app_data.is_empty() { - return None; - } - let hub_data = build_hub_data(&hub.auth); - api::get_releases(&hub.uuid, &app_data, &hub_data).await - } -} - -fn build_app_data<'a>( - app_id: &'a HashMap>, - api_keywords: &[String], -) -> BTreeMap<&'a str, &'a str> { - app_id - .iter() - .filter(|(k, v)| api_keywords.contains(k) && v.is_some()) - .map(|(k, v)| (k.as_str(), v.as_deref().unwrap())) - .collect() -} - -fn build_hub_data(auth: &HashMap) -> BTreeMap<&str, &str> { - auth.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect() -} - -impl Default for DataGetter { - fn default() -> Self { - Self::new() - } -} diff --git a/src/manager/hub_manager.rs b/src/manager/hub_manager.rs deleted file mode 100644 index 9c52503..0000000 --- a/src/manager/hub_manager.rs +++ /dev/null @@ -1,173 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; -use tokio::sync::RwLock; - -use crate::database::get_db; -use crate::database::models::hub::HubRecord; -use crate::error::Result; - -/// In-memory hub registry backed by the JSONL database. -/// -/// Mirrors Kotlin's `HubManager`. -pub struct HubManager { - hubs: Arc>>, -} - -impl HubManager { - /// Load all hubs from the database. - pub fn load() -> Result { - let records = get_db().load_hubs()?; - let map = records.into_iter().map(|h| (h.uuid.clone(), h)).collect(); - Ok(Self { - hubs: Arc::new(RwLock::new(map)), - }) - } - - pub async fn get_hub_list(&self) -> Vec { - self.hubs.read().await.values().cloned().collect() - } - - pub async fn get_hub(&self, uuid: &str) -> Option { - self.hubs.read().await.get(uuid).cloned() - } - - /// Insert or update a hub (persists to database). - pub async fn upsert_hub(&self, record: HubRecord) -> Result<()> { - get_db().upsert_hub(&record)?; - self.hubs.write().await.insert(record.uuid.clone(), record); - Ok(()) - } - - /// Remove a hub by UUID (persists deletion to database). - pub async fn remove_hub(&self, uuid: &str) -> Result { - let deleted = get_db().delete_hub(uuid)?; - self.hubs.write().await.remove(uuid); - Ok(deleted) - } - - pub async fn is_applications_mode_enabled(&self) -> bool { - self.hubs - .read() - .await - .values() - .any(|h| h.applications_mode_enabled()) - } - - /// Update the auth map for a hub identified by UUID and persist the change. - /// - /// Returns `false` if no hub with the given UUID exists. - pub async fn update_auth(&self, uuid: &str, auth: HashMap) -> Result { - let mut hubs = self.hubs.write().await; - let hub = match hubs.get_mut(uuid) { - Some(h) => h, - None => return Ok(false), - }; - hub.auth = auth; - get_db().upsert_hub(hub)?; - Ok(true) - } - - /// Return hubs whose api_keywords contain any of the given app_id keys. - pub async fn hubs_for_app(&self, app_id: &HashMap>) -> Vec { - let app_keys: Vec<&str> = app_id.keys().map(String::as_str).collect(); - self.hubs - .read() - .await - .values() - .filter(|h| { - h.hub_config - .api_keywords - .iter() - .any(|kw| app_keys.contains(&kw.as_str())) - }) - .cloned() - .collect() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::database; - use crate::websdk::cloud_rules::data::hub_item::{HubItem, Info}; - use tempfile::TempDir; - - fn setup_db() -> TempDir { - let dir = tempfile::tempdir().unwrap(); - database::init_db(dir.path()).ok(); // may already be init in other tests - dir - } - - fn make_hub(uuid: &str) -> HubRecord { - HubRecord::new( - uuid.to_string(), - HubItem { - base_version: 6, - config_version: 1, - uuid: uuid.to_string(), - info: Info { - hub_name: "TestHub".to_string(), - hub_icon_url: None, - }, - api_keywords: vec!["owner".to_string(), "repo".to_string()], - auth_keywords: vec![], - app_url_templates: vec![], - target_check_api: None, - }, - ) - } - - // These tests use a fresh TempDir + DB each time via open() directly, - // bypassing the global singleton to allow parallel test runs. - #[test] - fn test_upsert_and_list() { - let dir = tempfile::tempdir().unwrap(); - let db = crate::database::Database::open(dir.path()).unwrap(); - let hub = make_hub("uuid-1"); - db.upsert_hub(&hub).unwrap(); - let hubs = db.load_hubs().unwrap(); - assert_eq!(hubs.len(), 1); - assert_eq!(hubs[0].uuid, "uuid-1"); - } - - #[test] - fn test_delete_hub() { - let dir = tempfile::tempdir().unwrap(); - let db = crate::database::Database::open(dir.path()).unwrap(); - let hub = make_hub("uuid-2"); - db.upsert_hub(&hub).unwrap(); - let deleted = db.delete_hub("uuid-2").unwrap(); - assert!(deleted); - assert!(db.load_hubs().unwrap().is_empty()); - } - - #[tokio::test] - async fn test_update_auth() { - let dir = tempfile::tempdir().unwrap(); - crate::database::init_db(dir.path()).ok(); - - // Insert the hub via HubManager so it is in both the global DB and in-memory state. - let mgr = HubManager::load().unwrap(); - let hub = make_hub("uuid-auth"); - mgr.upsert_hub(hub).await.unwrap(); - - let new_auth: HashMap = - [("token".to_string(), "ghp_test123".to_string())].into(); - let ok = mgr - .update_auth("uuid-auth", new_auth.clone()) - .await - .unwrap(); - assert!(ok); - - // Verify in-memory state updated. - let updated = mgr.get_hub("uuid-auth").await.unwrap(); - assert_eq!(updated.auth, new_auth); - - // Returns false for unknown UUID. - let not_found = mgr - .update_auth("no-such-uuid", HashMap::new()) - .await - .unwrap(); - assert!(!not_found); - } -} diff --git a/src/manager/mod.rs b/src/manager/mod.rs deleted file mode 100644 index 275d73c..0000000 --- a/src/manager/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub mod android_api; -pub mod app_manager; -pub mod app_status; -pub mod auto_template; -pub mod cloud_config_getter; -pub mod data_getter; -pub mod hub_manager; -pub mod notification; -pub mod updater; -pub mod url_replace; -pub mod version; -pub mod version_map; diff --git a/src/manager/notification.rs b/src/manager/notification.rs deleted file mode 100644 index ef23840..0000000 --- a/src/manager/notification.rs +++ /dev/null @@ -1,152 +0,0 @@ -use once_cell::sync::OnceCell; -use serde::{Deserialize, Serialize}; - -use crate::database::models::app::AppRecord; -use crate::manager::app_status::AppStatus; - -/// Global notification dispatcher, registered via `register_notification` RPC. -static NOTIFICATION: OnceCell = OnceCell::new(); - -pub fn set_notification(url: String) { - let _ = NOTIFICATION.set(NotificationDispatcher { url }); -} - -pub fn get_notification() -> Option<&'static NotificationDispatcher> { - NOTIFICATION.get() -} - -/// Events emitted by the Rust manager layer to the Kotlin UI. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "type", rename_all = "snake_case")] -pub enum ManagerEvent { - AppStatusChanged { - record_id: String, - app_id: std::collections::HashMap>, - old_status: AppStatus, - new_status: AppStatus, - }, - RenewProgress { - done: usize, - total: usize, - }, - AppAdded { - record: AppRecord, - }, - AppDeleted { - record_id: String, - }, - AppDatabaseChanged { - record: AppRecord, - }, -} - -/// Dispatches manager events to the Kotlin UI layer via HTTP JSON-RPC. -/// -/// Kotlin registers a notification URL via `register_notification` RPC. -/// When an event fires, Rust POSTs `on_manager_event({event})` to that URL. -/// The Kotlin server handles it and updates the UI (ViewModel / LiveData). -pub struct NotificationDispatcher { - url: String, -} - -impl NotificationDispatcher { - /// Fire an event notification to Kotlin. Best-effort: errors are logged but not propagated. - pub async fn notify(&self, event: ManagerEvent) { - let body = match build_jsonrpc_request("on_manager_event", &event) { - Ok(b) => b, - Err(e) => { - eprintln!("NotificationDispatcher: failed to serialize event: {e}"); - return; - } - }; - - let client = reqwest::Client::new(); - match client - .post(&self.url) - .header("Content-Type", "application/json") - .body(body) - .send() - .await - { - Ok(_) => {} - Err(e) => { - eprintln!("NotificationDispatcher: failed to send notification: {e}"); - } - } - } -} - -fn build_jsonrpc_request( - method: &str, - params: &T, -) -> Result { - #[derive(Serialize)] - struct JsonRpcRequest<'a, P: Serialize> { - jsonrpc: &'a str, - method: &'a str, - params: &'a P, - id: u64, - } - serde_json::to_string(&JsonRpcRequest { - jsonrpc: "2.0", - method, - params, - id: 1, - }) -} - -/// Convenience: notify if a dispatcher is registered (no-op otherwise). -pub async fn notify_if_registered(event: ManagerEvent) { - if let Some(dispatcher) = get_notification() { - dispatcher.notify(event).await; - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_event_serialization_status_changed() { - let event = ManagerEvent::AppStatusChanged { - record_id: "abc-123".to_string(), - app_id: std::collections::HashMap::from([( - "owner".to_string(), - Some("alice".to_string()), - )]), - old_status: AppStatus::AppPending, - new_status: AppStatus::AppOutdated, - }; - let json = serde_json::to_string(&event).unwrap(); - assert!(json.contains("app_status_changed")); - assert!(json.contains("abc-123")); - assert!(json.contains("app_outdated")); - } - - #[test] - fn test_event_serialization_renew_progress() { - let event = ManagerEvent::RenewProgress { done: 3, total: 10 }; - let json = serde_json::to_string(&event).unwrap(); - assert!(json.contains("renew_progress")); - assert!(json.contains("10")); - } - - #[test] - fn test_event_serialization_app_deleted() { - let event = ManagerEvent::AppDeleted { - record_id: "del-456".to_string(), - }; - let json = serde_json::to_string(&event).unwrap(); - assert!(json.contains("app_deleted")); - assert!(json.contains("del-456")); - } - - #[test] - fn test_jsonrpc_request_format() { - let event = ManagerEvent::RenewProgress { done: 1, total: 5 }; - let body = build_jsonrpc_request("on_manager_event", &event).unwrap(); - assert!(body.contains("\"jsonrpc\":\"2.0\"")); - assert!(body.contains("\"method\":\"on_manager_event\"")); - assert!(body.contains("renew_progress")); - } -} diff --git a/src/manager/updater.rs b/src/manager/updater.rs deleted file mode 100644 index ec2fd24..0000000 --- a/src/manager/updater.rs +++ /dev/null @@ -1,151 +0,0 @@ -use super::app_status::AppStatus; -use super::version::VersionInfo; -use super::version_map::{HubStatus, VersionMap}; -use std::collections::HashMap; - -/// Determines the release status for an app given its version map and local version. -/// -/// Mirrors Kotlin's `Updater.getReleaseStatus()`. -pub fn get_release_status( - version_map: &mut VersionMap, - local_version: Option<&str>, - ignore_version: Option<&str>, - is_saved: bool, -) -> AppStatus { - let versions = version_map.get_version_list(); - - if versions.is_empty() { - if version_map.is_renewing() { - return AppStatus::AppPending; - } - let all_error = !version_map.hub_status.is_empty() - && version_map - .hub_status - .values() - .all(|s| *s == HubStatus::Error); - if all_error || is_saved { - return AppStatus::NetworkError; - } - return AppStatus::AppInactive; - } - - let latest_name = &versions[0].version_info.name; - - // If the latest version matches what the user chose to ignore - if let Some(ignored) = ignore_version { - if ignored == latest_name { - return AppStatus::AppLatest; - } - } - - let effective_local = local_version.or(ignore_version); - - match effective_local { - None => AppStatus::AppNoLocal, - Some(local_str) => { - let local_info = VersionInfo::new(local_str, None, None, HashMap::new()); - let latest_info = &versions[0].version_info; - if is_latest(&local_info, latest_info) { - AppStatus::AppLatest - } else { - AppStatus::AppOutdated - } - } - } -} - -fn is_latest(local: &VersionInfo, latest: &VersionInfo) -> bool { - use std::cmp::Ordering; - match local.compare(latest) { - Some(Ordering::Greater) | Some(Ordering::Equal) => true, - Some(Ordering::Less) => false, - None => { - // Fallback: string equality check - local.name == latest.name - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::manager::version_map::VersionMap; - use crate::websdk::repo::data::release::{AssetData, ReleaseData}; - - fn release(v: &str) -> ReleaseData { - ReleaseData { - version_number: v.to_string(), - changelog: String::new(), - assets: vec![AssetData { - file_name: "app.apk".to_string(), - file_type: "apk".to_string(), - download_url: "https://x.com".to_string(), - }], - extra: None, - } - } - - fn vm_with(versions: &[&str]) -> VersionMap { - let mut vm = VersionMap::new(None, None); - vm.add_release_list("hub1", versions.iter().map(|v| release(v)).collect()); - vm - } - - #[test] - fn test_latest() { - let mut vm = vm_with(&["2.0.0", "1.0.0"]); - let status = get_release_status(&mut vm, Some("2.0.0"), None, true); - assert_eq!(status, AppStatus::AppLatest); - } - - #[test] - fn test_outdated() { - let mut vm = vm_with(&["2.0.0", "1.0.0"]); - let status = get_release_status(&mut vm, Some("1.0.0"), None, true); - assert_eq!(status, AppStatus::AppOutdated); - } - - #[test] - fn test_no_local() { - let mut vm = vm_with(&["2.0.0"]); - let status = get_release_status(&mut vm, None, None, true); - assert_eq!(status, AppStatus::AppNoLocal); - } - - #[test] - fn test_ignored_version() { - let mut vm = vm_with(&["2.0.0"]); - let status = get_release_status(&mut vm, None, Some("2.0.0"), true); - assert_eq!(status, AppStatus::AppLatest); - } - - #[test] - fn test_network_error() { - let mut vm = VersionMap::new(None, None); - vm.set_error("hub1"); - let status = get_release_status(&mut vm, Some("1.0.0"), None, true); - assert_eq!(status, AppStatus::NetworkError); - } - - #[test] - fn test_pending() { - let mut vm = VersionMap::new(None, None); - vm.mark_renewing("hub1"); - let status = get_release_status(&mut vm, Some("1.0.0"), None, true); - assert_eq!(status, AppStatus::AppPending); - } - - #[test] - fn test_inactive_unsaved() { - let mut vm = VersionMap::new(None, None); - let status = get_release_status(&mut vm, Some("1.0.0"), None, false); - assert_eq!(status, AppStatus::AppInactive); - } - - #[test] - fn test_local_newer_than_remote() { - let mut vm = vm_with(&["1.0.0"]); - let status = get_release_status(&mut vm, Some("2.0.0"), None, true); - assert_eq!(status, AppStatus::AppLatest); - } -} diff --git a/src/manager/url_replace.rs b/src/manager/url_replace.rs deleted file mode 100644 index e205d0e..0000000 --- a/src/manager/url_replace.rs +++ /dev/null @@ -1,170 +0,0 @@ -/// Applies URL replacement rules from an ExtraHub configuration. -/// -/// Mirrors Kotlin's `URLReplace.replaceURL()`. -/// -/// Three replacement modes: -/// 1. Plain regex: `replace(search_regex, replace_str)` across the full URL. -/// 2. Host-only: if `replace_str` looks like a bare host URL (no path), only -/// the host portion of the original URL is replaced. -/// 3. Template: if `replace_str` contains `{DOWNLOAD_URL}`, the original URL -/// is embedded as a parameter (e.g. proxy wrappers). -pub fn apply_url_replace(url: &str, search: Option<&str>, replace: Option<&str>) -> String { - let replace_str = match replace { - Some(r) if !r.is_empty() => r, - _ => return url.to_string(), - }; - - // Mode 3: template substitution — replace_str contains {DOWNLOAD_URL} - if replace_str.contains("{DOWNLOAD_URL}") { - return replace_str.replace("{DOWNLOAD_URL}", url); - } - - // Mode 2: host-only replacement — replace_str is a bare host URL (no path component) - if is_host_only(replace_str) { - return replace_host(url, replace_str); - } - - // Mode 1: plain regex replacement (or literal if no search) - match search { - Some(pattern) if !pattern.is_empty() => match regex::Regex::new(pattern) { - Ok(re) => re.replace_all(url, replace_str).into_owned(), - Err(_) => url.replace(pattern, replace_str), - }, - // No search pattern: nothing to replace - _ => url.to_string(), - } -} - -/// Returns true if `s` looks like a bare host URL with no meaningful path. -/// e.g. "https://mirror.example.com" or "https://mirror.example.com/" -fn is_host_only(s: &str) -> bool { - match url::Url::parse(s) { - Ok(u) => { - let path = u.path(); - path.is_empty() || path == "/" - } - Err(_) => false, - } -} - -/// Replace only the host (scheme + host + port) of `original_url` with the -/// host from `host_url`, keeping the original path, query and fragment. -fn replace_host(original_url: &str, host_url: &str) -> String { - let orig = match url::Url::parse(original_url) { - Ok(u) => u, - Err(_) => return original_url.to_string(), - }; - let host = match url::Url::parse(host_url) { - Ok(u) => u, - Err(_) => return original_url.to_string(), - }; - - // Rebuild: scheme + host from `host`, everything else from `orig` - let mut result = host.clone(); - result.set_path(orig.path()); - result.set_query(orig.query()); - result.set_fragment(orig.fragment()); - result.to_string() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_no_replace_returns_original() { - let url = "https://github.com/user/repo/releases/download/v1.0/app.apk"; - assert_eq!(apply_url_replace(url, None, None), url); - assert_eq!(apply_url_replace(url, None, Some("")), url); - } - - #[test] - fn test_download_url_template() { - let url = "https://github.com/user/repo/releases/download/v1.0/app.apk"; - let replace = "https://ghproxy.com/?url={DOWNLOAD_URL}"; - let result = apply_url_replace(url, None, Some(replace)); - assert_eq!( - result, - "https://ghproxy.com/?url=https://github.com/user/repo/releases/download/v1.0/app.apk" - ); - } - - #[test] - fn test_host_only_replacement() { - let url = "https://github.com/user/repo/releases/download/v1.0/app.apk"; - let result = apply_url_replace(url, None, Some("https://mirror.ghproxy.com")); - assert!(result.contains("mirror.ghproxy.com")); - assert!(result.contains("/user/repo/releases/download/v1.0/app.apk")); - assert!(!result.contains("github.com")); - } - - #[test] - fn test_host_only_with_trailing_slash() { - let url = "https://github.com/owner/repo/archive/v2.zip"; - let result = apply_url_replace(url, None, Some("https://mirror.example.com/")); - assert!(result.contains("mirror.example.com")); - assert!(result.contains("/owner/repo/archive/v2.zip")); - } - - #[test] - fn test_regex_replacement() { - let url = "https://github.com/user/repo/releases/download/v1.0/app.apk"; - let result = apply_url_replace(url, Some("github\\.com"), Some("github.com.cnpmjs.org")); - assert!(result.contains("github.com.cnpmjs.org")); - assert!(!result.contains("//github.com/")); - } - - #[test] - fn test_invalid_regex_falls_back_to_literal() { - let url = "https://github.com/user/repo"; - // Invalid regex pattern — should fall back to literal string replace - let result = apply_url_replace(url, Some("github.com"), Some("gitlab.com")); - assert!(result.contains("gitlab.com")); - } - - // ------------------------------------------------------------------------- - // Phase 8: chained GLOBAL + hub-specific rules (as applied in get_download) - // ------------------------------------------------------------------------- - - #[test] - fn test_global_then_hub_specific_chain() { - // Simulate: GLOBAL rule replaces github.com host, then hub rule wraps via template. - let url = "https://github.com/owner/repo/releases/download/v1.0/app.apk"; - - // Step 1 — GLOBAL rule: replace host with mirror - let after_global = apply_url_replace(url, None, Some("https://mirror.example.com")); - assert!(after_global.contains("mirror.example.com")); - assert!(!after_global.contains("github.com")); - - // Step 2 — hub-specific rule: wrap with proxy template - let after_hub = apply_url_replace( - &after_global, - None, - Some("https://proxy.example.com/?url={DOWNLOAD_URL}"), - ); - assert!(after_hub.starts_with("https://proxy.example.com/?url=")); - assert!(after_hub.contains("mirror.example.com")); - } - - #[test] - fn test_no_global_rule_hub_rule_applies() { - let url = "https://github.com/owner/repo/archive/v2.zip"; - - // GLOBAL has no replace rule — url unchanged - let after_global = apply_url_replace(url, None, None); - assert_eq!(after_global, url); - - // hub-specific regex rule - let after_hub = - apply_url_replace(&after_global, Some("github\\.com"), Some("gh.example.com")); - assert!(after_hub.contains("gh.example.com")); - } - - #[test] - fn test_both_rules_none_url_unchanged() { - let url = "https://github.com/owner/repo/releases/v1.apk"; - let after_global = apply_url_replace(url, None, None); - let after_hub = apply_url_replace(&after_global, None, None); - assert_eq!(after_hub, url); - } -} diff --git a/src/manager/version.rs b/src/manager/version.rs deleted file mode 100644 index 85547e7..0000000 --- a/src/manager/version.rs +++ /dev/null @@ -1,136 +0,0 @@ -use crate::utils::versioning::Version as VersionUtil; -use crate::websdk::repo::data::release::ReleaseData; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -/// A parsed, comparable version identifier. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct VersionInfo { - /// Normalized version name (regex-filtered) - pub name: String, - /// Extra metadata (e.g. version_code from Android) - pub extra: HashMap, -} - -impl VersionInfo { - pub fn new( - raw_name: &str, - invalid_regex: Option<&str>, - include_regex: Option<&str>, - extra: HashMap, - ) -> Self { - let name = normalize_version(raw_name, invalid_regex, include_regex); - Self { name, extra } - } - - /// Compare using libversion. Returns Some(Ordering) if both are parseable. - pub fn compare(&self, other: &VersionInfo) -> Option { - let v1 = VersionUtil::new(self.name.clone()); - let v2 = VersionUtil::new(other.name.clone()); - v1.partial_cmp(&v2) - } -} - -impl PartialEq for VersionInfo { - fn eq(&self, other: &Self) -> bool { - self.name == other.name - } -} - -impl Eq for VersionInfo {} - -impl std::hash::Hash for VersionInfo { - fn hash(&self, state: &mut H) { - self.name.hash(state); - } -} - -impl PartialOrd for VersionInfo { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for VersionInfo { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.compare(other).unwrap_or(std::cmp::Ordering::Less) - } -} - -/// Strip unwanted parts from a version string using optional regex filters. -fn normalize_version( - raw: &str, - invalid_regex: Option<&str>, - include_regex: Option<&str>, -) -> String { - let mut result = raw.to_string(); - - if let Some(pattern) = invalid_regex { - if let Ok(re) = regex::Regex::new(pattern) { - result = re.replace_all(&result, "").to_string(); - } - } - - if let Some(pattern) = include_regex { - if let Ok(re) = regex::Regex::new(pattern) { - let matched: Vec<&str> = re.find_iter(&result).map(|m| m.as_str()).collect(); - result = matched.join(""); - } - } - - result.trim().to_string() -} - -/// A release from one hub, paired with its assets. -#[derive(Debug, Clone)] -pub struct VersionWrapper { - pub hub_uuid: String, - pub release: ReleaseData, - /// (release_index, asset_index) pairs - pub asset_indices: Vec<(usize, usize)>, -} - -/// Snapshot of a single version with all hub-provided wrappers. -#[derive(Debug, Clone)] -pub struct Version { - pub version_info: VersionInfo, - pub wrappers: Vec, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_version_info_compare() { - let v1 = VersionInfo::new("1.0.0", None, None, HashMap::new()); - let v2 = VersionInfo::new("1.0.1", None, None, HashMap::new()); - assert!(v1 < v2); - assert!(v2 > v1); - } - - #[test] - fn test_version_info_equal() { - let v1 = VersionInfo::new("1.0.0", None, None, HashMap::new()); - let v2 = VersionInfo::new("1.0.0", None, None, HashMap::new()); - assert_eq!(v1, v2); - } - - #[test] - fn test_normalize_version_invalid_regex() { - let name = normalize_version("v1.0.0", Some("^v"), None); - assert_eq!(name, "1.0.0"); - } - - #[test] - fn test_normalize_version_include_regex() { - let name = normalize_version("Release 1.0.0 (stable)", None, Some(r"\d+\.\d+\.\d+")); - assert_eq!(name, "1.0.0"); - } - - #[test] - fn test_normalize_version_both_filters() { - let name = normalize_version("v1.0.0-beta", Some(r"-beta"), Some(r"\d+\.\d+\.\d+")); - assert_eq!(name, "1.0.0"); - } -} diff --git a/src/manager/version_map.rs b/src/manager/version_map.rs deleted file mode 100644 index 9d275cb..0000000 --- a/src/manager/version_map.rs +++ /dev/null @@ -1,192 +0,0 @@ -use std::collections::HashMap; - -use super::version::{Version, VersionInfo, VersionWrapper}; -use crate::websdk::repo::data::release::ReleaseData; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum HubStatus { - Renewing, - Error, - /// Got latest release only (single entry per hub) - Single, - /// Got full release list - Full, -} - -/// In-memory version data for a single app, keyed by VersionInfo. -/// -/// Mirrors Kotlin's `VersionMap`. -#[derive(Debug)] -pub struct VersionMap { - pub invalid_version_regex: Option, - pub include_version_regex: Option, - /// Aggregated releases, grouped by normalized VersionInfo - entries: HashMap>, - pub hub_status: HashMap, - /// Cached sorted list, invalidated on mutation - sorted_cache: Option>, -} - -impl VersionMap { - pub fn new(invalid_regex: Option, include_regex: Option) -> Self { - Self { - invalid_version_regex: invalid_regex, - include_version_regex: include_regex, - entries: HashMap::new(), - hub_status: HashMap::new(), - sorted_cache: None, - } - } - - pub fn is_renewing(&self) -> bool { - self.hub_status.values().any(|s| *s == HubStatus::Renewing) - } - - pub fn mark_renewing(&mut self, hub_uuid: &str) { - self.hub_status - .insert(hub_uuid.to_string(), HubStatus::Renewing); - self.sorted_cache = None; - } - - pub fn set_error(&mut self, hub_uuid: &str) { - self.hub_status - .insert(hub_uuid.to_string(), HubStatus::Error); - } - - pub fn add_release_list(&mut self, hub_uuid: &str, releases: Vec) { - for (rel_idx, release) in releases.iter().enumerate() { - let info = self.make_version_info(&release.version_number); - let wrapper = VersionWrapper { - hub_uuid: hub_uuid.to_string(), - release: release.clone(), - asset_indices: (0..release.assets.len()).map(|i| (rel_idx, i)).collect(), - }; - self.entries.entry(info).or_default().push(wrapper); - } - self.hub_status - .insert(hub_uuid.to_string(), HubStatus::Full); - self.sorted_cache = None; - } - - pub fn add_single_release(&mut self, hub_uuid: &str, release: ReleaseData) { - let info = self.make_version_info(&release.version_number); - let wrapper = VersionWrapper { - hub_uuid: hub_uuid.to_string(), - asset_indices: (0..release.assets.len()).map(|i| (0, i)).collect(), - release, - }; - self.entries.entry(info).or_default().push(wrapper); - self.hub_status - .insert(hub_uuid.to_string(), HubStatus::Single); - self.sorted_cache = None; - } - - /// Returns versions sorted descending (newest first). - pub fn get_version_list(&mut self) -> &[Version] { - if self.sorted_cache.is_none() { - let mut versions: Vec = self - .entries - .iter() - .filter(|(info, _)| !info.name.is_empty()) - .map(|(info, wrappers)| Version { - version_info: info.clone(), - wrappers: wrappers.clone(), - }) - .collect(); - versions.sort_by(|a, b| b.version_info.cmp(&a.version_info)); - self.sorted_cache = Some(versions); - } - self.sorted_cache.as_deref().unwrap() - } - - fn make_version_info(&self, raw: &str) -> VersionInfo { - VersionInfo::new( - raw, - self.invalid_version_regex.as_deref(), - self.include_version_regex.as_deref(), - HashMap::new(), - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::websdk::repo::data::release::AssetData; - - fn make_release(version: &str) -> ReleaseData { - ReleaseData { - version_number: version.to_string(), - changelog: String::new(), - assets: vec![AssetData { - file_name: "app.apk".to_string(), - file_type: "apk".to_string(), - download_url: "https://example.com".to_string(), - }], - extra: None, - } - } - - #[test] - fn test_add_and_sort() { - let mut vm = VersionMap::new(None, None); - vm.add_release_list( - "hub1", - vec![ - make_release("1.0.0"), - make_release("2.0.0"), - make_release("1.5.0"), - ], - ); - let list = vm.get_version_list(); - assert_eq!(list[0].version_info.name, "2.0.0"); - assert_eq!(list[1].version_info.name, "1.5.0"); - assert_eq!(list[2].version_info.name, "1.0.0"); - } - - #[test] - fn test_single_release() { - let mut vm = VersionMap::new(None, None); - vm.add_single_release("hub1", make_release("3.0.0")); - let list = vm.get_version_list(); - assert_eq!(list.len(), 1); - assert_eq!(list[0].version_info.name, "3.0.0"); - assert_eq!(vm.hub_status["hub1"], HubStatus::Single); - } - - #[test] - fn test_hub_status_error() { - let mut vm = VersionMap::new(None, None); - vm.set_error("hub1"); - assert_eq!(vm.hub_status["hub1"], HubStatus::Error); - } - - #[test] - fn test_is_renewing() { - let mut vm = VersionMap::new(None, None); - assert!(!vm.is_renewing()); - vm.mark_renewing("hub1"); - assert!(vm.is_renewing()); - vm.set_error("hub1"); - assert!(!vm.is_renewing()); - } - - #[test] - fn test_dedup_versions_across_hubs() { - let mut vm = VersionMap::new(None, None); - vm.add_single_release("hub1", make_release("1.0.0")); - vm.add_single_release("hub2", make_release("1.0.0")); - // Same version from two hubs → merged under one VersionInfo key - let list = vm.get_version_list(); - assert_eq!(list.len(), 1); - assert_eq!(list[0].wrappers.len(), 2); - } - - #[test] - fn test_regex_filtering() { - let mut vm = VersionMap::new(Some("^v".to_string()), None); - vm.add_single_release("hub1", make_release("v1.2.3")); - let list = vm.get_version_list(); - assert_eq!(list[0].version_info.name, "1.2.3"); - } -} diff --git a/src/rpc.rs b/src/rpc.rs deleted file mode 100644 index 162790c..0000000 --- a/src/rpc.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod client; -pub mod data; -pub mod server; diff --git a/src/rpc/client.rs b/src/rpc/client.rs deleted file mode 100644 index 94c6091..0000000 --- a/src/rpc/client.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::websdk::repo::data::release::ReleaseData; - -use super::data::*; -use jsonrpsee::core::client::ClientT; -use jsonrpsee::core::client::Error; -use jsonrpsee::http_client::HttpClient; -use jsonrpsee::http_client::HttpClientBuilder; -use std::collections::BTreeMap; - -pub struct Client { - client: HttpClient, -} - -impl Client { - pub fn new(url: impl AsRef) -> Result { - let client = HttpClientBuilder::default().build(url)?; - Ok(Self { client }) - } - - pub async fn check_app_available( - &self, - hub_uuid: &str, - app_data: BTreeMap<&str, &str>, - hub_data: BTreeMap<&str, &str>, - ) -> Result { - let data = RpcAppRequest { - hub_uuid, - app_data, - hub_data, - }; - self.client.request("check_app_available", data).await - } - - pub async fn get_latest_release( - &self, - hub_uuid: &str, - app_data: BTreeMap<&str, &str>, - hub_data: BTreeMap<&str, &str>, - ) -> Result { - let data = RpcAppRequest { - hub_uuid, - app_data, - hub_data, - }; - self.client.request("get_latest_release", data).await - } - - pub async fn get_releases( - &self, - hub_uuid: &str, - app_data: BTreeMap<&str, &str>, - hub_data: BTreeMap<&str, &str>, - ) -> Result, Error> { - let data = RpcAppRequest { - hub_uuid, - app_data, - hub_data, - }; - self.client.request("get_releases", data).await - } - - pub async fn get_download( - &self, - hub_uuid: &str, - app_data: BTreeMap<&str, &str>, - hub_data: BTreeMap<&str, &str>, - asset_index: &[i32], - ) -> Result, Error> { - let data = RpcDownloadInfoRequest { - hub_uuid, - app_data, - hub_data, - asset_index: asset_index.to_vec(), - }; - self.client.request("get_download", data).await - } -} diff --git a/src/rpc/data.rs b/src/rpc/data.rs deleted file mode 100644 index 77f3a88..0000000 --- a/src/rpc/data.rs +++ /dev/null @@ -1,411 +0,0 @@ -use crate::downloader::{DownloadState, TaskInfo}; -use jsonrpsee::core::traits::ToRpcParams; -use serde::{Deserialize, Serialize}; -use serde_json::value::to_raw_value; -use std::collections::{BTreeMap, HashMap}; - -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcInitRequest<'a> { - pub data_path: &'a str, - pub cache_path: &'a str, - pub global_expire_time: u64, -} - -impl ToRpcParams for RpcInitRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcAppRequest<'a> { - pub hub_uuid: &'a str, - pub app_data: BTreeMap<&'a str, &'a str>, - pub hub_data: BTreeMap<&'a str, &'a str>, -} - -impl ToRpcParams for RpcAppRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcCloudConfigRequest<'a> { - pub api_url: &'a str, -} - -impl ToRpcParams for RpcCloudConfigRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -// ============================================================================ -// Provider Registration RPC Data Structures -// ============================================================================ - -/// Request to register an external provider -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcRegisterProviderRequest<'a> { - pub hub_uuid: &'a str, - pub url: &'a str, -} - -impl ToRpcParams for RpcRegisterProviderRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to get download info for an app -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDownloadInfoRequest<'a> { - pub hub_uuid: &'a str, - pub app_data: BTreeMap<&'a str, &'a str>, - pub hub_data: BTreeMap<&'a str, &'a str>, - pub asset_index: Vec, -} - -impl ToRpcParams for RpcDownloadInfoRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Download item data returned by get_download -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DownloadItemData { - pub name: Option, - pub url: String, - #[serde(default)] - pub headers: Option>, - #[serde(default)] - pub cookies: Option>, -} - -// ============================================================================ -// Downloader RPC Data Structures -// ============================================================================ - -/// Request to submit a download task -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDownloadRequest<'a> { - pub url: &'a str, - pub dest_path: &'a str, - #[serde(default)] - pub headers: Option>, - #[serde(default)] - pub cookies: Option>, - /// Hub UUID for routing to registered external downloaders. - /// When set, routes to external downloader; when None, uses default HTTP downloader. - #[serde(default)] - pub hub_uuid: Option, -} - -impl ToRpcParams for RpcDownloadRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to submit multiple download tasks -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDownloadBatchRequest { - pub tasks: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDownloadTask { - pub url: String, - pub dest_path: String, -} - -impl ToRpcParams for RpcDownloadBatchRequest { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Response with task ID -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct RpcTaskIdResponse { - pub task_id: String, -} - -/// Response with multiple task IDs -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct RpcTaskIdsResponse { - pub task_ids: Vec, -} - -/// Request to query task status -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcTaskStatusRequest<'a> { - pub task_id: &'a str, -} - -impl ToRpcParams for RpcTaskStatusRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to wait for task state change (long-polling) -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcWaitForChangeRequest<'a> { - pub task_id: &'a str, - pub timeout_seconds: u64, -} - -impl ToRpcParams for RpcWaitForChangeRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to cancel a task -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcCancelTaskRequest<'a> { - pub task_id: &'a str, -} - -impl ToRpcParams for RpcCancelTaskRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to pause a task -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcPauseTaskRequest<'a> { - pub task_id: &'a str, -} - -impl ToRpcParams for RpcPauseTaskRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to resume a task -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcResumeTaskRequest<'a> { - pub task_id: &'a str, -} - -impl ToRpcParams for RpcResumeTaskRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to query tasks by state -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcTasksByStateRequest { - pub state: DownloadState, -} - -impl ToRpcParams for RpcTasksByStateRequest { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Response with task information -pub type RpcTaskInfoResponse = TaskInfo; - -/// Response with multiple tasks -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct RpcTasksResponse { - pub tasks: Vec, -} - -// ============================================================================ -// Downloader Registration RPC Data Structures -// ============================================================================ - -/// Request to register an external downloader for a hub_uuid -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcRegisterDownloaderRequest<'a> { - pub hub_uuid: &'a str, - pub rpc_url: &'a str, -} - -impl ToRpcParams for RpcRegisterDownloaderRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -/// Request to unregister an external downloader for a hub_uuid -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcUnregisterDownloaderRequest<'a> { - pub hub_uuid: &'a str, -} - -impl ToRpcParams for RpcUnregisterDownloaderRequest<'_> { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} - -// ============================================================================ -// Manager RPC Data Structures -// ============================================================================ - -use crate::database::models::app::AppRecord; -use crate::database::models::extra_hub::ExtraHubRecord; -use crate::database::models::hub::HubRecord; -use crate::manager::app_status::AppStatus; - -/// Response wrapping a list of apps with their current status. -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct AppWithStatus { - pub record: AppRecord, - pub status: AppStatus, -} - -/// Request to save/update an app record. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcSaveAppRequest { - pub record: AppRecord, -} - -/// Request to delete an app by record id. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDeleteAppRequest { - pub record_id: String, -} - -/// Request to get a single app by record id. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcGetAppRequest { - pub record_id: String, -} - -/// Request to save/update a hub record. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcSaveHubRequest { - pub record: HubRecord, -} - -/// Request to delete a hub by UUID. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDeleteHubRequest { - pub hub_uuid: String, -} - -/// Request to get a hub by UUID. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcGetHubRequest { - pub hub_uuid: String, -} - -/// Request to set the applications mode for a hub. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcSetApplicationsModeRequest { - pub hub_uuid: String, - pub enable: bool, -} - -/// Request to ignore/unignore an app in a hub. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcHubIgnoreAppRequest { - pub hub_uuid: String, - pub app_id: HashMap>, - pub ignore: bool, -} - -/// Request to set virtual (installed) apps list from Android. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcSetVirtualAppsRequest { - pub apps: Vec, -} - -/// Request to get app status. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcGetAppStatusRequest { - pub record_id: String, -} - -/// Request to save/update an ExtraHub record. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcSaveExtraHubRequest { - pub record: ExtraHubRecord, -} - -/// Request to get an ExtraHub by id. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcGetExtraHubRequest { - pub id: String, -} - -// ============================================================================ -// Android API / Notification Registration RPC Data Structures -// ============================================================================ - -/// Request to register the Kotlin Android API callback URL. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcRegisterAndroidApiRequest { - pub url: String, -} - -/// Request to register the Kotlin notification callback URL. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcRegisterNotificationRequest { - pub url: String, -} - -// ============================================================================ -// ExtraApp RPC Data Structures -// ============================================================================ - -use crate::database::models::extra_app::ExtraAppRecord; - -/// Request to get an ExtraApp record by app_id map. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcGetExtraAppRequest { - pub app_id: HashMap>, -} - -/// Request to save/update an ExtraApp record. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcSaveExtraAppRequest { - pub record: ExtraAppRecord, -} - -/// Request to delete an ExtraApp by database id. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcDeleteExtraAppRequest { - pub id: String, -} - -// ============================================================================ -// Cloud Config Manager RPC Data Structures -// ============================================================================ - -/// Request to apply a specific cloud hub/app config by UUID. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcCloudConfigApplyRequest { - pub uuid: String, -} - -/// Request to initialise the CloudConfigGetter with an API URL. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcCloudConfigInitRequest { - pub api_url: String, -} - -/// Request to update the auth map for a hub. -#[derive(Serialize, Deserialize, Debug)] -pub struct RpcUpdateHubAuthRequest { - pub hub_uuid: String, - pub auth: HashMap, -} - -impl ToRpcParams for RpcUpdateHubAuthRequest { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - to_raw_value(&self).map(Some) - } -} diff --git a/src/rpc/server.rs b/src/rpc/server.rs deleted file mode 100644 index 6e1e54f..0000000 --- a/src/rpc/server.rs +++ /dev/null @@ -1,1255 +0,0 @@ -use super::data::*; -use crate::cache::init_cache_manager_with_expire; -use crate::core::config::world::{init_world_list, world_list}; -use crate::database::get_db; -use crate::database::models::extra_hub::GLOBAL_HUB_ID; -use crate::downloader::{DownloadConfig, DownloadTaskManager}; -use crate::manager::android_api; -use crate::manager::app_manager::AppManager; -use crate::manager::cloud_config_getter::CloudConfigGetter; -use crate::manager::hub_manager::HubManager; -use crate::manager::notification; -use crate::manager::url_replace::apply_url_replace; -use crate::websdk::cloud_rules::cloud_rules_manager::CloudRules; -use crate::websdk::repo::api; -use jsonrpsee::server::{RpcModule, Server, ServerConfig, ServerHandle}; -use jsonrpsee::types::{ErrorCode, ErrorObjectOwned}; -use once_cell::sync::OnceCell; -use std::net::SocketAddr; -use std::path::Path; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; -use std::time::Duration; -use tokio::sync::RwLock; - -/// Global manager state initialised on first `init` RPC call. -static APP_MANAGER: OnceCell>> = OnceCell::new(); -static HUB_MANAGER: OnceCell>> = OnceCell::new(); -static CLOUD_CONFIG_GETTER: OnceCell>> = OnceCell::new(); - -fn get_app_manager() -> Option>> { - APP_MANAGER.get().cloned() -} - -fn get_hub_manager() -> Option>> { - HUB_MANAGER.get().cloned() -} - -fn get_cloud_config_getter() -> Option>> { - CLOUD_CONFIG_GETTER.get().cloned() -} - -fn manager_not_init_err() -> ErrorObjectOwned { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Manager not initialized. Call init first.", - None::, - ) -} - -fn map_manager_err(e: impl std::fmt::Display) -> ErrorObjectOwned { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - e.to_string(), - None::, - ) -} - -// Default 2GB size limit for WebSocket messages -// Can be overridden at runtime by setting GETTER_WS_MAX_MESSAGE_SIZE environment variable -// Example: GETTER_WS_MAX_MESSAGE_SIZE=1073741824 ./getter (for 1GB) -const DEFAULT_MAX_SIZE: u32 = 2 * 1024 * 1024 * 1024; // 2GB - -fn get_max_message_size() -> u32 { - // Allow runtime configuration via environment variable - match std::env::var("GETTER_WS_MAX_MESSAGE_SIZE") { - Ok(size_str) => size_str.parse().unwrap_or(DEFAULT_MAX_SIZE), - Err(_) => DEFAULT_MAX_SIZE, - } -} - -pub async fn run_server( - addr: &str, - is_running: Arc, -) -> Result<(String, ServerHandle), Box> { - let addr = if addr.is_empty() { "127.0.0.1:0" } else { addr }; - let max_size = get_max_message_size(); - let config = ServerConfig::builder() - .max_request_body_size(max_size) - .max_response_body_size(max_size) - .build(); - let server = Server::builder() - .set_config(config) - .build(addr.parse::()?) - .await?; - let mut module = RpcModule::new(()); - // Register the shutdown method - let run_flag = is_running.clone(); - module.register_async_method("shutdown", move |_, _, _| { - let flag = run_flag.clone(); - async move { - flag.store(false, Ordering::SeqCst); - } - })?; - module.register_method("ping", |_, _, _| "pong")?; - module.register_async_method("init", |params, _, _| async move { - let request = params.parse::()?; - let data_dir = Path::new(request.data_path); - let cache_dir = Path::new(request.cache_path); - // Initialize world list, cache, and database. - let world_list_path = data_dir.join(world_list::WORLD_CONFIG_LIST_NAME); - init_world_list(&world_list_path).await.map_err(|e| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Internal error", - Some(e.to_string()), - ) - })?; - let local_cache_path = cache_dir.join("local_cache"); - init_cache_manager_with_expire(local_cache_path.as_path(), request.global_expire_time) - .await; - crate::database::init_db(data_dir).map_err(|e| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Internal error", - Some(e.to_string()), - ) - })?; - - // Initialize managers (idempotent: only on first call) - if APP_MANAGER.get().is_none() { - let hub_mgr = HubManager::load().map_err(map_manager_err)?; - let app_mgr = AppManager::load().map_err(map_manager_err)?; - let _ = HUB_MANAGER.set(Arc::new(RwLock::new(hub_mgr))); - let _ = APP_MANAGER.set(Arc::new(RwLock::new(app_mgr))); - } - - Ok::(true) - })?; - module.register_async_method( - "check_app_available", - |params, _context, _extensions| async move { - let request = params.parse::()?; - let result = - api::check_app_available(request.hub_uuid, &request.app_data, &request.hub_data) - .await - .unwrap_or(false); - Ok::(result) - }, - )?; - module.register_async_method( - "get_latest_release", - |params, _context, _extensions| async move { - let request = params.parse::()?; - if let Some(result) = - api::get_latest_release(request.hub_uuid, &request.app_data, &request.hub_data) - .await - { - Ok(result) - } else { - Err(ErrorObjectOwned::owned( - -32001, - "No release found", - None::, - )) - } - }, - )?; - module.register_async_method("get_releases", |params, _context, _extensions| async move { - let request = params.parse::()?; - if let Some(result) = - api::get_releases(request.hub_uuid, &request.app_data, &request.hub_data).await - { - Ok(result) - } else { - Err(ErrorObjectOwned::owned( - -32001, - "No releases found", - None::, - )) - } - })?; - - // register_provider: Dynamically register an external provider (e.g., Kotlin hub via HTTP JSON-RPC) - module.register_async_method( - "register_provider", - |params, _context, _extensions| async move { - let request = params.parse::()?; - api::add_outside_provider(request.hub_uuid, request.url); - Ok::(true) - }, - )?; - - // get_download: Get download info for an app's asset. - // After retrieving download URLs from the provider, applies URL replacement - // rules from ExtraHub configs (GLOBAL first, then hub-specific), mirroring - // Kotlin's URLReplace.replaceURL() in the download pipeline. - module.register_async_method("get_download", |params, _context, _extensions| async move { - let request = params.parse::()?; - let mut items = api::get_download( - request.hub_uuid, - &request.app_data, - &request.hub_data, - &request.asset_index, - ) - .await - .ok_or_else(|| ErrorObjectOwned::owned(-32001, "No download info found", None::))?; - - // Load URL-replace rules from ExtraHub configs. - // Priority: hub-specific rule overrides GLOBAL rule. - let db = get_db(); - let global_extra = db.find_extra_hub(GLOBAL_HUB_ID).unwrap_or(None); - let hub_extra = db.find_extra_hub(request.hub_uuid).unwrap_or(None); - - // Apply rules to every download URL in the result. - for item in &mut items { - // Apply GLOBAL rule first (lower priority) - if let Some(ref g) = global_extra { - item.url = apply_url_replace( - &item.url, - g.url_replace_search.as_deref(), - g.url_replace_string.as_deref(), - ); - } - // Apply hub-specific rule second (higher priority, may override) - if let Some(ref h) = hub_extra { - item.url = apply_url_replace( - &item.url, - h.url_replace_search.as_deref(), - h.url_replace_string.as_deref(), - ); - } - } - - Ok::, ErrorObjectOwned>(items) - })?; - - module.register_async_method( - "get_cloud_config", - |params, _context, _extensions| async move { - if let Ok(request) = params.parse::() { - let mut cloud_rules = CloudRules::new(request.api_url); - if let Err(e) = cloud_rules.renew().await { - return Err(ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Download cloud config failed", - Some(e.to_string()), - )); - } - Ok(cloud_rules.get_config_list().to_owned()) - } else { - Err(ErrorObjectOwned::owned( - ErrorCode::ParseError.code(), - "Parse params error", - Some(params.as_str().unwrap_or("None").to_string()), - )) - } - }, - )?; - - // ======================================================================== - // Downloader RPC Methods - // ======================================================================== - - // Create download task manager with HubDispatchDownloader - let download_config = DownloadConfig::from_env(); - let http_downloader = crate::downloader::create_downloader(&download_config); - let dispatcher = crate::downloader::HubDispatchDownloader::new(http_downloader); - - // Clone dispatcher for task manager (HubDispatchDownloader is cheap to clone via Arc internally) - let task_manager = Arc::new(DownloadTaskManager::new(Box::new(dispatcher.clone()))); - - // download_submit: Submit a single download task - let manager_clone = task_manager.clone(); - module.register_async_method("download_submit", move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - match manager.submit_task_with_options( - request.url, - request.dest_path, - request.headers, - request.cookies, - request.hub_uuid, - ) { - Ok(task_id) => Ok(RpcTaskIdResponse { task_id }), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Failed to submit download task", - Some(e.message), - )), - } - } - })?; - - // download_submit_batch: Submit multiple download tasks - let manager_clone = task_manager.clone(); - module.register_async_method( - "download_submit_batch", - move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - let tasks: Vec<(String, String)> = request - .tasks - .into_iter() - .map(|t| (t.url, t.dest_path)) - .collect(); - - match manager.submit_batch(tasks) { - Ok(task_ids) => Ok(RpcTaskIdsResponse { task_ids }), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Failed to submit batch download tasks", - Some(e.message), - )), - } - } - }, - )?; - - // download_get_status: Get status of a download task - let manager_clone = task_manager.clone(); - module.register_async_method( - "download_get_status", - move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - match manager.get_task(request.task_id) { - Ok(task_info) => Ok(task_info), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InvalidParams.code(), - "Task not found", - Some(e.message), - )), - } - } - }, - )?; - - // download_wait_for_change: Long-polling for task state change - let manager_clone = task_manager.clone(); - module.register_async_method( - "download_wait_for_change", - move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - let timeout = Duration::from_secs(request.timeout_seconds); - - match manager.wait_for_change(request.task_id, timeout).await { - Ok(task_info) => Ok(task_info), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InvalidParams.code(), - "Failed to wait for task change", - Some(e.message), - )), - } - } - }, - )?; - - // download_cancel: Cancel a download task - let manager_clone = task_manager.clone(); - module.register_async_method("download_cancel", move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - match manager.cancel_task(request.task_id) { - Ok(_) => Ok(true), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Failed to cancel task", - Some(e.message), - )), - } - } - })?; - - // download_pause: Pause a download task - let manager_clone = task_manager.clone(); - module.register_async_method("download_pause", move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - match manager.pause_task(request.task_id).await { - Ok(_) => Ok(true), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Failed to pause task", - Some(e.message), - )), - } - } - })?; - - // download_resume: Resume a paused download task - let manager_clone = task_manager.clone(); - module.register_async_method("download_resume", move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - match manager.resume_task(request.task_id).await { - Ok(_) => Ok(true), - Err(e) => Err(ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "Failed to resume task", - Some(e.message), - )), - } - } - })?; - - // download_get_capabilities: Get downloader capabilities - let manager_clone = task_manager.clone(); - module.register_method( - "download_get_capabilities", - move |_, _context, _extensions| { - let caps = manager_clone.get_capabilities(); - Ok::<_, ErrorObjectOwned>(caps.clone()) - }, - )?; - - // download_get_all_tasks: Get all tasks - let manager_clone = task_manager.clone(); - module.register_async_method("download_get_all_tasks", move |_, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - Ok::(RpcTasksResponse { - tasks: manager.get_all_tasks(), - }) - } - })?; - - // download_get_active_tasks: Get active tasks - let manager_clone = task_manager.clone(); - module.register_async_method( - "download_get_active_tasks", - move |_, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - Ok::(RpcTasksResponse { - tasks: manager.get_active_tasks(), - }) - } - }, - )?; - - // download_get_tasks_by_state: Get tasks by state - let manager_clone = task_manager.clone(); - module.register_async_method( - "download_get_tasks_by_state", - move |params, _context, _extensions| { - let manager = manager_clone.clone(); - async move { - let request = params.parse::()?; - Ok::(RpcTasksResponse { - tasks: manager.get_tasks_by_state(request.state), - }) - } - }, - )?; - - // register_downloader: Register an external downloader for a hub_uuid - let dispatcher_clone = dispatcher.clone(); - module.register_async_method( - "register_downloader", - move |params, _context, _extensions| { - let dispatcher = dispatcher_clone.clone(); - async move { - let request = params.parse::()?; - let external_downloader = Box::new(crate::downloader::ExternalRpcDownloader::new( - request.rpc_url.to_string(), - )); - dispatcher.register(request.hub_uuid, external_downloader); - Ok::(true) - } - }, - )?; - - // unregister_downloader: Unregister an external downloader for a hub_uuid - let dispatcher_clone = dispatcher.clone(); - module.register_async_method( - "unregister_downloader", - move |params, _context, _extensions| { - let dispatcher = dispatcher_clone.clone(); - async move { - let request = params.parse::()?; - dispatcher.unregister(request.hub_uuid); - Ok::(true) - } - }, - )?; - - // ======================================================================== - // App Manager RPC Methods - // ======================================================================== - - // manager_get_apps: Get all saved apps - module.register_async_method("manager_get_apps", |_, _, _| async move { - let mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let apps = mgr.read().await.get_saved_apps().await; - Ok::, ErrorObjectOwned>(apps) - })?; - - // manager_save_app: Insert or update an app record - module.register_async_method("manager_save_app", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let saved = mgr - .write() - .await - .save_app(request.record) - .await - .map_err(map_manager_err)?; - Ok::(saved) - })?; - - // manager_delete_app: Delete an app by record id - module.register_async_method("manager_delete_app", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let deleted = mgr - .write() - .await - .remove_app(&request.record_id) - .await - .map_err(map_manager_err)?; - Ok::(deleted) - })?; - - // manager_get_app_status: Get AppStatus for a specific app - module.register_async_method("manager_get_app_status", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let status = mgr.write().await.get_app_status(&request.record_id).await; - Ok::(status) - })?; - - // manager_set_virtual_apps: Set installed (virtual) apps from Android - module.register_async_method("manager_set_virtual_apps", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - mgr.read().await.set_virtual_apps(request.apps).await; - Ok::(true) - })?; - - // manager_renew_all: Trigger a full update check for all apps - module.register_async_method("manager_renew_all", |_, _, _| async move { - let app_mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let hub_mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let hubs = hub_mgr.read().await.get_hub_list().await; - app_mgr.read().await.renew_all(&hubs, None).await; - Ok::(true) - })?; - - // manager_check_invalid_applications: Return record IDs of apps whose configured - // hub UUIDs are all unknown (no valid hub found). Mirrors Kotlin's - // AppManager.check_invalid_applications logic. - module.register_async_method("manager_check_invalid_applications", |_, _, _| async move { - let app_mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let hub_mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let hubs = hub_mgr.read().await.get_hub_list().await; - let known_uuids: Vec = hubs.into_iter().map(|h| h.uuid).collect(); - let mgr = app_mgr.read().await; - let invalid_ids = mgr.check_invalid_applications(&known_uuids).await; - // Notify Kotlin UI about each invalid app so it can update status. - for record_id in &invalid_ids { - if let Some(app) = mgr.get_app(record_id).await { - notification::notify_if_registered(notification::ManagerEvent::AppStatusChanged { - record_id: record_id.clone(), - app_id: app.app_id.clone(), - old_status: crate::manager::app_status::AppStatus::AppLatest, - new_status: crate::manager::app_status::AppStatus::AppInactive, - }) - .await; - } - } - Ok::, ErrorObjectOwned>(invalid_ids) - })?; - - // ======================================================================== - // Hub Manager RPC Methods - // ======================================================================== - - // manager_get_hubs: Get all hubs - module.register_async_method("manager_get_hubs", |_, _, _| async move { - let mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let hubs = mgr.read().await.get_hub_list().await; - Ok::, ErrorObjectOwned>(hubs) - })?; - - // manager_save_hub: Insert or update a hub - module.register_async_method("manager_save_hub", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - mgr.write() - .await - .upsert_hub(request.record) - .await - .map_err(map_manager_err)?; - Ok::(true) - })?; - - // manager_update_hub_auth: Replace the auth map for a hub and persist. - module.register_async_method("manager_update_hub_auth", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let updated = mgr - .read() - .await - .update_auth(&request.hub_uuid, request.auth) - .await - .map_err(map_manager_err)?; - Ok::(updated) - })?; - - // manager_delete_hub: Delete a hub by UUID - module.register_async_method("manager_delete_hub", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let deleted = mgr - .write() - .await - .remove_hub(&request.hub_uuid) - .await - .map_err(map_manager_err)?; - Ok::(deleted) - })?; - - // manager_hub_ignore_app: Add or remove an app from a hub's ignore list - module.register_async_method("manager_hub_ignore_app", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let mut guard = mgr.write().await; - let mut hub = guard.get_hub(&request.hub_uuid).await.ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InvalidParams.code(), - "Hub not found", - None::, - ) - })?; - if request.ignore { - if !hub.user_ignore_app_id_list.contains(&request.app_id) { - hub.user_ignore_app_id_list.push(request.app_id); - } - } else { - hub.user_ignore_app_id_list - .retain(|id| id != &request.app_id); - } - guard.upsert_hub(hub).await.map_err(map_manager_err)?; - Ok::(true) - })?; - - // manager_set_applications_mode: Enable/disable auto app discovery for a hub - module.register_async_method("manager_set_applications_mode", |params, _, _| async move { - let request = params.parse::()?; - let mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - let mut guard = mgr.write().await; - let mut hub = guard.get_hub(&request.hub_uuid).await.ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InvalidParams.code(), - "Hub not found", - None::, - ) - })?; - hub.applications_mode = if request.enable { 1 } else { 0 }; - guard.upsert_hub(hub).await.map_err(map_manager_err)?; - Ok::(true) - })?; - - // ======================================================================== - // ExtraHub RPC Methods - // ======================================================================== - - // manager_get_extra_hubs: Get all extra hub configs - module.register_async_method("manager_get_extra_hubs", |_, _, _| async move { - let extra_hubs = crate::database::get_db() - .load_extra_hubs() - .map_err(map_manager_err)?; - Ok::, ErrorObjectOwned>(extra_hubs) - })?; - - // manager_save_extra_hub: Insert or update an extra hub config - module.register_async_method("manager_save_extra_hub", |params, _, _| async move { - let request = params.parse::()?; - crate::database::get_db() - .upsert_extra_hub(&request.record) - .map_err(map_manager_err)?; - Ok::(true) - })?; - - // manager_delete_extra_hub: Delete an extra hub by id - module.register_async_method("manager_delete_extra_hub", |params, _, _| async move { - let request = params.parse::()?; - let deleted = crate::database::get_db() - .delete_extra_hub(&request.id) - .map_err(map_manager_err)?; - Ok::(deleted) - })?; - - // ======================================================================== - // Android API / Notification Registration RPC Methods - // ======================================================================== - - // register_android_api: Register Kotlin's Android API callback URL - module.register_async_method("register_android_api", |params, _, _| async move { - let request = params.parse::()?; - android_api::set_android_api(request.url); - Ok::(true) - })?; - - // register_notification: Register Kotlin's notification callback URL - module.register_async_method("register_notification", |params, _, _| async move { - let request = params.parse::()?; - notification::set_notification(request.url); - Ok::(true) - })?; - - // ======================================================================== - // ExtraApp RPC Methods - // ======================================================================== - - // manager_get_extra_app_by_app_id: Get ExtraApp record by app_id map - module.register_async_method( - "manager_get_extra_app_by_app_id", - |params, _, _| async move { - let request = params.parse::()?; - let record = crate::database::get_db() - .get_extra_app_by_app_id(&request.app_id) - .map_err(map_manager_err)?; - Ok::, ErrorObjectOwned>( - record, - ) - }, - )?; - - // manager_save_extra_app: Insert or update an ExtraApp record - module.register_async_method("manager_save_extra_app", |params, _, _| async move { - let request = params.parse::()?; - crate::database::get_db() - .upsert_extra_app(&request.record) - .map_err(map_manager_err)?; - Ok::(true) - })?; - - // manager_delete_extra_app: Delete an ExtraApp by database id - module.register_async_method("manager_delete_extra_app", |params, _, _| async move { - let request = params.parse::()?; - let deleted = crate::database::get_db() - .delete_extra_app(&request.id) - .map_err(map_manager_err)?; - Ok::(deleted) - })?; - - // ======================================================================== - // Cloud Config Manager RPC Methods - // ======================================================================== - - // cloud_config_init: Initialise or re-initialise the CloudConfigGetter with an API URL - module.register_async_method("cloud_config_init", |params, _, _| async move { - let request = params.parse::()?; - let getter = CloudConfigGetter::new(request.api_url); - let _ = CLOUD_CONFIG_GETTER.set(Arc::new(RwLock::new(getter))); - Ok::(true) - })?; - - // cloud_config_renew: Download and cache the latest cloud config - module.register_async_method("cloud_config_renew", |_, _, _| async move { - let getter = get_cloud_config_getter().ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "CloudConfigGetter not initialised. Call cloud_config_init first.", - None::, - ) - })?; - getter.read().await.renew().await.map_err(map_manager_err)?; - Ok::(true) - })?; - - // cloud_config_get_app_list: Return all available app configs from cache - module.register_async_method("cloud_config_get_app_list", |_, _, _| async move { - let getter = get_cloud_config_getter().ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "CloudConfigGetter not initialised.", - None::, - ) - })?; - let list = getter.read().await.app_config_list().await; - Ok::, ErrorObjectOwned>(list) - })?; - - // cloud_config_get_hub_list: Return all available hub configs from cache - module.register_async_method("cloud_config_get_hub_list", |_, _, _| async move { - let getter = get_cloud_config_getter().ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "CloudConfigGetter not initialised.", - None::, - ) - })?; - let list = getter.read().await.hub_config_list().await; - Ok::, ErrorObjectOwned>(list) - })?; - - // cloud_config_apply_app: Apply a cloud app config by UUID - module.register_async_method("cloud_config_apply_app", |params, _, _| async move { - let request = params.parse::()?; - let getter = get_cloud_config_getter().ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "CloudConfigGetter not initialised.", - None::, - ) - })?; - let app_mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let hub_mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - getter - .read() - .await - .apply_app_config( - &request.uuid, - &mut *app_mgr.write().await, - &mut *hub_mgr.write().await, - ) - .await - .map_err(map_manager_err)?; - Ok::(true) - })?; - - // cloud_config_apply_hub: Apply a cloud hub config by UUID - module.register_async_method("cloud_config_apply_hub", |params, _, _| async move { - let request = params.parse::()?; - let getter = get_cloud_config_getter().ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "CloudConfigGetter not initialised.", - None::, - ) - })?; - let hub_mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - getter - .read() - .await - .apply_hub_config(&request.uuid, &mut *hub_mgr.write().await) - .await - .map_err(map_manager_err)?; - Ok::(true) - })?; - - // cloud_config_renew_all: Bulk-update all installed apps/hubs from cloud - module.register_async_method("cloud_config_renew_all", |_, _, _| async move { - let getter = get_cloud_config_getter().ok_or_else(|| { - ErrorObjectOwned::owned( - ErrorCode::InternalError.code(), - "CloudConfigGetter not initialised.", - None::, - ) - })?; - let app_mgr = get_app_manager().ok_or_else(manager_not_init_err)?; - let hub_mgr = get_hub_manager().ok_or_else(manager_not_init_err)?; - getter - .read() - .await - .renew_all_from_cloud(&mut *app_mgr.write().await, &mut *hub_mgr.write().await) - .await - .map_err(map_manager_err)?; - Ok::(true) - })?; - - let addr = server.local_addr()?; - let handle = server.start(module); - tokio::spawn(handle.clone().stopped()); - Ok((format!("http://{}", addr), handle)) -} - -#[allow(dead_code)] -pub async fn run_server_hanging( - addr: &str, - callback: impl Fn(&str) -> Result>, -) -> Result> { - let is_running = Arc::new(AtomicBool::new(true)); - let (url, handle) = match run_server(addr, is_running.clone()).await { - Ok((url, handle)) => (url, handle), - Err(e) => { - eprintln!("Failed to start server: {}", e); - return Err(e); - } - }; - let result = callback(&url)?; - while is_running.load(Ordering::SeqCst) { - tokio::time::sleep(Duration::from_secs(1)).await; - } - handle.stop()?; - Ok(result) -} - -#[cfg(test)] -mod tests { - use crate::rpc::client::Client; - use crate::websdk::repo::provider::github; - use crate::websdk::{ - cloud_rules::data::config_list::ConfigList, repo::data::release::ReleaseData, - }; - - use super::*; - use jsonrpsee::{core::client::ClientT, http_client::HttpClientBuilder, rpc_params}; - use mockito::Server; - use std::collections::BTreeMap; - use std::fs; - use tokio::time::timeout; - - #[tokio::test] - async fn test_server_start() { - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - assert!(url.starts_with("http://")); - assert!(url.split(":").last().unwrap().parse::().unwrap() > 0); - handle.stop().unwrap(); - let port = 33333; - let addr = format!("127.0.0.1:{}", port); - let (url, handle) = run_server(&addr, Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - assert!(url.starts_with("http://")); - assert!(url.split(":").last().unwrap().parse::().unwrap() == port); - handle.stop().unwrap(); - } - - #[tokio::test] - async fn test_ping() { - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - let client = HttpClientBuilder::default().build(url).unwrap(); - let response: Result = client.request("ping", rpc_params![]).await; - assert_eq!(response.unwrap(), "pong"); - handle.stop().unwrap(); - } - - #[tokio::test] - async fn test_init() { - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/DUpdateSystem/UpgradeAll") - .with_status(200) - .create_async() - .await; - - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - let client = HttpClientBuilder::default().build(url).unwrap(); - let temp_dir = tempfile::tempdir().unwrap(); - let temp_dir_path = temp_dir.path().to_str().unwrap(); - let params = RpcInitRequest { - data_path: &format!("{}/data", temp_dir_path), - cache_path: &format!("{}/cache", temp_dir_path), - global_expire_time: 3600, - }; - println!("{:?}", params); - let response: Result = client.request("init", params).await; - assert!(response.unwrap()); - handle.stop().unwrap(); - } - #[tokio::test] - async fn test_check_app_available() { - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/DUpdateSystem/UpgradeAll") - .with_status(200) - .create_async() - .await; - - let id_map = BTreeMap::from([("owner", "DUpdateSystem"), ("repo", "UpgradeAll")]); - let proxy_url = format!("{} -> {}", github::GITHUB_API_URL, server.url()); - let hub_data = BTreeMap::from([("reverse_proxy", proxy_url.as_str())]); - - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - let params = RpcAppRequest { - hub_uuid: "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - app_data: id_map, - hub_data, - }; - println!("{:?}", params); - let client = Client::new(url).unwrap(); - let response: Result = client - .check_app_available(params.hub_uuid, params.app_data, params.hub_data) - .await; - assert!(response.unwrap()); - handle.stop().unwrap(); - } - - #[tokio::test] - async fn test_get_latest_release() { - let body = fs::read_to_string("tests/files/web/github_api_release.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/repos/DUpdateSystem/UpgradeAll/releases") - .with_status(200) - .with_body(body) - .create(); - - let id_map = BTreeMap::from([("owner", "DUpdateSystem"), ("repo", "UpgradeAll")]); - let proxy_url = format!("{} -> {}", github::GITHUB_API_URL, server.url()); - let hub_data = BTreeMap::from([("reverse_proxy", proxy_url.as_str())]); - - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - let params = RpcAppRequest { - hub_uuid: "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - app_data: id_map, - hub_data, - }; - println!("{:?}", params); - let client = Client::new(url).unwrap(); - let response: Result = client - .get_latest_release(params.hub_uuid, params.app_data, params.hub_data) - .await; - let release = response.unwrap(); - assert!(!release.version_number.is_empty()); - handle.stop().unwrap(); - } - - #[tokio::test] - async fn test_get_releases() { - let body = fs::read_to_string("tests/files/web/github_api_release.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/repos/DUpdateSystem/UpgradeAll/releases") - .with_status(200) - .with_body(body) - .create(); - - let id_map = BTreeMap::from([("owner", "DUpdateSystem"), ("repo", "UpgradeAll")]); - let proxy_url = format!("{} -> {}", github::GITHUB_API_URL, server.url()); - let hub_data = BTreeMap::from([("reverse_proxy", proxy_url.as_str())]); - - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - let params = RpcAppRequest { - hub_uuid: "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - app_data: id_map, - hub_data, - }; - println!("{:?}", params); - let client = Client::new(url).unwrap(); - let response: Result, _> = client - .get_releases(params.hub_uuid, params.app_data, params.hub_data) - .await; - let releases = response.unwrap(); - assert!(!releases.is_empty()); - handle.stop().unwrap(); - } - - #[tokio::test] - async fn test_run_server_hanging() { - let addr = "127.0.0.1:33334"; - let server_task = tokio::spawn(async move { - // This should run the server and wait for the shutdown command - run_server_hanging(addr, |url| { - println!("Server started at {}", url); - Ok(()) - }) - .await - .expect("Server failed to run"); - }); - - // Allow some time for the server to start up - tokio::time::sleep(Duration::from_millis(500)).await; - - // The callback should print the URL, but since we cannot capture that output easily in a test, - // we assume the server starts correctly if no error happens till now. - // Here, manually create a client and send a shutdown request - let client = HttpClientBuilder::default() - .build(format!("http://{}", addr)) - .expect("Failed to build client"); - - let response: Result<(), _> = client.request("shutdown", rpc_params![]).await; - assert!(response.is_ok(), "Failed to shutdown server"); - - // Allow some time for the server to shut down - tokio::time::sleep(Duration::from_millis(500)).await; - - // Check if the shutdown was successful by confirming the server task is done - if timeout(Duration::from_secs(1), server_task).await.is_err() { - panic!("The server did not shut down within the expected time"); - } - - let response: Result<(), _> = client.request("shutdown", rpc_params![]).await; - assert!(response.is_err(), "Server should not be running"); - } - - #[tokio::test] - async fn test_get_cloud_config() { - let body = fs::read_to_string("tests/files/web/cloud_config.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/cloud_config.json") - .with_status(200) - .with_body(body) - .create(); - - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - println!("Server started at {}", url); - let client = HttpClientBuilder::default().build(url).unwrap(); - let url = format!("{}/cloud_config.json", server.url()); - let params = RpcCloudConfigRequest { api_url: &url }; - println!("{:?}", params); - let response: Result = client.request("get_cloud_config", params).await; - let config = response.unwrap(); - assert!(!config.app_config_list.is_empty()); - assert!(!config.hub_config_list.is_empty()); - handle.stop().unwrap(); - } - - // ======================================================================== - // WebSocket and message size tests - // ======================================================================== - - use jsonrpsee::ws_client::WsClientBuilder; - use serial_test::serial; - - /// Generate a random ASCII string of given byte length (not compressible). - /// Uses printable ASCII range (0x21-0x7e) to avoid JSON escape overhead. - fn generate_random_string(size: usize) -> String { - use rand::RngExt; - let mut rng = rand::rng(); - let mut buf = vec![0u8; size]; - rng.fill(&mut buf[..]); - // Map each byte to printable ASCII (0x21..=0x7e, 94 chars), avoid '"' and '\\' - for b in buf.iter_mut() { - *b = match (*b % 92) + 0x21 { - b'"' => b'a', - b'\\' => b'b', - v => v, - }; - } - // SAFETY: all bytes are valid ASCII - unsafe { String::from_utf8_unchecked(buf) } - } - - /// Helper: start a minimal RPC server with "echo_data" method for size testing. - async fn start_test_server_with_config(max_size: u32) -> (String, ServerHandle) { - let config = ServerConfig::builder() - .max_request_body_size(max_size) - .max_response_body_size(max_size) - .build(); - let server = jsonrpsee::server::Server::builder() - .set_config(config) - .build("127.0.0.1:0".parse::().unwrap()) - .await - .unwrap(); - let mut module = RpcModule::new(()); - module - .register_method("echo_data", |params, _, _| { - let data: String = params.one()?; - Ok::(data) - }) - .unwrap(); - module.register_method("ping", |_, _, _| "pong").unwrap(); - let addr = server.local_addr().unwrap(); - let handle = server.start(module); - (format!("ws://{}", addr), handle) - } - - #[tokio::test] - async fn test_ws_client_connection() { - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - let ws_url = url.replace("http://", "ws://"); - let max_size = get_max_message_size(); - let client = WsClientBuilder::default() - .max_request_size(max_size) - .max_response_size(max_size) - .build(&ws_url) - .await - .unwrap(); - let response: String = client.request("ping", rpc_params![]).await.unwrap(); - assert_eq!(response, "pong"); - handle.stop().unwrap(); - } - - #[tokio::test] - async fn test_ws_large_message_50mb() { - const SIZE: usize = 50 * 1024 * 1024; // 50MB - let max_size = DEFAULT_MAX_SIZE; - let (ws_url, handle) = start_test_server_with_config(max_size).await; - let client = WsClientBuilder::default() - .max_request_size(max_size) - .max_response_size(max_size) - .build(&ws_url) - .await - .unwrap(); - let data = generate_random_string(SIZE); - let response: String = client - .request("echo_data", rpc_params![&data]) - .await - .unwrap(); - assert_eq!(response.len(), data.len()); - handle.stop().unwrap(); - } - - #[tokio::test] - #[serial] - async fn test_ws_env_var_limits_message_size() { - const ONE_MB: u32 = 1024 * 1024; - const TWO_MB: usize = 2 * 1024 * 1024; - - // Set env var to 1MB limit - // SAFETY: This test is marked #[serial] so no other tests run concurrently - unsafe { std::env::set_var("GETTER_WS_MAX_MESSAGE_SIZE", ONE_MB.to_string()) }; - assert_eq!(get_max_message_size(), ONE_MB); - - let (url, handle) = run_server("", Arc::new(AtomicBool::new(true))) - .await - .unwrap(); - let ws_url = url.replace("http://", "ws://"); - - // Client allows large messages, but server should reject - let client = WsClientBuilder::default() - .max_request_size(u32::MAX) - .max_response_size(u32::MAX) - .build(&ws_url) - .await - .unwrap(); - - // Verify ping still works (small message) - let response: String = client.request("ping", rpc_params![]).await.unwrap(); - assert_eq!(response, "pong"); - - // Send 2MB data via init request, should be rejected by 1MB server limit - let large_data = generate_random_string(TWO_MB); - let params = RpcInitRequest { - data_path: &large_data, - cache_path: "/tmp/cache", - global_expire_time: 3600, - }; - let response: Result = client.request("init", params).await; - assert!( - response.is_err(), - "2MB request should be rejected by 1MB server limit" - ); - - handle.stop().unwrap(); - // SAFETY: This test is marked #[serial] so no other tests run concurrently - unsafe { std::env::remove_var("GETTER_WS_MAX_MESSAGE_SIZE") }; - } -} diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index d940e27..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod convert; -pub mod http; -pub mod instance; -pub mod json; -pub mod time; -pub mod versioning; diff --git a/src/utils/convert.rs b/src/utils/convert.rs deleted file mode 100644 index 73be4df..0000000 --- a/src/utils/convert.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::collections::{BTreeMap, HashMap}; - -pub fn convert_hashmap_to_btreemap(hash_map: HashMap) -> BTreeMap -where - K: Ord + Eq + std::hash::Hash, - V: Clone, -{ - let mut btree_map: BTreeMap = BTreeMap::new(); - for (key, value) in hash_map { - btree_map.insert(key, value); - } - btree_map -} - -pub fn convert_btreemap(original: &BTreeMap) -> BTreeMap<&str, &str> { - let mut new_map: BTreeMap<&str, &str> = BTreeMap::new(); - for (key, value) in original.iter() { - new_map.insert(key.as_str(), value.as_str()); - } - new_map -} diff --git a/src/utils/http.rs b/src/utils/http.rs deleted file mode 100644 index a1894fb..0000000 --- a/src/utils/http.rs +++ /dev/null @@ -1,327 +0,0 @@ -use bytes::{Bytes, BytesMut}; -use http_body_util::{BodyExt, Empty}; -use hyper::{StatusCode, Uri}; -#[cfg(not(feature = "rustls-platform-verifier"))] -use hyper_rustls::ConfigBuilderExt; -use hyper_util::{ - client::legacy::{connect::HttpConnector, Client}, - rt::TokioExecutor, -}; -use once_cell::sync::Lazy; -use rustls::ClientConfig; -#[cfg(feature = "rustls-platform-verifier")] -use rustls_platform_verifier::BuilderVerifierExt; -use std::{collections::HashMap, fmt}; - -// Custom http response Error -#[derive(Debug)] -pub struct ResponseData { - pub status: u16, - pub body: Option, -} - -impl fmt::Display for ResponseData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "Response status: {}, body: {}", - self.status, - self.body.as_ref().map_or_else( - || "".to_string(), - |body| String::from_utf8_lossy(body).to_string(), - ) - ) - } -} - -pub async fn get( - url: Uri, - header_map: &HashMap, -) -> Result> { - if url.scheme_str() == Some("https") { - https_get(url, header_map).await - } else { - http_get(url, header_map).await - } -} - -pub async fn head( - url: Uri, - header_map: &HashMap, -) -> Result> { - if url.scheme_str() == Some("https") { - https_head(url, header_map).await - } else { - http_head(url, header_map).await - } -} - -pub async fn http_get( - url: Uri, - header_map: &HashMap, -) -> Result> { - _http_get(url, header_map, false).await -} - -pub async fn http_head( - url: Uri, - header_map: &HashMap, -) -> Result> { - _http_get(url, header_map, true).await -} - -async fn _http_get( - url: Uri, - header_map: &HashMap, - only_status: bool, -) -> Result> { - let http = HttpConnector::new(); - let client = Client::builder(TokioExecutor::new()).build(http); - - let mut req = hyper::Request::builder().method("GET").uri(url.clone()); - for (key, value) in header_map { - req = req.header(key, value); - } - let req = req.body(Empty::::new())?; - let mut res = client.request(req).await?; - let status = res.status(); - if only_status { - Ok(ResponseData { - status: status.as_u16(), - body: None, - }) - } else { - let mut body = BytesMut::new(); - while let Some(next) = res.frame().await { - let frame = next?; - if let Some(chunk) = frame.data_ref() { - body.extend_from_slice(chunk); - } - } - Ok(ResponseData { - status: status.as_u16(), - body: Some(body.freeze()), - }) - } -} - -pub async fn https_get( - url: Uri, - header_map: &HashMap, -) -> Result> { - _https_get(url, header_map, false).await -} - -pub async fn https_head( - url: Uri, - header_map: &HashMap, -) -> Result> { - _https_get(url, header_map, true).await -} - -// Global https provider with lazy initialization -static PROVIDER: Lazy> = - Lazy::new(|| std::sync::Arc::new(rustls::crypto::ring::default_provider())); - -// Https config error wrapper error -struct HttpsConfigError { - error: Box, -} -impl fmt::Display for HttpsConfigError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "HttpsConfigError: {}", self.error) - } -} -impl fmt::Debug for HttpsConfigError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "HttpsConfigError: {:?}", self.error) - } -} -impl std::error::Error for HttpsConfigError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - None - } -} - -fn https_config() -> Result, HttpsConfigError> { - let provider = PROVIDER.clone(); - let tls: rustls::ClientConfig; - #[cfg(feature = "rustls-platform-verifier")] - { - tls = ClientConfig::builder_with_provider(provider) - .with_safe_default_protocol_versions() - .map_err(|e| HttpsConfigError { error: Box::new(e) })? - .with_platform_verifier() - .map_err(|e| HttpsConfigError { error: Box::new(e) })? - .with_no_client_auth(); - } - #[cfg(all(feature = "webpki-roots", not(feature = "rustls-platform-verifier")))] - { - tls = rustls::ClientConfig::builder_with_provider(provider) - .with_safe_default_protocol_versions() - .map_err(|e| HttpsConfigError { error: Box::new(e) })? - .with_webpki_roots() - .with_no_client_auth(); - } - #[cfg(all( - feature = "native-tokio", - not(feature = "webpki-roots"), - not(feature = "rustls-platform-verifier") - ))] - { - tls = rustls::ClientConfig::builder_with_provider(provider) - .with_safe_default_protocol_versions() - .map_err(|e| HttpsConfigError { error: Box::new(e) })? - .with_native_roots() - .map_err(|e| HttpsConfigError { error: Box::new(e) })? - .with_no_client_auth(); - } - #[cfg(all( - not(feature = "native-tokio"), - not(feature = "webpki-roots"), - not(feature = "rustls-platform-verifier") - ))] - { - compile_error!("No TLS backend enabled"); - } - Ok(hyper_rustls::HttpsConnectorBuilder::new() - .with_tls_config(tls) - .https_or_http() - .enable_http1() - .enable_http2() - .build()) -} - -async fn _https_get( - url: Uri, - header_map: &HashMap, - only_status: bool, -) -> Result> { - let https = https_config()?; - let client = Client::builder(TokioExecutor::new()).build(https); - let mut req = hyper::Request::builder().method("GET").uri(url.clone()); - for (key, value) in header_map { - req = req.header(key, value); - } - let req = req.body(Empty::::new())?; - - let mut res = client.request(req).await?; - let status = res.status(); - if only_status { - Ok(ResponseData { - status: status.as_u16(), - body: None, - }) - } else { - let mut body = BytesMut::new(); - while let Some(next) = res.frame().await { - let frame = next?; - if let Some(chunk) = frame.data_ref() { - body.extend_from_slice(chunk); - } - } - Ok(ResponseData { - status: status.as_u16(), - body: Some(body.freeze()), - }) - } -} - -pub fn http_status_is_ok(status: u16) -> bool { - if let Ok(status) = StatusCode::from_u16(status) { - !(status.is_client_error() || status.is_server_error()) - } else { - false - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_https_get() { - let url = "https://github.com".parse().unwrap(); - let result = https_get(url, &HashMap::new()).await; - assert!(result.is_ok()); - assert!(!result.unwrap().body.unwrap().is_empty()); - } - - #[tokio::test] - async fn test_https_get_invalid() { - let url = "https://123123".parse().unwrap(); - let result = https_get(url, &HashMap::new()).await; - assert!(result.is_err()); - } - - #[tokio::test] - async fn test_https_get_status() { - let url = "https://mockhttp.org/status/418".parse().unwrap(); - let result = https_get(url, &HashMap::new()).await; - assert_eq!(result.unwrap().status, 418); - } - - #[tokio::test] - async fn test_https_head() { - let url = "https://github.com".parse().unwrap(); - let result = https_head(url, &HashMap::new()).await; - assert!(result.is_ok()); - assert!(result.unwrap().body.is_none()); - } - - #[tokio::test] - async fn test_https_get_header() { - let url = "https://postman-echo.com/get".parse().unwrap(); - let header_map = { - let mut map = HashMap::new(); - map.insert("X-Test".to_string(), "test000".to_string()); - map.insert("Test-Header".to_string(), "test001".to_string()); - map - }; - let result = https_get(url, &header_map).await; - assert!(result.is_ok()); - let body = result.unwrap().body.expect("Response body was empty"); - let json: serde_json::Value = serde_json::from_slice(&body).expect("Failed to parse JSON"); - for (key, value) in header_map { - // key to lowercase, for fix podman-echo - // Due https://stackoverflow.com/questions/5258977/are-http-headers-case-sensitive - let key = key.to_lowercase(); - assert_eq!(json["headers"][key], value); - } - } - - #[tokio::test] - async fn test_http_get() { - let url = "http://example.com".parse().unwrap(); - let result = http_get(url, &HashMap::new()).await; - assert!(result.is_ok()); - assert!(!result.unwrap().body.unwrap().is_empty()); - } - - #[tokio::test] - async fn test_http_head() { - let url = "http://example.com".parse().unwrap(); - let result = http_head(url, &HashMap::new()).await; - assert!(result.is_ok()); - assert!(result.unwrap().body.is_none()); - } - - #[tokio::test] - async fn test_http_get_header() { - let url = "http://postman-echo.com/get".parse().unwrap(); - let header_map = { - let mut map = HashMap::new(); - map.insert("X-Test".to_string(), "test000".to_string()); - map.insert("Test-Header".to_string(), "test001".to_string()); - map - }; - let result = http_get(url, &header_map).await; - assert!(result.is_ok()); - let body = result.unwrap().body.expect("Response body was empty"); - let json: serde_json::Value = serde_json::from_slice(&body).expect("Failed to parse JSON"); - for (key, value) in header_map { - let key = key.to_lowercase(); - assert_eq!(json["headers"][key], value); - } - } -} diff --git a/src/utils/instance.rs b/src/utils/instance.rs deleted file mode 100644 index d4acd9c..0000000 --- a/src/utils/instance.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::sync::Arc; -use tokio::sync::Mutex; - -pub struct InstanceContainer { - instance: Arc>, -} - -impl InstanceContainer { - pub fn new(value: T) -> Self { - Self { - instance: Arc::new(Mutex::new(value)), - } - } - - pub async fn get(&self) -> Arc> { - self.instance.clone() - } -} diff --git a/src/utils/json.rs b/src/utils/json.rs deleted file mode 100644 index 54c9710..0000000 --- a/src/utils/json.rs +++ /dev/null @@ -1,31 +0,0 @@ -use bytes::Bytes; -use serde::de::Deserialize; -use serde::ser::Serialize; - -pub fn json_to_bytes(json: &T) -> Result -where - T: ?Sized + Serialize, -{ - serde_json::to_vec(json).map(Bytes::from) -} - -pub fn bytes_to_json<'a, T>(bytes: &'a Bytes) -> Result -where - T: Deserialize<'a>, -{ - serde_json::from_slice(bytes) -} - -pub fn json_to_string(json: &T) -> Result -where - T: ?Sized + Serialize, -{ - serde_json::to_string(json) -} - -pub fn string_to_json<'a, T>(string: &'a str) -> Result -where - T: Deserialize<'a>, -{ - serde_json::from_str(string) -} diff --git a/src/utils/time.rs b/src/utils/time.rs deleted file mode 100644 index 98f3039..0000000 --- a/src/utils/time.rs +++ /dev/null @@ -1,8 +0,0 @@ -use std::time::{Duration, SystemTime, UNIX_EPOCH}; - -pub fn get_now_unix() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_else(|_| Duration::from_secs(0)) - .as_secs() -} diff --git a/src/utils/versioning.rs b/src/utils/versioning.rs deleted file mode 100644 index c25d819..0000000 --- a/src/utils/versioning.rs +++ /dev/null @@ -1,170 +0,0 @@ -use std::cmp::Ordering; - -use libversion_sys; - -use once_cell::sync::Lazy; -use regex::Regex; - -static VERSION_NUMBER_STRICT_MATCH_REGEX: Lazy = - Lazy::new(|| Regex::new(r"\d+(\.\d+)+([.|\-|+|_| ]*[A-Za-z0-9]+)*").unwrap()); - -static VERSION_NUMBER_MATCH_REGEX: Lazy = - Lazy::new(|| Regex::new(r"\d+(\.\d+)*([.|\-|+|_| ]*[A-Za-z0-9]+)*").unwrap()); - -#[derive(Debug, Clone)] -pub struct Version { - string: String, -} - -impl Version { - pub fn new(string: String) -> Self { - Version { string } - } - - pub fn is_valid(&self) -> bool { - self.get_valid_version().is_some() - } - - pub fn get_valid_version(&self) -> Option { - VERSION_NUMBER_STRICT_MATCH_REGEX - .find(&self.string) - .or_else(|| VERSION_NUMBER_MATCH_REGEX.find(&self.string)) - .map(|match_str| match_str.as_str().to_string()) - } -} - -impl PartialEq for Version { - fn eq(&self, other: &Self) -> bool { - match (self.get_valid_version(), other.get_valid_version()) { - (Some(v1), Some(v2)) => libversion_sys::compare(&v1, &v2) == Ordering::Equal, - _ => false, - } - } -} - -impl PartialOrd for Version { - fn partial_cmp(&self, other: &Self) -> Option { - match (self.get_valid_version(), other.get_valid_version()) { - (Some(v1), Some(v2)) => Some(libversion_sys::compare(&v1, &v2)), - _ => None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_version_is_valid() { - let version = Version { - string: "1.0.0".to_string(), - }; - assert!(version.is_valid()); - let version = Version { - string: "1.0.0-alpha".to_string(), - }; - assert!(version.is_valid()); - let version = Version { - string: "版本1.0.0".to_string(), - }; - assert!(version.is_valid()); - let chinese_suffix_version = Version { - string: "版本1.0.0 天行健".to_string(), - }; - assert!(chinese_suffix_version.is_valid()); - } - - #[test] - fn test_version_is_invalid() { - let version = Version { - string: "xxx".to_string(), - }; - assert!(!version.is_valid()); - } - - #[test] - fn test_version_eq() { - let version = Version { - string: "1.0.0".to_string(), - }; - let other_version = Version { - string: "1.0".to_string(), - }; - assert_eq!(version, other_version); - - let chinese_version = Version { - string: "版本1.0.0".to_string(), - }; - assert_eq!(version, chinese_version); - } - - #[test] - fn test_version_ne() { - let version = Version { - string: "1.0.0".to_string(), - }; - let other_version = Version { - string: "1.0.1".to_string(), - }; - assert_ne!(version, other_version); - } - - #[test] - fn test_version_lt() { - let version = Version { - string: "1.0".to_string(), - }; - let other_version = Version { - string: "1.0.1".to_string(), - }; - assert!(version < other_version); - } - - #[test] - fn test_version_gt() { - let version = Version { - string: "1.0.1".to_string(), - }; - let other_version = Version { - string: "1.0.1-alpha".to_string(), - }; - assert!(version > other_version); - } - - #[test] - fn test_version_get_valid_version() { - let version = Version { - string: "1.0.0 123123".to_string(), - }; - assert_eq!( - version.get_valid_version(), - Some("1.0.0 123123".to_string()) - ); - let version = Version { - string: "1.0.0-alpha 版本".to_string(), - }; - assert_eq!(version.get_valid_version(), Some("1.0.0-alpha".to_string())); - let version = Version { - string: "版本1.0.0".to_string(), - }; - assert_eq!(version.get_valid_version(), Some("1.0.0".to_string())); - let chinese_suffix_version = Version { - string: "版本1.0.0 天行健".to_string(), - }; - assert_eq!( - chinese_suffix_version.get_valid_version(), - Some("1.0.0".to_string()) - ); - - let version = Version { - string: "xxx".to_string(), - }; - assert_eq!(version.get_valid_version(), None); - - let version = Version { - string: "1.0-alpha 版本 123123".to_string(), - }; - assert_eq!(version.get_valid_version(), Some("1.0-alpha".to_string())); - } -} diff --git a/src/websdk.rs b/src/websdk.rs deleted file mode 100644 index 6100512..0000000 --- a/src/websdk.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod cloud_rules; -pub mod repo; diff --git a/src/websdk/cloud_rules.rs b/src/websdk/cloud_rules.rs deleted file mode 100644 index 5952fe8..0000000 --- a/src/websdk/cloud_rules.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod cloud_rules_manager; -pub mod cloud_rules_wrapper; -pub mod data; diff --git a/src/websdk/cloud_rules/cloud_rules_manager.rs b/src/websdk/cloud_rules/cloud_rules_manager.rs deleted file mode 100644 index 17aaebf..0000000 --- a/src/websdk/cloud_rules/cloud_rules_manager.rs +++ /dev/null @@ -1,112 +0,0 @@ -use hyper::Uri; -use std::collections::HashMap; - -use super::data::config_list::{ConfigList, ConfigListViewer}; -use crate::utils::http::get; - -pub struct CloudRules { - pub api_url: String, - - _config_list: Option, -} - -#[derive(Debug)] -pub struct DownloadError { - pub url: Uri, - pub error: Box, -} - -impl std::fmt::Display for DownloadError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "DownloadError: {} ({})", self.error, self.url) - } -} - -impl CloudRules { - pub fn new(api_url: &str) -> CloudRules { - CloudRules { - api_url: api_url.to_string(), - _config_list: None, - } - } - - pub fn get_config_list(&self) -> ConfigListViewer<'_> { - self._config_list - .as_ref() - .map_or_else(ConfigListViewer::default, |config_list| { - config_list.viewer() - }) - } - - pub async fn renew(&mut self) -> Result<(), DownloadError> { - let config_list = self.download_config_list(&self.api_url).await?; - self._config_list = Some(config_list); - Ok(()) - } - - async fn download_config_list(&self, url: &str) -> Result { - Self::_download_config_list_impl(url) - .await - .map_err(|e| DownloadError { - url: url.parse().unwrap(), - error: e, - }) - } - async fn _download_config_list_impl( - url: &str, - ) -> Result> { - let map = HashMap::new(); - let resp = get(url.parse()?, &map).await?; - if let Some(body) = resp.body { - let config_list: ConfigList = serde_json::from_slice(&body)?; - Ok(config_list) - } else { - Err("No body".into()) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use mockito::Server; - use std::fs; - - #[tokio::test] - async fn test_download_config_list() { - let json = fs::read_to_string("tests/files/data/UpgradeAll-rules_rules.json").unwrap(); - let path = "/DUpdateSystem/UpgradeAll-rules/master/rules.json"; - let mut server = Server::new_async().await; - server.mock("GET", path).with_body(json).create(); - let url = server.url() + path; - - let mut cloud_rules = CloudRules::new(&url); - cloud_rules.renew().await.unwrap(); - let config_list = cloud_rules.get_config_list(); - assert_eq!(config_list.app_config_list.len(), 219); - assert_eq!(config_list.app_config_list[0].info.name, "UpgradeAll"); - assert_eq!( - config_list.app_config_list.last().unwrap().info.name, - "黑阈" - ); - assert_eq!(config_list.hub_config_list.len(), 11); - assert_eq!(config_list.hub_config_list[0].info.hub_name, "GitHub"); - assert_eq!( - config_list.hub_config_list.last().unwrap().info.hub_name, - "Xposed Module Repository" - ); - } - - #[tokio::test] - async fn test_download_config_list_invalid() { - let path = "/DUpdateSystem/UpgradeAll-rules/master/rules.json"; - let mut server = Server::new_async().await; - server.mock("GET", path).with_status(404).create(); - let url = server.url() + path; - - let mut cloud_rules = CloudRules::new(&url); - let result = cloud_rules.renew().await; - assert!(result.is_err()); - assert_eq!(result.unwrap_err().url.to_string(), url); - } -} diff --git a/src/websdk/cloud_rules/cloud_rules_wrapper.rs b/src/websdk/cloud_rules/cloud_rules_wrapper.rs deleted file mode 100644 index 24a718a..0000000 --- a/src/websdk/cloud_rules/cloud_rules_wrapper.rs +++ /dev/null @@ -1,109 +0,0 @@ -pub use super::cloud_rules_manager::CloudRules; -use super::data::app_item::AppItem; -use super::data::hub_item::HubItem; - -impl CloudRules { - pub fn get_cloud_app_rules(&self, filter: F) -> Option - where - F: Fn(&AppItem) -> bool, - { - self._get_cloud_app_rules(filter, true) - .first() - .map(|x| x.to_owned()) - } - - pub fn get_cloud_app_rules_list(&self, filter: F) -> Vec - where - F: Fn(&AppItem) -> bool, - { - self._get_cloud_app_rules(filter, false) - } - - pub fn _get_cloud_app_rules(&self, filter: F, only_first: bool) -> Vec - where - F: Fn(&AppItem) -> bool, - { - let mut list = Vec::new(); - for config in self.get_config_list().app_config_list { - if filter(config) { - list.push(config.to_owned()); - if only_first { - break; - } - } - } - list - } - - pub fn get_cloud_hub_rules(&self, filter: F) -> Option - where - F: Fn(&HubItem) -> bool, - { - for config in self.get_config_list().hub_config_list { - if filter(config) { - return Some(config.to_owned()); - } - } - None - } -} - -#[cfg(test)] -mod tests { - use super::*; - use mockito::Server; - use std::fs; - - #[tokio::test] - async fn test_get_cloud_app_rules() { - let json = fs::read_to_string("tests/files/data/UpgradeAll-rules_rules.json").unwrap(); - let path = "/DUpdateSystem/UpgradeAll-rules/master/rules.json"; - let mut server = Server::new_async().await; - server.mock("GET", path).with_body(json).create(); - let url = server.url() + path; - - let mut cloud_rules = CloudRules::new(&url); - cloud_rules.renew().await.unwrap(); - let list = cloud_rules.get_cloud_app_rules(|x| x.info.name == "UpgradeAll"); - assert!(list.is_some()); - let list = cloud_rules.get_cloud_app_rules(|x| x.info.name.is_empty()); - assert!(list.is_none()); - } - - #[tokio::test] - async fn test_get_cloud_app_rules_list() { - let json = fs::read_to_string("tests/files/data/UpgradeAll-rules_rules.json").unwrap(); - let path = "/DUpdateSystem/UpgradeAll-rules/master/rules.json"; - let mut server = Server::new_async().await; - server.mock("GET", path).with_body(json).create(); - let url = server.url() + path; - - let mut cloud_rules = CloudRules::new(&url); - cloud_rules.renew().await.unwrap(); - let list = cloud_rules.get_cloud_app_rules_list(|x| x.info.name == "UpgradeAll"); - assert_eq!(list.len(), 1); - let list = cloud_rules.get_cloud_app_rules_list(|x| x.info.name.is_empty()); - assert_eq!(list.len(), 0); - let list = cloud_rules.get_cloud_app_rules_list(|x| !x.info.name.is_empty()); - assert_eq!( - list.len(), - cloud_rules.get_config_list().app_config_list.len() - ); - } - - #[tokio::test] - async fn test_get_cloud_hub_rules() { - let json = fs::read_to_string("tests/files/data/UpgradeAll-rules_rules.json").unwrap(); - let path = "/DUpdateSystem/UpgradeAll-rules/master/rules.json"; - let mut server = Server::new_async().await; - server.mock("GET", path).with_body(json).create(); - let url = server.url() + path; - - let mut cloud_rules = CloudRules::new(&url); - cloud_rules.renew().await.unwrap(); - let list = cloud_rules.get_cloud_hub_rules(|x| x.info.hub_name == "GitHub"); - assert!(list.is_some()); - let list = cloud_rules.get_cloud_hub_rules(|x| x.info.hub_name.is_empty()); - assert!(list.is_none()); - } -} diff --git a/src/websdk/cloud_rules/data.rs b/src/websdk/cloud_rules/data.rs deleted file mode 100644 index 07f8d80..0000000 --- a/src/websdk/cloud_rules/data.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod app_item; -pub mod config_list; -pub mod hub_item; diff --git a/src/websdk/cloud_rules/data/app_item.rs b/src/websdk/cloud_rules/data/app_item.rs deleted file mode 100644 index dca31c9..0000000 --- a/src/websdk/cloud_rules/data/app_item.rs +++ /dev/null @@ -1,94 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -/// AppConfig -/// -/// JSON Schema: -/// ```json -/// { -/// "base_version": 2, -/// "config_version": 1, -/// "uuid": "", -/// "base_hub_uuid": "", -/// "info": { -/// "name": "", -/// "url": "", -/// "extra_map": { -/// "android_app_package": "" -/// } -/// } -/// } -/// ``` - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct AppItem { - #[serde(rename = "base_version")] - pub base_version: i32, - - #[serde(rename = "config_version")] - pub config_version: i32, - - #[serde(rename = "uuid")] - pub uuid: String, - - #[serde(rename = "base_hub_uuid")] - pub base_hub_uuid: String, - - #[serde(rename = "info")] - pub info: AppInfo, -} - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct AppInfo { - #[serde(rename = "name")] - pub name: String, - - #[serde(rename = "url")] - pub url: String, - - #[serde(rename = "extra_map")] - pub extra_map: HashMap, // Use HashMap to store arbitrary key/value pairs -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json; - - #[test] - fn test_app_config() { - let json = r#" -{ - "base_version": 2, - "config_version": 1, - "uuid": "f27f71e1-d7a1-4fd1-bbcc-9744380611a1", - "base_hub_uuid": "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - "info": { - "name": "UpgradeAll", - "url": "https://github.com/xz-dev/UpgradeAll", - "extra_map": { - "android_app_package": "net.xzos.upgradeall" - } - } -} -"#; - - let app_item: AppItem = serde_json::from_str(json).unwrap(); - assert_eq!(app_item.base_version, 2); - assert_eq!(app_item.config_version, 1); - assert_eq!(app_item.uuid, "f27f71e1-d7a1-4fd1-bbcc-9744380611a1"); - assert_eq!( - app_item.base_hub_uuid, - "fd9b2602-62c5-4d55-bd1e-0d6537714ca0" - ); - assert_eq!(app_item.info.name, "UpgradeAll"); - assert_eq!(app_item.info.url, "https://github.com/xz-dev/UpgradeAll"); - assert_eq!( - app_item.info.extra_map, - HashMap::from([( - "android_app_package".to_string(), - "net.xzos.upgradeall".to_string() - )]) - ); - } -} diff --git a/src/websdk/cloud_rules/data/config_list.rs b/src/websdk/cloud_rules/data/config_list.rs deleted file mode 100644 index 886fcfe..0000000 --- a/src/websdk/cloud_rules/data/config_list.rs +++ /dev/null @@ -1,80 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use super::app_item::AppItem; -use super::hub_item::HubItem; - -/// Configuration lists -/// -/// JSON Schema: -/// ```json -/// { -/// "app_config_list": [], -/// "hub_config_list": [] -/// } -/// ``` - -#[derive(Default, Serialize, Debug, Clone)] -pub struct ConfigListViewer<'a> { - #[serde(rename = "app_config_list")] - pub app_config_list: Vec<&'a AppItem>, - - #[serde(rename = "hub_config_list")] - pub hub_config_list: Vec<&'a HubItem>, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ConfigList { - #[serde(rename = "app_config_list")] - pub app_config_list: Vec, - - #[serde(rename = "hub_config_list")] - pub hub_config_list: Vec, -} - -impl ConfigList { - pub fn viewer(&self) -> ConfigListViewer<'_> { - ConfigListViewer { - app_config_list: self.app_config_list.iter().collect(), - hub_config_list: self.hub_config_list.iter().collect(), - } - } -} - -impl ConfigListViewer<'_> { - pub fn to_owned(&self) -> ConfigList { - ConfigList { - app_config_list: self.app_config_list.iter().map(|&x| x.clone()).collect(), - hub_config_list: self.hub_config_list.iter().map(|&x| x.clone()).collect(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json; - use std::fs; - - #[test] - fn test_config_list() { - // read json from file - let json = fs::read_to_string("tests/files/data/UpgradeAll-rules_rules.json").unwrap(); - - let config_list: ConfigList = serde_json::from_str(&json).unwrap(); - - // check app_config_list - assert_eq!(config_list.app_config_list.len(), 219); - assert_eq!(config_list.app_config_list[0].info.name, "UpgradeAll"); - assert_eq!( - config_list.app_config_list.last().unwrap().info.name, - "黑阈" - ); - // check hub_config_list - assert_eq!(config_list.hub_config_list.len(), 11); - assert_eq!(config_list.hub_config_list[0].info.hub_name, "GitHub"); - assert_eq!( - config_list.hub_config_list.last().unwrap().info.hub_name, - "Xposed Module Repository" - ); - } -} diff --git a/src/websdk/cloud_rules/data/hub_item.rs b/src/websdk/cloud_rules/data/hub_item.rs deleted file mode 100644 index c8c43b7..0000000 --- a/src/websdk/cloud_rules/data/hub_item.rs +++ /dev/null @@ -1,123 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// HubConfig -/// -/// JSON Schema: -/// ```json -/// { -/// base_version: 6 -/// config_version: 1 -/// uuid: "" -/// info: { -/// "hub_name": "", -/// "hub_icon_url": "" -/// } -/// target_check_api: "" -/// api_keywords: [] -/// auth_keywords: [] -/// app_url_templates": [] -/// } -/// ``` - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct HubItem { - #[serde(rename = "base_version", default)] - pub base_version: i32, - - #[serde(rename = "config_version", default)] - pub config_version: i32, - - #[serde(rename = "uuid", default)] - pub uuid: String, - - #[serde(rename = "info")] - pub info: Info, - - #[serde(rename = "api_keywords", default)] - pub api_keywords: Vec, - - /// Auth parameter keys required by this hub (e.g. ["token"] for GitHub). - /// Used by the UI to provide autocomplete suggestions when editing hub auth. - #[serde(rename = "auth_keywords", default)] - pub auth_keywords: Vec, - - #[serde(rename = "app_url_templates", default)] - pub app_url_templates: Vec, - - #[serde(rename = "target_check_api")] - pub target_check_api: Option, -} - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct Info { - #[serde(rename = "hub_name", default)] - pub hub_name: String, - - #[serde(rename = "hub_icon_url", default)] - pub hub_icon_url: Option, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_hub_item() { - let json = r#" -{ - "base_version": 6, - "config_version": 3, - "uuid": "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - "info": { - "hub_name": "GitHub", - "hub_icon_url": "" - }, - "target_check_api": "", - "api_keywords": [ - "owner", - "repo" - ], - "app_url_templates": [ - "https://github.com/%owner/%repo/" - ] -} - "#; - - let hub_item: HubItem = serde_json::from_str(json).unwrap(); - assert_eq!(hub_item.base_version, 6); - assert_eq!(hub_item.config_version, 3); - assert_eq!(hub_item.uuid, "fd9b2602-62c5-4d55-bd1e-0d6537714ca0"); - assert_eq!(hub_item.info.hub_name, "GitHub"); - assert_eq!(hub_item.info.hub_icon_url, Some("".to_string())); - assert_eq!(hub_item.target_check_api, Some("".to_string())); - assert_eq!(hub_item.api_keywords, ["owner", "repo"]); - // Old config without auth_keywords deserializes to empty vec. - assert_eq!(hub_item.auth_keywords, Vec::::new()); - assert_eq!( - hub_item.app_url_templates[0], - "https://github.com/%owner/%repo/" - ); - } - - #[test] - fn test_hub_item_auth_keywords() { - let json = r#" -{ - "base_version": 5, - "config_version": 3, - "uuid": "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - "info": { "hub_name": "GitHub" }, - "api_keywords": ["owner", "repo"], - "auth_keywords": ["token"], - "app_url_templates": ["https://github.com/%owner/%repo/"] -} - "#; - let hub_item: HubItem = serde_json::from_str(json).unwrap(); - assert_eq!(hub_item.auth_keywords, ["token"]); - - // Round-trip serialization preserves auth_keywords. - let serialized = serde_json::to_string(&hub_item).unwrap(); - let hub_item2: HubItem = serde_json::from_str(&serialized).unwrap(); - assert_eq!(hub_item2.auth_keywords, ["token"]); - } -} diff --git a/src/websdk/repo.rs b/src/websdk/repo.rs deleted file mode 100644 index d10139d..0000000 --- a/src/websdk/repo.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod api; -pub mod data; -pub mod provider; diff --git a/src/websdk/repo/api.rs b/src/websdk/repo/api.rs deleted file mode 100644 index 044176a..0000000 --- a/src/websdk/repo/api.rs +++ /dev/null @@ -1,166 +0,0 @@ -use async_fn_traits::AsyncFnOnce2; -use serde::de::DeserializeOwned; -use serde::Serialize; - -use super::data::release::ReleaseData; -use super::provider::base_provider::{AppDataMap, DataMap, FIn, FOut, FunctionType, HubDataMap}; -use super::provider::outside_rpc::OutsideProvider; -use super::provider::{self, add_provider}; -use crate::cache::get_cache_manager; -use crate::cache::manager::GroupType; -use crate::rpc::data::DownloadItemData; -use crate::utils::json::{bytes_to_json, json_to_bytes}; -use std::collections::HashMap; - -#[derive(Debug, Clone)] -struct ErrorProviderNotFound; - -impl std::fmt::Display for ErrorProviderNotFound { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Provider not found for this request.") - } -} - -impl std::error::Error for ErrorProviderNotFound { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - None - } -} - -async fn call_func( - uuid: &str, - app_data: &AppDataMap<'_>, - hub_data: &HubDataMap<'_>, - func_type: FunctionType, - provider_func: F, -) -> Result, ErrorProviderNotFound> -where - T: Send + DeserializeOwned + Serialize, - F: for<'b> AsyncFnOnce2<&'b str, &'b FIn<'b>, Output = Option>>, -{ - let cache_manager = get_cache_manager().await; - let data_map = DataMap { app_data, hub_data }; - let api_cache_key = data_map.get_hash(); - if let Some(bytes) = cache_manager - .lock() - .await - .get(&GroupType::Api, &api_cache_key.to_string(), None) - .await - { - if let Ok(value) = bytes_to_json::(&bytes) { - return Ok(Some(value)); - } - } - let cache_keys = provider::get_cache_request_key(uuid, &func_type, &data_map); - let mut cache_map = HashMap::new(); - if let Some(keys) = cache_keys { - for key in keys { - if let Some(value) = cache_manager - .lock() - .await - .get(&GroupType::RepoInside, &key, None) - .await - { - cache_map.insert(key, value); - } - } - } - - let fin = FIn::new(data_map, Some(cache_map)); - if let Some(fout) = provider_func(uuid, &fin).await { - if let Some(cached_map) = fout.cached_map { - for (key, value) in cached_map { - let _ = cache_manager - .lock() - .await - .save(&GroupType::RepoInside, &key, value) - .await; - } - } - if let Ok(data) = fout.result { - if let Ok(value) = json_to_bytes(&data) { - let _ = cache_manager - .lock() - .await - .save(&GroupType::Api, &api_cache_key.to_string(), value) - .await; - } - Ok(Some(data)) - } else { - Ok(None) - } - } else { - Err(ErrorProviderNotFound) - } -} - -pub async fn check_app_available<'a>( - uuid: &str, - app_data: &AppDataMap<'a>, - hub_data: &HubDataMap<'a>, -) -> Option { - call_func( - uuid, - app_data, - hub_data, - FunctionType::CheckAppAvailable, - provider::check_app_available, - ) - .await - .unwrap_or(None) -} - -pub async fn get_latest_release<'a>( - uuid: &str, - app_data: &AppDataMap<'a>, - hub_data: &HubDataMap<'a>, -) -> Option { - call_func( - uuid, - app_data, - hub_data, - FunctionType::GetLatestRelease, - provider::get_latest_release, - ) - .await - .unwrap_or(None) -} - -pub async fn get_releases<'a>( - uuid: &str, - app_data: &AppDataMap<'a>, - hub_data: &HubDataMap<'a>, -) -> Option> { - call_func( - uuid, - app_data, - hub_data, - FunctionType::GetReleases, - provider::get_releases, - ) - .await - .unwrap_or(None) -} - -pub async fn get_download<'a>( - uuid: &str, - app_data: &AppDataMap<'a>, - hub_data: &HubDataMap<'a>, - asset_index: &[i32], -) -> Option> { - let data_map = DataMap { app_data, hub_data }; - let fin = FIn::new(data_map, None); - if let Some(fout) = provider::get_download(uuid, &fin, asset_index).await { - fout.result.ok() - } else { - None - } -} - -pub fn add_outside_provider(uuid: &str, url: &str) { - let provider = OutsideProvider { - uuid: uuid.to_string(), - url: url.to_string(), - }; - add_provider(uuid, provider); -} diff --git a/src/websdk/repo/controller.rs b/src/websdk/repo/controller.rs deleted file mode 100644 index f9eae81..0000000 --- a/src/websdk/repo/controller.rs +++ /dev/null @@ -1,121 +0,0 @@ -use bytes::Bytes; -use std::future::Future; - -use crate::cache::convert::{bool_to_bytes, bytes_to_bool}; -use crate::utils::json::{bytes_to_json, json_to_bytes}; -use crate::cache::manager::GroupType::{API, REPO_INSIDE}; -use crate::get_cache_manager; - -use super::data::release::ReleaseData; -use super::provider; -use super::provider::base_provider::{FIn, FOut, FunctionType, IdMap}; - -pub async fn check_app_available<'a>(uuid: &str, id_map: &IdMap<'a>) -> Option { - let key = format!("check_app_available_{}_{:?}", &uuid, id_map); - let expire_time = Some(60 * 60 * 24); - cache_future_return( - _check_app_available(uuid, id_map), - &key, - expire_time, - |data| Ok(bool_to_bytes(data)), - |bytes| Ok(bytes_to_bool(bytes)), - ) - .await -} - -pub async fn get_latest_release<'a>(uuid: &str, id_map: &IdMap<'a>) -> Option { - let key = format!("get_latest_release_{}_{:?}", &uuid, id_map); - let expire_time = Some(60 * 60 * 24); - cache_future_return( - _get_latest_release(uuid, id_map), - &key, - expire_time, - |data| json_to_bytes(&data), - |bytes| bytes_to_json(bytes), - ) - .await -} - -pub async fn get_releases<'a>(uuid: &str, id_map: &IdMap<'a>) -> Option> { - let key = format!("get_releases_{}_{:?}", &uuid, id_map); - let expire_time = Some(60 * 60 * 24); - cache_future_return( - _get_releases(uuid, id_map), - &key, - expire_time, - |data| json_to_bytes(&data), - |bytes| bytes_to_json(bytes), - ) - .await -} - -async fn _check_app_available<'a>(uuid: &str, id_map: &IdMap<'a>) -> Option { - let fin = get_fin(uuid, id_map, &FunctionType::CheckAppAvailable).await; - let fout = provider::check_app_available(uuid, &fin).await; - detach_result(fout).await -} - -async fn _get_latest_release<'a>(uuid: &str, id_map: &IdMap<'a>) -> Option { - let fin = get_fin(uuid, id_map, &FunctionType::GetLatestRelease).await; - let fout = provider::get_latest_release(uuid, &fin).await; - detach_result(fout).await -} - -async fn _get_releases<'a>(uuid: &str, id_map: &IdMap<'a>) -> Option> { - let fin = get_fin(uuid, id_map, &FunctionType::GetReleases).await; - let fout = provider::get_releases(uuid, &fin).await; - detach_result(fout).await -} - -async fn get_fin<'a>(uuid: &str, id_map: &'a IdMap<'a>, function_type: &FunctionType) -> FIn<'a> { - let cache_map = if let Some(cache_key_list) = - provider::get_cache_request_key(uuid, function_type, id_map) - { - let map = get_cache_manager!() - .get_cache_map(&REPO_INSIDE, &cache_key_list, None) - .await; - if map.is_empty() { - None - } else { - Some(map) - } - } else { - None - }; - FIn::new(id_map, cache_map) -} - -async fn detach_result(fout: Option>) -> Option { - if let Some(fout) = fout { - if let Some(cache_map) = fout.cached_map { - for (key, value) in cache_map { - let _ = get_cache_manager!().save(&REPO_INSIDE, &key, value).await; - } - } - fout.result.ok() - } else { - None - } -} - -async fn cache_future_return( - f: impl Future>, - key: &str, - expire_time: Option, - encoder: fn(&T) -> Result, - decoder: fn(&Bytes) -> Result, -) -> Option { - if let Some(value) = get_cache_manager!().get(&API, key, expire_time).await { - decoder(&value).ok() - } else { - let result = f.await; - if let Some(result) = result { - if let Ok(value) = encoder(&result) { - let _ = get_cache_manager!().save(&API, key, value).await; - } - Some(result) - } else { - None - } - } -} diff --git a/src/websdk/repo/data.rs b/src/websdk/repo/data.rs deleted file mode 100644 index f35143a..0000000 --- a/src/websdk/repo/data.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod release; diff --git a/src/websdk/repo/data/release.rs b/src/websdk/repo/data/release.rs deleted file mode 100644 index 6d25119..0000000 --- a/src/websdk/repo/data/release.rs +++ /dev/null @@ -1,17 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct ReleaseData { - pub version_number: String, - pub changelog: String, - pub assets: Vec, - pub extra: Option>, -} - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct AssetData { - pub file_name: String, - pub file_type: String, - pub download_url: String, -} diff --git a/src/websdk/repo/provider.rs b/src/websdk/repo/provider.rs deleted file mode 100644 index 220367b..0000000 --- a/src/websdk/repo/provider.rs +++ /dev/null @@ -1,99 +0,0 @@ -pub mod base_provider; -pub mod fdroid; -pub mod github; -pub mod gitlab; -pub mod lsposed_repo; -pub mod outside_rpc; - -use once_cell::sync::Lazy; -use std::collections::HashMap; -use std::sync::{Arc, RwLock}; - -use self::base_provider::{BaseProvider, DataMap, FIn, FOut, FunctionType}; -use self::fdroid::FDroidProvider; -use self::github::GitHubProvider; -use self::gitlab::GitLabProvider; -use self::lsposed_repo::LsposedRepoProvider; -use super::data::release::ReleaseData; -use crate::rpc::data::DownloadItemData; - -type ProviderMap = HashMap<&'static str, Arc>; - -static PROVIDER_MAP: Lazy>> = Lazy::new(|| { - Arc::new(RwLock::new(HashMap::from([ - ( - "fd9b2602-62c5-4d55-bd1e-0d6537714ca0", - Arc::new(GitHubProvider::new()) as Arc, - ), - ( - "6a6d590b-1809-41bf-8ce3-7e3f6c8da945", - Arc::new(FDroidProvider::new()) as Arc, - ), - ( - "a84e2fbe-1478-4db5-80ae-75d00454c7eb", - Arc::new(GitLabProvider::new()) as Arc, - ), - ( - "401e6259-2eab-46f0-8e8a-d2bfafedf5bf", - Arc::new(LsposedRepoProvider::new()) as Arc, - ), - ]))) -}); - -fn get_provider(uuid: &str) -> Option> { - let map = PROVIDER_MAP.read().unwrap(); - map.get(uuid).cloned() -} - -pub fn add_provider(uuid: &str, provider: impl BaseProvider + Send + Sync + 'static) { - let mut map = PROVIDER_MAP.write().unwrap(); - let uuid: &'static str = Box::leak(Box::new(uuid.to_string())); - map.insert( - uuid, - Arc::new(provider) as Arc, - ); -} - -pub fn get_cache_request_key( - uuid: &str, - function_type: &FunctionType, - data_map: &DataMap, -) -> Option> { - get_provider(uuid).map(|provider| provider.get_cache_request_key(function_type, data_map)) -} - -pub async fn check_app_available<'a>(uuid: &str, fin: &FIn<'a>) -> Option> { - if let Some(provider) = get_provider(uuid) { - Some(provider.check_app_available(fin).await) - } else { - None - } -} - -pub async fn get_latest_release<'a>(uuid: &str, fin: &FIn<'a>) -> Option> { - if let Some(provider) = get_provider(uuid) { - Some(provider.get_latest_release(fin).await) - } else { - None - } -} - -pub async fn get_releases<'a>(uuid: &str, fin: &FIn<'a>) -> Option>> { - if let Some(provider) = get_provider(uuid) { - Some(provider.get_releases(fin).await) - } else { - None - } -} - -pub async fn get_download<'a>( - uuid: &str, - fin: &FIn<'a>, - asset_index: &[i32], -) -> Option>> { - if let Some(provider) = get_provider(uuid) { - Some(provider.get_download(fin, asset_index).await) - } else { - None - } -} diff --git a/src/websdk/repo/provider/base_provider.rs b/src/websdk/repo/provider/base_provider.rs deleted file mode 100644 index 2dcec20..0000000 --- a/src/websdk/repo/provider/base_provider.rs +++ /dev/null @@ -1,427 +0,0 @@ -use async_trait::async_trait; - -use bytes::Bytes; -use core::fmt; -use regex::Regex; -use std::hash::{DefaultHasher, Hash, Hasher}; -use std::{ - collections::{BTreeMap, HashMap}, - error::Error, -}; - -use super::super::data::release::*; -use crate::rpc::data::DownloadItemData; - -pub type HubDataMap<'a> = BTreeMap<&'a str, &'a str>; -pub type AppDataMap<'a> = BTreeMap<&'a str, &'a str>; - -#[derive(Hash)] -pub struct DataMap<'a> { - pub app_data: &'a AppDataMap<'a>, - pub hub_data: &'a HubDataMap<'a>, -} - -impl fmt::Display for DataMap<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "app_data: {:?}, hub_data: {:?}", - self.app_data, self.hub_data - ) - } -} - -impl DataMap<'_> { - pub fn get_hash(&self) -> u64 { - let mut hasher = DefaultHasher::new(); - self.hash(&mut hasher); - hasher.finish() - } -} - -pub type CacheMap = HashMap; - -pub enum FunctionType { - CheckAppAvailable, - GetLatestRelease, - GetReleases, - GetDownload, -} - -pub struct FIn<'a> { - pub data_map: DataMap<'a>, - cache_map: Option>, -} - -impl<'a> FIn<'a> { - pub fn new_with_frag( - app_data: &'a AppDataMap<'a>, - hub_data: &'a HubDataMap<'a>, - cache_map: Option>, - ) -> Self { - FIn { - data_map: DataMap { app_data, hub_data }, - cache_map, - } - } - pub fn new(data_map: DataMap<'a>, cache_map: Option>) -> Self { - FIn { - data_map, - cache_map, - } - } - - pub fn get_cache(&self, key: &str) -> Option<&Bytes> { - if let Some(cache_map) = &self.cache_map { - if let Some(value) = cache_map.get(key) { - return Some(value); - } - } - None - } -} - -#[derive(Debug)] -pub struct FOut { - pub result: Result>, - pub cached_map: Option>, -} - -impl FOut { - pub fn new(data: T) -> Self { - FOut { - result: Ok(data), - cached_map: None, - } - } - - pub fn new_empty() -> Self { - FOut { - result: Err(Box::new(std::io::Error::other("no data"))), - cached_map: None, - } - } - - pub fn set_cache(mut self, key: &str, value: Bytes) -> Self { - let cache_map = self.cached_map.get_or_insert_with(HashMap::new); - cache_map.insert(key.to_string(), value); - self - } - - pub fn set_error(mut self, error: Box) -> Self { - self.result = Err(error); - self - } - - pub fn set_data(mut self, data: T) -> Self { - self.result = Ok(data); - self - } - - pub fn set_cached_map(mut self, cached_map: HashMap) -> Self { - self.cached_map = Some(cached_map); - self - } -} - -#[async_trait] -pub trait BaseProvider { - fn get_cache_request_key( - &self, - function_type: &FunctionType, - data_map: &DataMap, - ) -> Vec; - - async fn check_app_available(&self, fin: &FIn) -> FOut; - - async fn get_latest_release(&self, fin: &FIn) -> FOut { - let result = self.get_releases(fin).await; - - let fout_result = match result.result { - Ok(releases) => { - let release = releases.first().cloned(); - if let Some(release) = release { - Ok(release) - } else { - Err( - Box::new(std::io::Error::new(std::io::ErrorKind::NotFound, "no data")) - as Box, - ) - } - } - Err(e) => Err(e), - }; - - FOut { - result: fout_result, - cached_map: result.cached_map, - } - } - - async fn get_releases(&self, fin: &FIn) -> FOut>; - - /// Get download info for an app's asset. Default returns empty (not supported). - /// Only OutsideProvider (Kotlin hubs) needs to implement this. - async fn get_download(&self, _fin: &FIn, _asset_index: &[i32]) -> FOut> { - FOut::new_empty() - } -} - -pub trait BaseProviderExt: BaseProvider { - fn url_proxy_map(&self, fin: &FIn) -> HashMap { - let hub_data = fin.data_map.hub_data; - if let Some(proxy_map) = hub_data.get(REVERSE_PROXY) { - proxy_map - .lines() - .map(|line| { - let mut parts = line.splitn(2, "->"); - let url_prefix = parts.next().unwrap_or_default().trim(); - let proxy_url = parts.next().unwrap_or_default().trim(); - (url_prefix.to_string(), proxy_url.to_string()) - }) - .filter(|v| !v.0.is_empty() && !v.1.is_empty()) - .collect::>() - } else { - HashMap::new() - } - } - - fn replace_proxy_url(&self, fin: &FIn, url: &str) -> String { - let mut result_url = url.to_string(); - for (url_prefix, proxy_url) in self.url_proxy_map(fin).iter() { - let regex_prefix = "regex:"; - if let Some(stripped) = url_prefix.strip_prefix(regex_prefix) { - let url_prefix = &stripped.trim(); - if let Ok(re) = Regex::new(url_prefix) { - result_url = re.replace_all(&result_url, proxy_url.clone()).to_string(); - } - } else { - result_url = result_url.replace(url_prefix, proxy_url); - } - } - result_url - } -} - -pub const ANDROID_APP_TYPE: &str = "android_app_package"; -pub const ANDROID_MAGISK_MODULE_TYPE: &str = "android_magisk_module"; -pub const ANDROID_CUSTOM_SHELL: &str = "android_custom_shell"; -pub const ANDROID_CUSTOM_SHELL_ROOT: &str = "android_custom_shell_root"; - -pub const KEY_REPO_URL: &str = "repo_url"; -pub const KEY_REPO_API_URL: &str = "repo_api_url"; - -pub const REVERSE_PROXY: &str = "reverse_proxy"; - -#[cfg(test)] -mod tests { - use super::*; - - pub struct MockProvider; - - impl MockProvider { - pub fn new() -> MockProvider { - MockProvider {} - } - } - - #[async_trait] - impl BaseProvider for MockProvider { - fn get_cache_request_key( - &self, - function_type: &FunctionType, - data_map: &DataMap, - ) -> Vec { - let key_name = match function_type { - FunctionType::CheckAppAvailable => "check_app_available", - FunctionType::GetLatestRelease => "get_latest_release", - FunctionType::GetReleases => "get_releases", - FunctionType::GetDownload => "get_download", - }; - let id_map = data_map.app_data; - vec![format!( - "{}:{}", - key_name, - id_map - .iter() - .map(|(k, v)| format!("{}={}", k, v)) - .collect::>() - .join(",") - )] - } - - async fn check_app_available(&self, fin: &FIn) -> FOut { - let id_map = fin.data_map.app_data; - let cache_map = fin.cache_map.clone(); - FOut::new(cache_map.unwrap_or_default().contains_key(id_map["id"])) - } - - async fn get_releases(&self, fin: &FIn) -> FOut> { - let id_map = fin.data_map.app_data; - let cache_map = fin.cache_map.clone(); - FOut::new( - cache_map - .unwrap_or_default() - .get(id_map["id"]) - .unwrap() - .iter() - .map(|i| ReleaseData { - version_number: i.to_string(), - changelog: "".to_string(), - assets: vec![], - extra: None, - }) - .collect::>(), - ) - } - } - - impl BaseProviderExt for MockProvider {} - - #[tokio::test] - async fn test_get_cache_request_key() { - let mock = MockProvider::new(); - let data_map = DataMap { - app_data: &AppDataMap::from([("id", "123")]), - hub_data: &HubDataMap::new(), - }; - - let key = mock.get_cache_request_key(&FunctionType::CheckAppAvailable, &data_map); - assert_eq!(key, vec!["check_app_available:id=123"]); - let key = mock.get_cache_request_key(&FunctionType::GetReleases, &data_map); - assert_eq!(key, vec!["get_releases:id=123"]); - } - - #[tokio::test] - async fn test_check_app_available() { - let mock = MockProvider::new(); - let id_map = AppDataMap::from([("id", "123")]); - let cache_map = CacheMap::from([("123".to_string(), Bytes::from(vec![1u8]))]); - - let fin = FIn { - data_map: DataMap { - app_data: &id_map, - hub_data: &BTreeMap::new(), - }, - cache_map: Some(cache_map), - }; - - let available = mock.check_app_available(&fin).await; - assert_eq!(available.result.ok(), Some(true)); - } - - #[tokio::test] - async fn test_get_releases() { - let mock = MockProvider::new(); - let cache_map = CacheMap::from([("123".to_string(), Bytes::from(vec![1u8, 2u8, 3u8]))]); - - let fin = FIn { - data_map: DataMap { - app_data: &AppDataMap::from([("id", "123")]), - hub_data: &BTreeMap::new(), - }, - cache_map: Some(cache_map), - }; - - let releases = mock.get_releases(&fin).await; - assert_eq!(releases.result.unwrap().len(), 3); - } - - #[tokio::test] - async fn test_get_latest_release() { - let mock = MockProvider::new(); - let cache_map = CacheMap::from([("123".to_string(), Bytes::from(vec![1u8, 2u8, 3u8]))]); - - let fin = FIn { - data_map: DataMap { - app_data: &AppDataMap::from([("id", "123")]), - hub_data: &BTreeMap::new(), - }, - cache_map: Some(cache_map), - }; - - let latest_release = mock.get_latest_release(&fin).await; - let latest_version = latest_release.result.unwrap().version_number; - assert_eq!(latest_version, "1"); - } - - #[test] - fn test_replace_proxy_url() { - let mock = MockProvider::new(); - let url = "https://github.com"; - let url_r = "https://github.com.proxy"; - let proxy_url = format!("{} -> {}", url, url_r); - let data_map = HubDataMap::from([("reverse_proxy", proxy_url.as_str())]); - let result = mock.replace_proxy_url( - &FIn::new_with_frag(&AppDataMap::new(), &data_map, None), - url, - ); - assert_eq!(result, url_r); - - let proxy_url = format!("{}->{}", url, url_r); - let data_map = HubDataMap::from([("reverse_proxy", proxy_url.as_str())]); - let result = mock.replace_proxy_url( - &FIn::new_with_frag(&AppDataMap::new(), &data_map, None), - url, - ); - assert_eq!(result, url_r); - - let url_r = format!("{} -> proxy", url); - let proxy_url = format!(" {} ->{} ", url, url_r); - let data_map = HubDataMap::from([("reverse_proxy", proxy_url.as_str())]); - let result = mock.replace_proxy_url( - &FIn::new_with_frag(&AppDataMap::new(), &data_map, None), - url, - ); - assert_eq!(result, url_r); - } - - #[test] - fn test_replace_proxy_url_with_regex() { - let mock = MockProvider::new(); - - let regex_url = "regex:^https:.*/"; - let url_r = "https://github-proxy.com/"; - - let proxy_url = format!("{} -> {}", regex_url, url_r); - let data_map = HubDataMap::from([("reverse_proxy", proxy_url.as_str())]); - - let url = "https://github.com/GitHub"; - let expected_url = "https://github-proxy.com/GitHub"; - - let result = mock.replace_proxy_url( - &FIn::new_with_frag(&AppDataMap::new(), &data_map, None), - url, - ); - - assert_eq!( - result, expected_url, - "URL should be rewritten to use the proxy domain." - ); - - let non_matching_url = "http://example.com"; - let result = mock.replace_proxy_url( - &FIn::new_with_frag(&AppDataMap::new(), &data_map, None), - non_matching_url, - ); - - assert_eq!( - result, non_matching_url, - "Non-matching URL should not be rewritten." - ); - } - - #[test] - fn test_replace_proxy_url_multiple() { - let mock = MockProvider::new(); - let url = "https://github.com"; - let proxy_url = "https -> http\ngithub -> github-proxy"; - let url_r = "http://github-proxy.com"; - let data_map = HubDataMap::from([("reverse_proxy", proxy_url)]); - let result = mock.replace_proxy_url( - &FIn::new_with_frag(&AppDataMap::new(), &data_map, None), - url, - ); - assert_eq!(result, url_r); - } -} diff --git a/src/websdk/repo/provider/fdroid.rs b/src/websdk/repo/provider/fdroid.rs deleted file mode 100644 index 9e2f276..0000000 --- a/src/websdk/repo/provider/fdroid.rs +++ /dev/null @@ -1,356 +0,0 @@ -use async_trait::async_trait; -use bytes::Bytes; -use quick_xml::events::Event; -use quick_xml::Reader; -use std::collections::HashMap; -use std::error::Error; - -use crate::utils::http::{get, head, http_status_is_ok}; - -use super::super::data::release::*; -use super::base_provider::*; - -const FDROID_URL: &str = "https://f-droid.org"; - -pub struct FDroidProvider; - -impl FDroidProvider { - pub fn new() -> FDroidProvider { - FDroidProvider {} - } - - pub fn get_api_url(url: &str) -> String { - format!("{}/repo/index.xml", url) - } - - fn get_urls(data_map: &DataMap) -> (String, String) { - let url = data_map.hub_data.get(KEY_REPO_URL).unwrap_or(&FDROID_URL); - let api_url = if let Some(api_url) = data_map.hub_data.get(KEY_REPO_API_URL) { - api_url.to_string() - } else { - FDroidProvider::get_api_url(url) - }; - (url.to_string(), api_url) - } -} - -impl BaseProviderExt for FDroidProvider {} - -#[async_trait] -impl BaseProvider for FDroidProvider { - fn get_cache_request_key( - &self, - function_type: &FunctionType, - data_map: &DataMap, - ) -> Vec { - let (url, api_url) = FDroidProvider::get_urls(data_map); - let id_map = data_map.app_data; - match function_type { - FunctionType::CheckAppAvailable => vec![format!( - "{}/packages/{}/HEAD", - url, id_map[ANDROID_APP_TYPE] - )], - FunctionType::GetLatestRelease | FunctionType::GetReleases => vec![api_url.to_string()], - FunctionType::GetDownload => vec![], - } - } - - async fn check_app_available(&self, fin: &FIn) -> FOut { - let (url, _) = FDroidProvider::get_urls(&fin.data_map); - let id_map = fin.data_map.app_data; - let package_id = id_map[ANDROID_APP_TYPE]; - let api_url = format!("{}/packages/{}", url, package_id); - let api_url = self.replace_proxy_url(fin, &api_url); - - if let Ok(parsed_url) = api_url.parse() { - if let Ok(rsp) = head(parsed_url, &HashMap::new()).await { - return FOut::new(http_status_is_ok(rsp.status)); - } - } - FOut::new_empty() - } - - async fn get_releases(&self, fin: &FIn) -> FOut> { - let (url, api_url) = FDroidProvider::get_urls(&fin.data_map); - let id_map = fin.data_map.app_data; - let package_id = id_map[ANDROID_APP_TYPE]; - let api_url = self.replace_proxy_url(fin, &api_url); - let cache_key = self - .get_cache_request_key(&FunctionType::GetReleases, &fin.data_map) - .first() - .unwrap() - .clone(); - let mut cache_map_fout = CacheMap::new(); - let index_cache = fin.get_cache(&cache_key); - let mut index: Option = None; - if let Some(i) = index_cache { - index = Some(i.clone()); - } else if let Ok(parsed_url) = api_url.parse() { - if let Ok(rsp) = get(parsed_url, &HashMap::new()).await { - index = rsp.body; - cache_map_fout.insert(cache_key.to_string(), index.clone().unwrap()); - } - }; - if index.is_none() { - return FOut::new_empty(); - } - let mut releases_fout = Vec::new(); - if let Ok(content) = std::str::from_utf8(&index.unwrap()) { - let mut reader = Reader::from_str(content.trim()); - loop { - let (xml_package_id, releases) = - FDroidProvider::get_releases_from_xml(&mut reader, &url) - .await - .unwrap(); - if xml_package_id == package_id { - releases_fout = releases; - } - if xml_package_id.is_empty() { - break; - } - } - } - let mut fout = FOut::new(releases_fout); - if !cache_map_fout.is_empty() { - fout = fout.set_cached_map(cache_map_fout); - } - fout - } -} - -type Result = std::result::Result>; - -impl FDroidProvider { - async fn decode_package_xml(reader: &mut Reader<&[u8]>, url: &str) -> Result { - let xml_key = b"package"; - let mut version_number = String::new(); - let mut changelog = String::new(); - let mut file_name = String::new(); - let mut extra = HashMap::new(); - - let mut current_tag = String::new(); - loop { - match reader.read_event() { - Err(e) => return Err(Box::new(e)), - Ok(Event::Eof) => break, - Ok(Event::Start(e)) => { - let name = e.name(); - current_tag = String::from_utf8_lossy(name.as_ref()).to_string(); - } - Ok(Event::End(e)) => { - if e.name().as_ref() == xml_key { - break; - } - } - Ok(Event::Text(e)) => { - if let Ok(text) = e.decode() { - match current_tag.as_str() { - "version" => version_number += text.as_ref(), - "changelog" => changelog += text.as_ref(), - "versionCode" | "nativecode" => { - extra.insert(current_tag.clone(), text.to_string()); - } - "apkname" => file_name += text.as_ref(), - _ => (), - } - } - } - _ => (), - }; - } - let download_url = format!("{}/{}", url, file_name); - let file_type = file_name.split('.').next_back().unwrap_or("").to_string(); - - let extra = if extra.is_empty() { None } else { Some(extra) }; - Ok(ReleaseData { - version_number, - changelog, - assets: vec![AssetData { - file_name, - file_type, - download_url, - }], - extra, - }) - } - async fn decode_release_xml(reader: &mut Reader<&[u8]>, url: &str) -> Result> { - let mut releases = Vec::new(); - let mut changelog = String::new(); - - let mut current_tag = String::new(); - loop { - match reader.read_event() { - Err(e) => return Err(Box::new(e)), - Ok(Event::Eof) => break, - Ok(Event::Start(e)) => { - let name = e.name(); - match name.as_ref() { - b"package" => { - releases.push(FDroidProvider::decode_package_xml(reader, url).await?); - } - _ => { - current_tag = String::from_utf8_lossy(name.as_ref()).to_string(); - } - } - } - Ok(Event::End(e)) => { - if e.name().as_ref() == b"application" { - break; - } - } - Ok(Event::Text(e)) => { - if let Ok(text) = e.decode() { - if current_tag.as_str() == "changelog" { - changelog += text.as_ref() - } - } - } - _ => (), - }; - } - if !changelog.is_empty() { - if let Some(release) = releases.first_mut() { - release.changelog = changelog.clone(); - } - } - Ok(releases) - } - - async fn get_releases_from_xml( - reader: &mut Reader<&[u8]>, - url: &str, - ) -> Result<(String, Vec)> { - let mut package_id = String::new(); - loop { - match reader.read_event() { - Err(e) => return Err(Box::new(e)), - Ok(Event::Eof) => return Ok((package_id, Vec::new())), - Ok(Event::Start(e)) => { - if e.name().as_ref() == b"application" { - for attr in e.attributes().filter_map(|id| id.ok()) { - if attr.key.as_ref() == b"id" { - package_id = - String::from_utf8_lossy(attr.value.as_ref()).to_string(); - let releases = - FDroidProvider::decode_release_xml(reader, url).await?; - return Ok((package_id, releases)); - } - } - } - } - _ => (), - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use mockito::Server; - use std::fs; - - #[tokio::test] - async fn test_check_app_available() { - let package_id = "com.termux"; - let mut server = Server::new_async().await; - let _m = server - .mock("GET", format!("/packages/{}", package_id).as_str()) - .with_status(200) - .create_async() - .await; - - let provider = FDroidProvider::new(); - let app_data = AppDataMap::from([(ANDROID_APP_TYPE, package_id)]); - let proxy_url = format!("{} -> {}", FDROID_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - let fin = FIn::new_with_frag(&app_data, &hub_data, None); - let fout = provider.check_app_available(&fin).await; - assert!(fout.result.unwrap()); - } - - #[tokio::test] - async fn test_check_app_available_nonexist() { - let package_id = "com.termux"; - let mut server = Server::new_async().await; - let _m = server - .mock("GET", format!("/packages/{}", package_id).as_str()) - .with_status(200) - .create_async() - .await; - - let provider = FDroidProvider::new(); - let nonexist_package_id = "nonexist"; - let app_data = AppDataMap::from([(ANDROID_APP_TYPE, nonexist_package_id)]); - let proxy_url = format!("{} -> {}", FDROID_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - let fin = FIn::new_with_frag(&app_data, &hub_data, None); - let fout = provider.check_app_available(&fin).await; - assert!(!fout.result.unwrap()); - } - - #[tokio::test] - async fn test_get_releases() { - let body = fs::read_to_string("tests/files/web/f-droid.xml").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/repo/index.xml") - .with_status(200) - .with_body(body) - .create(); - - let package_id = "org.fdroid.fdroid.privileged"; - let provider = FDroidProvider::new(); - let app_data = AppDataMap::from([(ANDROID_APP_TYPE, package_id)]); - let proxy_url = format!("{} -> {}", FDROID_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - let fin = FIn::new_with_frag(&app_data, &hub_data, None); - let fout = provider.get_releases(&fin).await; - let releases = fout.result.unwrap(); - assert!(!releases.is_empty()); - assert_eq!(releases[0].assets[0].file_type, "apk"); - } - - #[tokio::test] - async fn test_get_releases_nonexist() { - let body = fs::read_to_string("tests/files/web/f-droid.xml").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/repo/index.xml") - .with_status(200) - .with_body(body) - .create(); - - let package_id = "nonexist"; - let provider = FDroidProvider::new(); - let app_data = AppDataMap::from([(ANDROID_APP_TYPE, package_id)]); - let proxy_url = format!("{} -> {}", FDROID_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - let fin = FIn::new_with_frag(&app_data, &hub_data, None); - let fout = provider.get_releases(&fin).await; - let releases = fout.result.unwrap(); - assert!(releases.is_empty()); - } - - #[tokio::test] - async fn test_get_releases_assets_type() { - let body = fs::read_to_string("tests/files/web/f-droid.xml").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/repo/index.xml") - .with_status(200) - .with_body(body) - .create(); - - let package_id = "org.fdroid.fdroid.privileged.ota"; - let provider = FDroidProvider::new(); - let app_data = AppDataMap::from([(ANDROID_APP_TYPE, package_id)]); - let proxy_url = format!("{} -> {}", FDROID_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - let fin = FIn::new_with_frag(&app_data, &hub_data, None); - let fout = provider.get_releases(&fin).await; - let releases = fout.result.unwrap(); - assert!(!releases.is_empty()); - assert_eq!(releases[0].assets[0].file_type, "zip"); - } -} diff --git a/src/websdk/repo/provider/github.rs b/src/websdk/repo/provider/github.rs deleted file mode 100644 index f7e8bc8..0000000 --- a/src/websdk/repo/provider/github.rs +++ /dev/null @@ -1,262 +0,0 @@ -use async_trait::async_trait; -use bytes::Bytes; -use serde_json::Value; -use std::collections::HashMap; - -use super::super::data::release::*; -use super::base_provider::*; - -use crate::utils::{ - http::{get, head, http_status_is_ok}, - versioning::Version, -}; - -pub const GITHUB_API_URL: &str = "https://api.github.com"; -const GITHUB_URL: &str = "https://github.com"; - -const VERSION_NUMBER_KEY: &str = "version_number_key"; -const VERSION_CODE_KEY: &str = "version_code_key"; - -pub struct GitHubProvider; - -impl GitHubProvider { - pub fn new() -> Self { - GitHubProvider {} - } - - fn get_token_header(&self, fin: &FIn) -> HashMap { - let mut map = HashMap::new(); - // check if token is empty or blank - let token = fin - .data_map - .hub_data - .get("token") - .filter(|t| !t.trim().is_empty()) - .or_else(|| fin.data_map.app_data.get("token")); - if let Some(token) = token { - map.insert("Authorization".to_string(), format!("Bearer {}", token)); - } - map - } -} - -impl BaseProviderExt for GitHubProvider {} - -#[async_trait] -impl BaseProvider for GitHubProvider { - fn get_cache_request_key( - &self, - function_type: &FunctionType, - data_map: &DataMap, - ) -> Vec { - let id_map = data_map.app_data; - match function_type { - FunctionType::CheckAppAvailable => vec![format!( - "{}/{}/{}/HEAD", - GITHUB_URL, id_map["owner"], id_map["repo"] - )], - FunctionType::GetLatestRelease | FunctionType::GetReleases => vec![format!( - "{}/repos/{}/{}/releases", - GITHUB_API_URL, id_map["owner"], id_map["repo"] - )], - FunctionType::GetDownload => vec![], - } - } - - async fn check_app_available(&self, fin: &FIn) -> FOut { - let id_map = fin.data_map.app_data; - let api_url = format!("{}/{}/{}", GITHUB_URL, id_map["owner"], id_map["repo"]); - let api_url = self.replace_proxy_url(fin, &api_url); - - if let Ok(parsed_url) = api_url.parse() { - if let Ok(rsp) = head(parsed_url, &HashMap::new()).await { - return FOut::new(http_status_is_ok(rsp.status)); - } - } - FOut::new_empty() - } - - async fn get_releases(&self, fin: &FIn) -> FOut> { - let id_map = fin.data_map.app_data; - let url = format!( - "{}/repos/{}/{}/releases", - GITHUB_API_URL, id_map["owner"], id_map["repo"] - ); - let url = self.replace_proxy_url(fin, &url); - let mut fout = FOut::new_empty(); - let cache_body = fin.get_cache(&url); - let mut rsp_body = None; - if cache_body.is_none() { - if let Ok(parsed_url) = url.parse() { - let header_map = { - let mut map = HashMap::new(); - map.insert("User-Agent".to_string(), "UpgradeAll-App".to_string()); - let token_map = self.get_token_header(fin); - map.extend(token_map); - map - }; - if let Ok(rsp) = get(parsed_url, &header_map).await { - if let Some(content) = rsp.body { - rsp_body = Some(content); - } - } - } - } - - let body: &Bytes; - if let Some(ref content) = rsp_body { - body = content; - } else if let Some(content) = cache_body { - body = content; - } else { - return fout; - } - - if let Ok(data) = serde_json::from_slice::>(body) { - let release_list = data - .iter() - .filter_map(|json| { - let assets_data = match json.get("assets") { - Some(assets) => assets - .as_array()? - .iter() - .filter_map(|asset| { - let file_name = asset.get("name")?.as_str()?.to_string(); - let file_type = asset.get("content_type")?.as_str()?.to_string(); - let download_url = - asset.get("browser_download_url")?.as_str()?.to_string(); - Some(AssetData { - file_name, - file_type, - download_url, - }) - }) - .collect(), - None => vec![], - }; - let mut keys_to_try = vec!["name", "tag_name"]; - if let Some(tag) = fin.data_map.hub_data.get(VERSION_NUMBER_KEY) { - keys_to_try.insert(0, tag); - } - let mut version_number: Option = None; - - for key in keys_to_try.iter() { - if let Some(value) = json.get(key).and_then(|v| v.as_str()) { - if Version::new(value.to_string()).is_valid() { - version_number = Some(value.to_string()); - break; - } - } - } - let changelog = json.get("body")?.as_str()?.to_string(); - - let mut extra = None; - if let Some(tag) = fin.data_map.hub_data.get(VERSION_CODE_KEY) { - if let Some(value) = json.get(tag) { - extra = Some(HashMap::from([(tag.to_string(), value.to_string())])); - } - } - Some(ReleaseData { - version_number: version_number?.to_string(), - changelog, - assets: assets_data, - extra, - }) - }) - .collect::>(); - fout = fout.set_data(release_list); - }; - - if let Some(content) = rsp_body { - fout.set_cached_map(HashMap::from([(url, content)])) - } else { - fout - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use mockito::Server; - use std::fs; - - #[tokio::test] - async fn test_check_app_available() { - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/DUpdateSystem/UpgradeAll") - .with_status(200) - .create_async() - .await; - - let id_map = AppDataMap::from([("owner", "DUpdateSystem"), ("repo", "UpgradeAll")]); - let proxy_url = format!("{} -> {}", GITHUB_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - - let github_provider = GitHubProvider::new(); - assert!(github_provider - .check_app_available(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap()); - } - - #[tokio::test] - async fn test_get_releases() { - let body = fs::read_to_string("tests/files/web/github_api_release.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/repos/DUpdateSystem/UpgradeAll/releases") - .with_status(200) - .with_body(body) - .create(); - - let id_map = AppDataMap::from([("owner", "DUpdateSystem"), ("repo", "UpgradeAll")]); - let proxy_url = format!("{} -> {}", GITHUB_API_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - - let github_provider = GitHubProvider::new(); - let releases = github_provider - .get_releases(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap(); - - let release_json = - fs::read_to_string("tests/files/data/provider_github_release.json").unwrap(); - let releases_saved = serde_json::from_str::>(&release_json).unwrap(); - assert_eq!(releases, releases_saved) - } - - #[tokio::test] - async fn test_get_releases_token() { - let mut id_map = AppDataMap::from([("owner", "DUpdateSystem"), ("repo", "UpgradeAll")]); - let test_token = std::env::var("GITHUB_TOKEN"); - if test_token.is_err() { - return; - } - let test_token = test_token.unwrap(); - let hub_data = HubDataMap::from([("token", test_token.as_str())]); - - let github_provider = GitHubProvider::new(); - let releases = github_provider - .get_releases(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap(); - - assert!(!releases.is_empty()); - - let hub_data = HubDataMap::from([("token", " ")]); - id_map.insert("token", &test_token); - - let releases = github_provider - .get_releases(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap(); - - assert!(!releases.is_empty()); - } -} diff --git a/src/websdk/repo/provider/gitlab.rs b/src/websdk/repo/provider/gitlab.rs deleted file mode 100644 index d3a67ad..0000000 --- a/src/websdk/repo/provider/gitlab.rs +++ /dev/null @@ -1,327 +0,0 @@ -use async_trait::async_trait; -use bytes::Bytes; -use serde_json::Value; -use std::collections::HashMap; - -use super::super::data::release::*; -use super::base_provider::*; -use markdown::{mdast::Node, to_mdast, ParseOptions}; - -use crate::utils::{ - http::{get, head, http_status_is_ok}, - versioning::Version, -}; - -const GITLAB_URL: &str = "https://gitlab.com"; -const GITLAB_API_URL: &str = "https://gitlab.com/api/v4/projects"; - -const VERSION_NUMBER_KEY: &str = "version_number_key"; - -pub struct GitLabProvider; - -impl GitLabProvider { - pub fn new() -> GitLabProvider { - GitLabProvider {} - } -} - -impl BaseProviderExt for GitLabProvider {} - -impl GitLabProvider { - async fn get_project_id(&self, fin: &FIn<'_>) -> Option { - let id_map = fin.data_map.app_data; - let api_url = format!( - "{}/{}%2F{}", - GITLAB_API_URL, id_map["owner"], id_map["repo"] - ); - let api_url = self.replace_proxy_url(fin, &api_url); - - if let Ok(parsed_url) = api_url.parse() { - if let Ok(rsp) = get(parsed_url, &HashMap::new()).await { - if let Some(body) = rsp.body { - if let Ok(data) = serde_json::from_slice::>(&body) { - return Some(data.get("id")?.as_number()?.to_string()); - } - } - } - } - None - } - - fn try_get_download_url_from_changelog(&self, changelog: &str) -> Vec<(String, String)> { - let changelog_ast = to_mdast(changelog, &ParseOptions::default()); - if changelog_ast.is_err() { - return vec![]; - } - let changelog_ast = changelog_ast.unwrap(); - - fn get_link_text(nodes: &Vec) -> Vec<(String, String)> { - let mut url_map = vec![]; - for node in nodes { - let mut name = String::new(); - if let Node::Link(link) = node { - for c in &link.children { - if let Node::Text(text) = c { - name.push_str(&text.value); - } - } - url_map.push((name, link.url.to_string())); - } - if let Some(child) = node.children() { - url_map.extend(get_link_text(child)); - } - } - url_map - } - if let Some(children) = changelog_ast.children() { - return get_link_text(children); - } - vec![] - } - - fn fix_download_url(&self, download_url: &str, project_id: &str) -> String { - if download_url.starts_with("/uploads/") { - return format!("{}/-/project/{}{}", GITLAB_URL, project_id, download_url); - } - download_url.to_string() - } -} - -#[async_trait] -impl BaseProvider for GitLabProvider { - fn get_cache_request_key( - &self, - function_type: &FunctionType, - data_map: &DataMap, - ) -> Vec { - let id_map = data_map.app_data; - match function_type { - FunctionType::CheckAppAvailable => vec![format!( - "{}/{}/{}/HEAD", - GITLAB_URL, id_map["owner"], id_map["repo"] - )], - FunctionType::GetLatestRelease | FunctionType::GetReleases => vec![format!( - "{}/{}/{}/releases", - GITLAB_API_URL, id_map["owner"], id_map["repo"] - )], - FunctionType::GetDownload => vec![], - } - } - - async fn check_app_available(&self, fin: &FIn) -> FOut { - let id_map = fin.data_map.app_data; - let api_url = format!("{}/{}/{}", GITLAB_URL, id_map["owner"], id_map["repo"]); - let api_url = self.replace_proxy_url(fin, &api_url); - - if let Ok(parsed_url) = api_url.parse() { - if let Ok(rsp) = head(parsed_url, &HashMap::new()).await { - return FOut::new(http_status_is_ok(rsp.status)); - } - } - FOut::new_empty() - } - - async fn get_releases(&self, fin: &FIn) -> FOut> { - let id_map = fin.data_map.app_data; - let url = format!( - "{}/{}%2F{}/releases", - GITLAB_API_URL, id_map["owner"], id_map["repo"] - ); - let url = self.replace_proxy_url(fin, &url); - let mut fout = FOut::new_empty(); - let cache_body = fin.get_cache(&url); - let mut rsp_body = None; - if cache_body.is_none() { - if let Ok(parsed_url) = url.parse() { - let header_map = { - let mut map = HashMap::new(); - map.insert("User-Agent".to_string(), "Awesome-Octocat-App".to_string()); - map - }; - if let Ok(rsp) = get(parsed_url, &header_map).await { - if let Some(content) = rsp.body { - rsp_body = Some(content); - } - } - } - } - - let body: &Bytes; - if let Some(ref content) = rsp_body { - body = content; - } else if let Some(content) = cache_body { - body = content; - } else { - return fout; - } - - if let Ok(data) = serde_json::from_slice::>(body) { - let mut release_list = data - .iter() - .filter_map(|json| { - let assets_data = match json.get("assets")?.get("links") { - Some(links) => links - .as_array()? - .iter() - .filter_map(|asset| { - let file_name = asset.get("name")?.as_str()?.to_string(); - let file_type = asset.get("link_type")?.as_str()?.to_string(); - let download_url = asset.get("url")?.as_str()?.to_string(); - Some(AssetData { - file_name, - file_type, - download_url, - }) - }) - .collect(), - None => vec![], - }; - let mut keys_to_try = vec!["name", "tag_name"]; - if let Some(tag) = fin.data_map.hub_data.get(VERSION_NUMBER_KEY) { - keys_to_try.insert(0, tag); - } - let mut version_number: Option = None; - - for key in keys_to_try.iter() { - if let Some(value) = json.get(key).and_then(|v| v.as_str()) { - if Version::new(value.to_string()).is_valid() { - version_number = Some(value.to_string()); - break; - } - } - } - let changelog = json.get("description")?.as_str()?.to_string(); - let extra_download_url = self.try_get_download_url_from_changelog(&changelog); - let assets_data = assets_data - .into_iter() - .chain(extra_download_url.into_iter().map(|(k, v)| AssetData { - file_name: k, - file_type: "".to_string(), - download_url: v, - })) - .collect(); - Some(ReleaseData { - version_number: version_number?.to_string(), - changelog, - assets: assets_data, - extra: None, - }) - }) - .collect::>(); - let mut project_id = None; - for release in release_list.iter_mut() { - for asset in release.assets.iter_mut() { - if asset.download_url.starts_with("/uploads/") { - if project_id.is_none() { - project_id = self.get_project_id(fin).await; - } - if let Some(project_id) = &project_id { - asset.download_url = - self.fix_download_url(&asset.download_url, project_id); - } - } - } - } - fout = fout.set_data(release_list); - }; - - if let Some(content) = rsp_body { - fout.set_cached_map(HashMap::from([(url, content)])) - } else { - fout - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use mockito::Server; - use std::fs; - - #[tokio::test] - async fn test_check_app_available() { - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/fdroid/fdroidclient") - .with_status(200) - .create_async() - .await; - - let id_map = AppDataMap::from([("owner", "fdroid"), ("repo", "fdroidclient")]); - let proxy_url = format!("{} -> {}", GITLAB_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - - let gitlab_provider = GitLabProvider::new(); - assert!(gitlab_provider - .check_app_available(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap()); - } - - #[tokio::test] - async fn test_get_releases() { - let body = fs::read_to_string("tests/files/web/gitlab_api_release.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/fdroid%2Ffdroidclient/releases") - .with_status(200) - .with_body(body) - .create(); - - let id_map = AppDataMap::from([("owner", "fdroid"), ("repo", "fdroidclient")]); - let proxy_url = format!("{} -> {}", GITLAB_API_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - - let gitlab_provider = GitLabProvider::new(); - let releases = gitlab_provider - .get_releases(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap(); - - let release_json = - fs::read_to_string("tests/files/data/provider_gitlab_release.json").unwrap(); - let releases_saved = serde_json::from_str::>(&release_json).unwrap(); - assert_eq!(releases, releases_saved) - } - - #[tokio::test] - async fn test_try_get_download_url_from_changelog_in_release() { - let body = - fs::read_to_string("tests/files/web/gitlab_api_release_AuroraStore.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/AuroraOSS%2FAuroraStore/releases") - .with_status(200) - .with_body(body) - .create(); - let project_body = - fs::read_to_string("tests/files/web/gitlab_api_project_AuroraStore.json").unwrap(); - let _m = server - .mock("GET", "/AuroraOSS%2FAuroraStore") - .with_status(200) - .with_body(project_body) - .create_async() - .await; - - let id_map = AppDataMap::from([("owner", "AuroraOSS"), ("repo", "AuroraStore")]); - let proxy_url = format!("{} -> {}", GITLAB_API_URL, server.url()); - let hub_data = HubDataMap::from([(REVERSE_PROXY, proxy_url.as_str())]); - - let gitlab_provider = GitLabProvider::new(); - let releases = gitlab_provider - .get_releases(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap(); - - let release_json = - fs::read_to_string("tests/files/data/provider_gitlab_release_AuroraStore.json") - .unwrap(); - let releases_saved = serde_json::from_str::>(&release_json).unwrap(); - - assert_eq!(releases, releases_saved) - } -} diff --git a/src/websdk/repo/provider/lsposed_repo.rs b/src/websdk/repo/provider/lsposed_repo.rs deleted file mode 100644 index c7dc50c..0000000 --- a/src/websdk/repo/provider/lsposed_repo.rs +++ /dev/null @@ -1,224 +0,0 @@ -use std::collections::HashMap; - -use async_trait::async_trait; -use bytes::Bytes; -use serde_json::Value; - -use crate::utils::http::get; -use crate::utils::versioning::Version; - -use super::super::data::release::*; -use super::base_provider::*; - -const LSPOSED_REPO_API_URL: &str = "https://modules.lsposed.org/modules.json"; - -pub struct LsposedRepoProvider; - -impl LsposedRepoProvider { - pub fn new() -> Self { - LsposedRepoProvider {} - } - - fn get_app_json(package_name: &str, body: &Bytes) -> Option { - if let Ok(json) = serde_json::from_slice::>(body) { - for i in json { - if let Some(name) = i.get("name") { - if let Some(name_str) = name.as_str() { - if name_str == package_name { - return Some(i); - } - } - } - } - } - None - } -} - -impl BaseProviderExt for LsposedRepoProvider {} - -#[async_trait] -impl BaseProvider for LsposedRepoProvider { - fn get_cache_request_key( - &self, - _function_type: &FunctionType, - _data_map: &DataMap, - ) -> Vec { - vec![LSPOSED_REPO_API_URL.to_string()] - } - - async fn check_app_available(&self, fin: &FIn) -> FOut { - let url = self.replace_proxy_url(fin, LSPOSED_REPO_API_URL); - let mut fout = FOut::new_empty(); - let cache_body = fin.get_cache(&url); - let mut rsp_body = None; - if cache_body.is_none() { - if let Ok(parsed_url) = url.parse() { - let map = HashMap::new(); - if let Ok(rsp) = get(parsed_url, &map).await { - if let Some(content) = rsp.body { - fout = fout.set_cache(&url, content.clone()); - rsp_body = Some(content); - } - } - } - } - let body: &Bytes; - if let Some(ref content) = rsp_body { - body = content; - } else if let Some(content) = cache_body { - body = content; - } else { - return fout; - } - let id_map = fin.data_map.app_data; - let package_id = id_map[ANDROID_APP_TYPE]; - let json = LsposedRepoProvider::get_app_json(package_id, body); - fout.set_data(json.is_some()) - } - - async fn get_releases(&self, fin: &FIn) -> FOut> { - let url = self.replace_proxy_url(fin, LSPOSED_REPO_API_URL); - let mut fout = FOut::new_empty(); - let cache_body = fin.get_cache(&url); - let mut rsp_body = None; - if cache_body.is_none() { - if let Ok(parsed_url) = url.parse() { - let map = HashMap::new(); - if let Ok(rsp) = get(parsed_url, &map).await { - if let Some(content) = rsp.body { - fout = fout.set_cache(&url, content.clone()); - rsp_body = Some(content); - } - } - } - } - let body: &Bytes; - if let Some(ref content) = rsp_body { - body = content; - } else if let Some(content) = cache_body { - body = content; - } else { - return fout; - } - let id_map = fin.data_map.app_data; - let package_id = id_map[ANDROID_APP_TYPE]; - let json = LsposedRepoProvider::get_app_json(package_id, body); - if let Some(json) = json { - if let Some(releases_block) = json.get("releases") { - if let Some(release_json) = releases_block.as_array() { - let release_list = release_json - .iter() - .filter_map(|json| { - if let Some(assets_block) = json.get("releaseAssets") { - if let Some(assets) = assets_block.as_array() { - let assets_data = - assets - .iter() - .filter_map(|asset| { - Some(AssetData { - file_name: asset.get("name")?.as_str()?.to_string(), - file_type: asset - .get("contentType")? - .as_str() - .unwrap_or("application/vnd.android.package-archive") - .to_string(), - download_url: asset - .get("downloadUrl")? - .as_str()? - .to_string(), - }) - }) - .collect(); - let mut version_number: Option = None; - - for key in &["name", "tagName"] { - if let Some(value) = json.get(key).and_then(|v| v.as_str()) - { - if Version::new(value.to_string()).is_valid() { - version_number = Some(value.to_string()); - break; - } - } - } - let changelog = json - .get("descriptionHTML") - .and_then(|v| v.as_str()) - .unwrap_or("") - .to_string(); - return Some(ReleaseData { - version_number: version_number?.to_string(), - changelog, - assets: assets_data, - extra: None, - }); - } - } - None - }) - .collect(); - fout = fout.set_data(release_list); - } - } - } - fout - } -} - -#[cfg(test)] -mod tests { - use super::*; - use mockito::Server; - use std::fs; - - #[tokio::test] - async fn test_check_app_available() { - let body = fs::read_to_string("tests/files/web/lsposed_modules.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/modules.json") - .with_status(200) - .with_body(body) - .create_async() - .await; - - let id_map = AppDataMap::from([(ANDROID_APP_TYPE, "com.agoines.relaxhelp")]); - let proxy_url = format!("{} -> {}", LSPOSED_REPO_API_URL, server.url()); - let hub_data = HubDataMap::from([("proxy_url", proxy_url.as_str())]); - - let lsposed_provider = LsposedRepoProvider::new(); - assert!(lsposed_provider - .check_app_available(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap()); - } - - #[tokio::test] - async fn test_get_releases() { - let body = fs::read_to_string("tests/files/web/lsposed_modules.json").unwrap(); - let mut server = Server::new_async().await; - let _m = server - .mock("GET", "/modules.json") - .with_status(200) - .with_body(body) - .create_async() - .await; - - let id_map = AppDataMap::from([(ANDROID_APP_TYPE, "com.agoines.relaxhelp")]); - let proxy_url = format!("{} -> {}", LSPOSED_REPO_API_URL, server.url()); - let hub_data = HubDataMap::from([("proxy_url", proxy_url.as_str())]); - - let lsposed_provider = LsposedRepoProvider::new(); - let releases = lsposed_provider - .get_releases(&FIn::new_with_frag(&id_map, &hub_data, None)) - .await - .result - .unwrap(); - - let release_json = - fs::read_to_string("tests/files/data/provider_lsposed_releases.json").unwrap(); - let releases_saved = serde_json::from_str::>(&release_json).unwrap(); - assert_eq!(releases, releases_saved) - } -} diff --git a/src/websdk/repo/provider/outside_rpc.rs b/src/websdk/repo/provider/outside_rpc.rs deleted file mode 100644 index ddfcc79..0000000 --- a/src/websdk/repo/provider/outside_rpc.rs +++ /dev/null @@ -1,115 +0,0 @@ -use super::super::data::release::*; -use super::base_provider::*; -use crate::rpc::client::*; -use crate::rpc::data::DownloadItemData; -use async_trait::async_trait; - -pub struct OutsideProvider { - pub uuid: String, - pub url: String, -} - -impl OutsideProvider { - pub fn new(uuid: String, url: String) -> Self { - OutsideProvider { uuid, url } - } -} - -#[async_trait] -impl BaseProvider for OutsideProvider { - fn get_cache_request_key( - &self, - _function_type: &FunctionType, - _data_map: &DataMap, - ) -> Vec { - vec![] - } - - async fn check_app_available(&self, fin: &FIn) -> FOut { - FOut { - result: match Client::new(&self.url).map(|client| async move { - client - .check_app_available( - &self.uuid, - fin.data_map.app_data.to_owned(), - fin.data_map.hub_data.to_owned(), - ) - .await - }) { - Ok(result) => match result.await { - Ok(result) => Ok(result), - Err(e) => Err(Box::new(e)), - }, - Err(e) => Err(Box::new(e)), - }, - cached_map: None, - } - } - - async fn get_latest_release(&self, fin: &FIn) -> FOut { - FOut { - result: match Client::new(&self.url).map(|client| async move { - client - .get_latest_release( - &self.uuid, - fin.data_map.app_data.to_owned(), - fin.data_map.hub_data.to_owned(), - ) - .await - }) { - Ok(result) => match result.await { - Ok(result) => Ok(result), - Err(e) => Err(Box::new(e)), - }, - Err(e) => Err(Box::new(e)), - }, - cached_map: None, - } - } - - async fn get_releases(&self, fin: &FIn) -> FOut> { - FOut { - result: match Client::new(&self.url).map(|client| async move { - client - .get_releases( - &self.uuid, - fin.data_map.app_data.to_owned(), - fin.data_map.hub_data.to_owned(), - ) - .await - }) { - Ok(result) => match result.await { - Ok(result) => Ok(result), - Err(e) => Err(Box::new(e)), - }, - Err(e) => Err(Box::new(e)), - }, - cached_map: None, - } - } - - async fn get_download(&self, fin: &FIn, asset_index: &[i32]) -> FOut> { - FOut { - result: match Client::new(&self.url).map(|client| async move { - client - .get_download( - &self.uuid, - fin.data_map.app_data.to_owned(), - fin.data_map.hub_data.to_owned(), - asset_index, - ) - .await - }) { - Ok(result) => match result.await { - Ok(result) => Ok(result), - Err(e) => Err(Box::new(e)), - }, - Err(e) => Err(Box::new(e)), - }, - cached_map: None, - } - } -} - -#[cfg(test)] -mod tests {} From 475c3b2823fb9fb56cde7430711b82c2cbe79097 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 21 Jun 2026 23:53:58 +0800 Subject: [PATCH 02/71] feat(update): add deterministic update selection Add tokenized version comparison and selection policy that skips ignored versions while choosing the highest newer candidate. --- crates/getter-core/src/lib.rs | 1 + crates/getter-core/src/update.rs | 272 +++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 crates/getter-core/src/update.rs diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 45a464d..da959a3 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -11,6 +11,7 @@ use std::str::FromStr; pub mod lua; pub mod repository; +pub mod update; /// Error returned when parsing or constructing a [`PackageId`]. #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] diff --git a/crates/getter-core/src/update.rs b/crates/getter-core/src/update.rs new file mode 100644 index 0000000..ab3256c --- /dev/null +++ b/crates/getter-core/src/update.rs @@ -0,0 +1,272 @@ +//! Update selection helpers owned by getter core. + +use crate::{PackageId, SelectedUpdate, UpdateArtifact, UpdateCandidate}; +use std::cmp::Ordering; + +/// User state that affects update selection. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct UpdateSelectionPolicy { + /// Candidate version the user chose to ignore/mark as skipped. + pub ignored_version: Option, +} + +/// Compare human-facing version strings using a deterministic token ordering. +/// +/// This intentionally starts small: digit runs compare numerically, ASCII text +/// runs compare case-insensitively, common separators are ignored, and a text +/// suffix such as `beta` or `rc` sorts before the final release with the same +/// numeric prefix. +/// +/// Examples: +/// +/// - `1.10` > `1.2` +/// - `1.0.0` == `1.0` +/// - `1.0.0-beta` < `1.0.0` +pub fn compare_versions(left: &str, right: &str) -> Ordering { + let left = tokenize_version(left); + let right = tokenize_version(right); + let max_len = left.len().max(right.len()); + + for index in 0..max_len { + match (left.get(index), right.get(index)) { + (Some(left), Some(right)) => { + let ordering = left.cmp(right); + if ordering != Ordering::Equal { + return ordering; + } + } + (Some(VersionToken::Number(value)), None) if *value == 0 => continue, + (None, Some(VersionToken::Number(value))) if *value == 0 => continue, + (Some(VersionToken::Text(_)), None) => return Ordering::Less, + (None, Some(VersionToken::Text(_))) => return Ordering::Greater, + (Some(_), None) => return Ordering::Greater, + (None, Some(_)) => return Ordering::Less, + (None, None) => break, + } + } + + Ordering::Equal +} + +/// Select the best update candidate for a package. +/// +/// The selected candidate is the highest version that is newer than the +/// installed version and not equal to the user's ignored version. If no +/// installed version is known, the highest non-ignored candidate is selected. +pub fn select_update( + package_id: PackageId, + installed_version: Option<&str>, + candidates: &[UpdateCandidate], + policy: &UpdateSelectionPolicy, +) -> Option { + candidates + .iter() + .filter(|candidate| is_selectable(candidate, installed_version, policy)) + .max_by(|left, right| compare_versions(&left.version, &right.version)) + .cloned() + .map(|candidate| SelectedUpdate { + package_id, + artifact: first_artifact(&candidate), + candidate, + }) +} + +fn is_selectable( + candidate: &UpdateCandidate, + installed_version: Option<&str>, + policy: &UpdateSelectionPolicy, +) -> bool { + if policy + .ignored_version + .as_deref() + .is_some_and(|ignored| compare_versions(&candidate.version, ignored) == Ordering::Equal) + { + return false; + } + + match installed_version { + Some(installed) => compare_versions(&candidate.version, installed) == Ordering::Greater, + None => true, + } +} + +fn first_artifact(candidate: &UpdateCandidate) -> Option { + candidate.artifacts.first().cloned() +} + +#[derive(Debug, Clone, PartialEq, Eq)] +enum VersionToken { + Number(u128), + Text(String), +} + +impl Ord for VersionToken { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Self::Number(left), Self::Number(right)) => left.cmp(right), + (Self::Text(left), Self::Text(right)) => left.cmp(right), + (Self::Number(_), Self::Text(_)) => Ordering::Greater, + (Self::Text(_), Self::Number(_)) => Ordering::Less, + } + } +} + +impl PartialOrd for VersionToken { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +fn tokenize_version(version: &str) -> Vec { + let mut tokens = Vec::new(); + let mut current = String::new(); + let mut current_kind: Option = None; + + for ch in version.chars() { + let kind = TokenKind::from_char(ch); + match (kind, current_kind) { + (Some(kind), Some(existing)) if kind == existing => current.push(ch), + (Some(kind), Some(_)) => { + push_token(&mut tokens, ¤t, current_kind.expect("kind exists")); + current.clear(); + current.push(ch); + current_kind = Some(kind); + } + (Some(kind), None) => { + current.push(ch); + current_kind = Some(kind); + } + (None, Some(existing)) => { + push_token(&mut tokens, ¤t, existing); + current.clear(); + current_kind = None; + } + (None, None) => {} + } + } + + if let Some(kind) = current_kind { + push_token(&mut tokens, ¤t, kind); + } + + tokens +} + +fn push_token(tokens: &mut Vec, value: &str, kind: TokenKind) { + match kind { + TokenKind::Number => tokens.push(VersionToken::Number(parse_u128_lossy(value))), + TokenKind::Text => tokens.push(VersionToken::Text(value.to_ascii_lowercase())), + } +} + +fn parse_u128_lossy(value: &str) -> u128 { + value.parse::().unwrap_or(u128::MAX) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum TokenKind { + Number, + Text, +} + +impl TokenKind { + fn from_char(ch: char) -> Option { + if ch.is_ascii_digit() { + Some(Self::Number) + } else if ch.is_ascii_alphabetic() { + Some(Self::Text) + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn compares_numeric_segments_numerically() { + assert_eq!(compare_versions("1.10", "1.2"), Ordering::Greater); + assert_eq!(compare_versions("1.0.0", "1.0"), Ordering::Equal); + assert_eq!(compare_versions("2", "10"), Ordering::Less); + } + + #[test] + fn treats_prerelease_suffix_as_older_than_release() { + assert_eq!(compare_versions("1.0.0-beta", "1.0.0"), Ordering::Less); + assert_eq!( + compare_versions("1.0.0-rc1", "1.0.0-beta2"), + Ordering::Greater + ); + } + + #[test] + fn selects_highest_newer_candidate() { + let selected = select_update( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("1.0.0"), + &[candidate("1.0.1"), candidate("1.2.0"), candidate("0.9.0")], + &UpdateSelectionPolicy::default(), + ) + .unwrap(); + + assert_eq!(selected.candidate.version, "1.2.0"); + } + + #[test] + fn respects_ignored_version() { + let selected = select_update( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("1.0.0"), + &[candidate("1.1.0"), candidate("1.2.0")], + &UpdateSelectionPolicy { + ignored_version: Some("1.2.0".to_owned()), + }, + ) + .unwrap(); + + assert_eq!(selected.candidate.version, "1.1.0"); + } + + #[test] + fn returns_none_when_no_candidate_is_newer() { + assert!(select_update( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("2.0.0"), + &[candidate("1.9.0"), candidate("2.0.0")], + &UpdateSelectionPolicy::default(), + ) + .is_none()); + } + + #[test] + fn unknown_installed_version_selects_highest_candidate() { + let selected = select_update( + "android/org.fdroid.fdroid".parse().unwrap(), + None, + &[candidate("1.0.0-beta"), candidate("1.0.0")], + &UpdateSelectionPolicy::default(), + ) + .unwrap(); + + assert_eq!(selected.candidate.version, "1.0.0"); + assert_eq!( + selected.artifact.as_ref().unwrap().file_name.as_deref(), + Some("app.apk") + ); + } + + fn candidate(version: &str) -> UpdateCandidate { + UpdateCandidate { + version: version.to_owned(), + channel: None, + source: None, + artifacts: vec![UpdateArtifact { + name: "APK".to_owned(), + url: format!("https://example.invalid/{version}.apk"), + file_name: Some("app.apk".to_owned()), + }], + } + } +} From 2ca92b80cdc89ac9c18f45ba812ed52098d4c8f7 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 08:55:46 +0800 Subject: [PATCH 03/71] fix: satisfy clippy on stable Use sort_by_key for repository package ordering and elide needless lifetimes in highest_priority. --- crates/getter-core/src/repository.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index 0bacf47..fe9fa21 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -105,7 +105,7 @@ impl RepositoryLayout { let mut packages = Vec::new(); collect_package_files(&packages_dir, &packages_dir, &mut packages)?; - packages.sort_by(|a, b| a.id.to_string().cmp(&b.id.to_string())); + packages.sort_by_key(|package| package.id.to_string()); Ok(Self { root, @@ -224,7 +224,7 @@ pub fn package_id_from_path( }) } -pub fn highest_priority<'a, T, F>(items: &'a [T], priority: F) -> Option<&'a T> +pub fn highest_priority(items: &[T], priority: F) -> Option<&T> where F: Fn(&T) -> RepositoryPriority, { From 90c02bac7442e57d422ab0e2fd7e783c15bc11fe Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 10:04:20 +0800 Subject: [PATCH 04/71] fix(lua): support lib-prefixed repository modules Preserve the documented require("lib.xxx") contract by extending the Lua package path while keeping unprefixed lib_dir lookups for compatibility. --- crates/getter-core/src/lua.rs | 44 ++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index fd6348e..0583664 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -85,10 +85,14 @@ pub fn evaluate_package_source( fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Result<()> { let package: Table = lua.globals().get("package")?; let current_path: String = package.get("path")?; + let root_lib_pattern = repository.root.join("?.lua"); + let root_nested_lib_pattern = repository.root.join("?/init.lua"); let lib_pattern = repository.lib_dir.join("?.lua"); let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); let new_path = format!( - "{};{};{}", + "{};{};{};{};{}", + root_lib_pattern.to_string_lossy(), + root_nested_lib_pattern.to_string_lossy(), lib_pattern.to_string_lossy(), nested_lib_pattern.to_string_lossy(), current_path @@ -457,4 +461,42 @@ return android.local_app { assert_eq!(package.name, "F-Droid"); assert_eq!(package.installed.len(), 1); } + + #[test] + fn require_can_load_repository_lib_modules_with_lib_prefix() { + let (_temp, layout, package_path) = fixture_repo(); + fs::write( + layout.lib_dir.join("android.lua"), + r#" +return { + local_app = function(input) + return { + id = input.id, + name = input.name, + installed = { + { kind = "android_package", package_name = input.package_name }, + }, + } + end +} +"#, + ) + .unwrap(); + fs::write( + &package_path, + r#" +local android = require("lib.android") +return android.local_app { + id = "android/org.fdroid.fdroid", + name = "F-Droid", + package_name = "org.fdroid.fdroid", +} +"#, + ) + .unwrap(); + + let package = evaluate_package_file(&layout, &package_path).unwrap(); + assert_eq!(package.name, "F-Droid"); + assert_eq!(package.installed.len(), 1); + } } From bf932d655727ad2a9fd1c856ac2a047db3c95d54 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 10:31:26 +0800 Subject: [PATCH 05/71] fix(lua): constrain lib-prefixed module loading Support the documented require("lib.*") contract with a constrained searcher instead of exposing the repository root through package.path. --- crates/getter-core/src/lua.rs | 97 ++++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 6 deletions(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 0583664..b8e905b 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -85,19 +85,80 @@ pub fn evaluate_package_source( fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Result<()> { let package: Table = lua.globals().get("package")?; let current_path: String = package.get("path")?; - let root_lib_pattern = repository.root.join("?.lua"); - let root_nested_lib_pattern = repository.root.join("?/init.lua"); let lib_pattern = repository.lib_dir.join("?.lua"); let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); let new_path = format!( - "{};{};{};{};{}", - root_lib_pattern.to_string_lossy(), - root_nested_lib_pattern.to_string_lossy(), + "{};{};{}", lib_pattern.to_string_lossy(), nested_lib_pattern.to_string_lossy(), current_path ); - package.set("path", new_path) + package.set("path", new_path)?; + install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone()) +} + +fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> mlua::Result<()> { + let searchers: Table = package.get("searchers")?; + let searcher = lua.create_function(move |lua, module: String| { + let Some(module) = module.strip_prefix("lib.") else { + return lua + .create_string("\n\tconstrained repository lib searcher only handles lib.* modules") + .map(Value::String); + }; + + let Some(relative_module) = module_to_relative_path(module) else { + return lua + .create_string(format!( + "\n\tinvalid repository lib module name 'lib.{module}'" + )) + .map(Value::String); + }; + + let module_path = lib_dir.join(&relative_module).with_extension("lua"); + let init_path = lib_dir.join(&relative_module).join("init.lua"); + for candidate in [&module_path, &init_path] { + if candidate.is_file() { + let source = fs::read_to_string(candidate).map_err(mlua::Error::external)?; + let chunk = lua + .load(&source) + .set_name(candidate.to_string_lossy().as_ref()); + return chunk.into_function().map(Value::Function); + } + } + + lua.create_string(format!( + "\n\tno repository lib module 'lib.{module}' in {}", + lib_dir.display() + )) + .map(Value::String) + })?; + + let len = searchers.raw_len(); + for index in (2..=len).rev() { + let value: Value = searchers.raw_get(index)?; + searchers.raw_set(index + 1, value)?; + } + searchers.raw_set(2, searcher) +} + +fn module_to_relative_path(module: &str) -> Option { + let mut path = PathBuf::new(); + for part in module.split('.') { + if part.is_empty() + || part == ".." + || part.contains('/') + || part.contains('\\') + || part.contains(std::path::MAIN_SEPARATOR) + { + return None; + } + path.push(part); + } + if path.as_os_str().is_empty() { + None + } else { + Some(path) + } } fn install_helpers(lua: &Lua) -> mlua::Result<()> { @@ -499,4 +560,28 @@ return android.local_app { assert_eq!(package.name, "F-Droid"); assert_eq!(package.installed.len(), 1); } + + #[test] + fn lib_prefixed_searcher_does_not_expose_repository_templates() { + let (_temp, layout, package_path) = fixture_repo(); + fs::write( + layout.templates_dir.join("android.lua"), + r#"return { leaked = true }"#, + ) + .unwrap(); + fs::write( + &package_path, + r#" +local ok = pcall(require, "templates.android") +return { + id = "android/org.fdroid.fdroid", + name = ok and "leaked" or "F-Droid", +} +"#, + ) + .unwrap(); + + let package = evaluate_package_file(&layout, &package_path).unwrap(); + assert_eq!(package.name, "F-Droid"); + } } From f746c96161de03548e2ebaccf57e7800a98becc7 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 11:01:13 +0800 Subject: [PATCH 06/71] fix(lua): avoid default cwd module leakage Do not retain Lua's default package.path for package evaluation. Keep repository lib loading explicit and add coverage for root/template leakage when cwd is the repository root. --- crates/getter-core/src/lua.rs | 41 +++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index b8e905b..c6b8f96 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -84,14 +84,12 @@ pub fn evaluate_package_source( fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Result<()> { let package: Table = lua.globals().get("package")?; - let current_path: String = package.get("path")?; let lib_pattern = repository.lib_dir.join("?.lua"); let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); let new_path = format!( - "{};{};{}", + "{};{}", lib_pattern.to_string_lossy(), - nested_lib_pattern.to_string_lossy(), - current_path + nested_lib_pattern.to_string_lossy() ); package.set("path", new_path)?; install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone()) @@ -584,4 +582,39 @@ return { let package = evaluate_package_file(&layout, &package_path).unwrap(); assert_eq!(package.name, "F-Droid"); } + + #[test] + fn repository_root_is_not_exposed_even_when_cwd_is_repository_root() { + let (_temp, layout, package_path) = fixture_repo(); + fs::write( + layout.root.join("rootleak.lua"), + r#"return { leaked = true }"#, + ) + .unwrap(); + fs::write( + layout.templates_dir.join("android.lua"), + r#"return { leaked = true }"#, + ) + .unwrap(); + fs::write( + &package_path, + r#" +local root_ok = pcall(require, "rootleak") +local template_ok = pcall(require, "templates.android") +return { + id = "android/org.fdroid.fdroid", + name = (root_ok or template_ok) and "leaked" or "F-Droid", +} +"#, + ) + .unwrap(); + + let original_cwd = std::env::current_dir().unwrap(); + std::env::set_current_dir(&layout.root).unwrap(); + let result = evaluate_package_file(&layout, &package_path); + std::env::set_current_dir(original_cwd).unwrap(); + + let package = result.unwrap(); + assert_eq!(package.name, "F-Droid"); + } } From 3b7613d709b405cb7229f2fbbf546c2d29ee96e6 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 11:26:45 +0800 Subject: [PATCH 07/71] fix(android): keep JNI facade free of Lua domain deps Make getter domain crates an optional feature so the Android api_proxy facade can build without vendored Lua on armeabi-v7a. --- Cargo.toml | 7 ++++--- src/lib.rs | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 500b0f1..e45af61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,8 @@ version = "0.1.0" edition = "2021" [features] -default = ["cli"] +default = ["cli", "domain"] +domain = ["dep:getter-core", "dep:getter-storage"] cli = ["dep:getter-cli"] rustls-platform-verifier = ["dep:rustls-platform-verifier"] rustls-platform-verifier-android = ["rustls-platform-verifier", "rustls-platform-verifier/jni"] @@ -29,8 +30,8 @@ webpki-roots = [] native-tokio = [] [dependencies] -getter-core = { path = "crates/getter-core" } -getter-storage = { path = "crates/getter-storage" } +getter-core = { path = "crates/getter-core", optional = true } +getter-storage = { path = "crates/getter-storage", optional = true } getter-cli = { path = "crates/getter-cli", optional = true } thiserror = "1" tokio = { version = "1", features = ["net"] } diff --git a/src/lib.rs b/src/lib.rs index 4ffc0b5..607df46 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,9 @@ //! behavior lives in split crates such as `getter-core` and `getter-storage`; //! Android/Flutter hosts embed this crate through a stable facade. +#[cfg(feature = "domain")] pub use getter_core as core; +#[cfg(feature = "domain")] pub use getter_storage as storage; #[cfg(feature = "rustls-platform-verifier")] From 7357980bc5972288db1817ede5610b5974b1546e Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 16:41:26 +0800 Subject: [PATCH 08/71] feat(cli): expose legacy migration reports Add a legacy report-list command so app and test adapters can consume sanitized migration reports through the getter JSON envelope instead of reading the data directory layout directly. --- README.md | 2 +- crates/getter-cli/src/lib.rs | 56 +++++++++++++++++++ crates/getter-cli/tests/bdd_cli.rs | 21 +++++++ .../legacy_import_room_bundle_failure.feature | 2 + 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 34543a0..6fc932d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This repository is intentionally usable outside the UpgradeAll UI. The UpgradeAl - Package IDs are readable, for example `android/org.fdroid.fdroid`. - Lua package repositories use `repo.toml` plus `packages/`, `lib/`, and `templates/` directories. - SQLite storage uses a main DB and a separate cache DB. -- `getter-cli` exposes JSON command contracts for init, app list, repository registration/evaluation, package evaluation, storage validation, and legacy bridge-bundle import. +- `getter-cli` exposes JSON command contracts for init, app list, repository registration/evaluation, package evaluation, storage validation, legacy bridge-bundle import, and sanitized legacy report listing. ## Verify diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 8e0155e..48d240d 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -51,6 +51,7 @@ pub enum CliCommand { LegacyImportRoomBundle { bundle: PathBuf, }, + LegacyReportList, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -262,6 +263,9 @@ where bundle: PathBuf::from(bundle), } } + [domain, command] if domain == "legacy" && command == "report-list" => { + CliCommand::LegacyReportList + } _ => { return Err(CliError::Usage(format!( "unsupported command: {}", @@ -404,6 +408,10 @@ fn execute(invocation: CliInvocation) -> Result { "apps": tracked_packages_json(records), })) } + CliCommand::LegacyReportList => { + open_initialized_storage(&invocation.data_dir)?; + Ok(json!({ "reports": list_migration_reports(&invocation.data_dir)? })) + } } } @@ -647,6 +655,53 @@ fn report_file_name(code: &str) -> String { format!("{}.json", code.replace('.', "-")) } +fn list_migration_reports(data_dir: &Path) -> Result, CliError> { + let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); + if !reports_dir.exists() { + return Ok(Vec::new()); + } + + let mut report_paths = fs::read_dir(&reports_dir) + .map_err(|source| CliError::Storage(format!("failed to read migration reports: {source}")))? + .map(|entry| entry.map(|entry| entry.path())) + .collect::, _>>() + .map_err(|source| { + CliError::Storage(format!("failed to read migration report entry: {source}")) + })?; + report_paths.retain(|path| { + path.extension() + .is_some_and(|extension| extension == "json") + }); + report_paths.sort(); + + report_paths + .into_iter() + .map(|path| { + let bytes = fs::read(&path).map_err(|source| { + CliError::Storage(format!( + "failed to read migration report '{}': {source}", + path.display() + )) + })?; + let report: Value = serde_json::from_slice(&bytes).map_err(|source| { + CliError::Storage(format!( + "failed to parse migration report '{}': {source}", + path.display() + )) + })?; + Ok(json!({ + "ok": report.get("ok").and_then(Value::as_bool).unwrap_or(false), + "code": report.get("code").and_then(Value::as_str).unwrap_or("migration.unknown"), + "message": report.get("message").and_then(Value::as_str).unwrap_or("Legacy migration report"), + "bundle_file_name": report.get("bundle_file_name").and_then(Value::as_str), + "imported_records": report.get("imported_records").and_then(Value::as_u64).unwrap_or(0), + "tracked_records": report.get("tracked_records").and_then(Value::as_u64).unwrap_or(0), + "report_path": path, + })) + }) + .collect() +} + #[derive(Debug, Serialize)] struct MigrationReport<'a> { ok: bool, @@ -746,6 +801,7 @@ impl CliCommand { Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", + Self::LegacyReportList => "legacy report-list", } } } diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 807aaed..28ed839 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -190,6 +190,13 @@ fn run_getter_legacy_import(world: &mut CliWorld) { world.json = None; } +#[when("I run getter legacy report-list for that directory")] +fn run_getter_legacy_report_list(world: &mut CliWorld) { + let output = run_getter(world, ["legacy".to_owned(), "report-list".to_owned()]); + world.output = Some(output); + world.json = None; +} + #[then("the command succeeds")] fn command_succeeds(world: &mut CliWorld) { let output = world.output.as_ref().expect("command output exists"); @@ -356,6 +363,20 @@ fn import_reports_one_tracked_app(world: &mut CliWorld) { ); } +#[then(expr = "the output lists migration report {string}")] +fn output_lists_migration_report(world: &mut CliWorld, code: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "legacy report-list"); + let reports = json["data"]["reports"].as_array().expect("reports array"); + assert!( + reports + .iter() + .any(|report| report["code"].as_str() == Some(code.as_str())), + "reports should contain {code}: {reports:?}" + ); +} + #[then(expr = "the app list contains imported package {string}")] fn app_list_contains_imported_package(world: &mut CliWorld, package_id: String) { let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); diff --git a/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature b/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature index 864417f..02b6e8b 100644 --- a/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature +++ b/crates/getter-cli/tests/features/cli/legacy_import_room_bundle_failure.feature @@ -16,3 +16,5 @@ Feature: Legacy import failure recovery And the output is valid JSON And the import reports one tracked app And the app list contains imported package "android/org.fdroid.fdroid" + When I run getter legacy report-list for that directory + Then the output lists migration report "migration.imported" From 6be047ac13d6dc4dd94dc29fbe6c4f622acd640f Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 16:58:33 +0800 Subject: [PATCH 09/71] fix(cli): keep migration report list sanitized Do not expose getter data-directory report paths in legacy report-list output; callers only need the sanitized report fields. --- crates/getter-cli/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 48d240d..b13bd02 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -696,7 +696,6 @@ fn list_migration_reports(data_dir: &Path) -> Result, CliError> { "bundle_file_name": report.get("bundle_file_name").and_then(Value::as_str), "imported_records": report.get("imported_records").and_then(Value::as_u64).unwrap_or(0), "tracked_records": report.get("tracked_records").and_then(Value::as_u64).unwrap_or(0), - "report_path": path, })) }) .collect() From 13816863b20744beadab7bf10153afa2156e3010 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 20:19:23 +0800 Subject: [PATCH 10/71] feat(cli): add offline repo validation Expose repo validate through the getter JSON envelope with structured getter-owned diagnostics for repository layout, Lua runtime, schema, and domain errors. Add BDD coverage and cache-key hashing for repository changes. --- crates/getter-cli/src/lib.rs | 18 +- crates/getter-cli/tests/bdd_cli.rs | 134 ++++++++ .../tests/features/cli/repo_validate.feature | 35 ++ crates/getter-core/src/diagnostics.rs | 309 ++++++++++++++++++ crates/getter-core/src/lib.rs | 1 + crates/getter-core/src/repository.rs | 86 ++++- 6 files changed, 581 insertions(+), 2 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/repo_validate.feature create mode 100644 crates/getter-core/src/diagnostics.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index b13bd02..22fb675 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -4,6 +4,7 @@ //! while durable state is initialized through `getter-storage` so the command //! surface exercises the same Rust-owned SQLite direction used by embedders. +use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; @@ -43,6 +44,9 @@ pub enum CliCommand { RepoEval { id: RepositoryId, }, + RepoValidate { + path: PathBuf, + }, PackageEval { package_id: PackageId, repo_id: Option, @@ -241,6 +245,11 @@ where [domain, command, id] if domain == "repo" && command == "eval" => CliCommand::RepoEval { id: parse_repository_id(id)?, }, + [domain, command, path] if domain == "repo" && command == "validate" => { + CliCommand::RepoValidate { + path: PathBuf::from(path), + } + } [domain, command, package_id] if domain == "package" && command == "eval" => { CliCommand::PackageEval { package_id: parse_package_id(package_id)?, @@ -331,6 +340,12 @@ fn execute(invocation: CliInvocation) -> Result { "packages": packages, })) } + CliCommand::RepoValidate { path } => { + open_initialized_storage(&invocation.data_dir)?; + serde_json::to_value(validate_repository_path(path)).map_err(|source| { + CliError::Repository(format!("failed to serialize validation report: {source}")) + }) + } CliCommand::PackageEval { package_id, repo_id, @@ -747,7 +762,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |package eval [--repo ]|storage validate|hub list|legacy import-room-bundle >\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|legacy import-room-bundle |legacy report-list>\n".to_owned() } #[derive(Debug, Deserialize)] @@ -797,6 +812,7 @@ impl CliCommand { Self::RepoList => "repo list", Self::RepoAdd { .. } => "repo add", Self::RepoEval { .. } => "repo eval", + Self::RepoValidate { .. } => "repo validate", Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 28ed839..4f084c7 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -80,6 +80,61 @@ fn fixture_lua_repository_named( create_fixture_lua_repository(world, repo_id, package_id, package_name); } +#[given(expr = "a fixture Lua repository {string} with invalid Lua package {string}")] +fn fixture_lua_repository_invalid_lua(world: &mut CliWorld, repo_id: String, package_id: String) { + create_custom_fixture_lua_repository( + world, + repo_id, + package_id.clone(), + format!("return package_def {{ id = \"{package_id}\", name = "), + ); +} + +#[given(expr = "a fixture Lua repository {string} with schema-invalid package {string}")] +fn fixture_lua_repository_invalid_schema( + world: &mut CliWorld, + repo_id: String, + package_id: String, +) { + create_custom_fixture_lua_repository( + world, + repo_id, + package_id.clone(), + format!("return {{ id = \"{package_id}\" }}"), + ); +} + +#[given(expr = "a fixture Lua repository {string} with mismatched package path {string}")] +fn fixture_lua_repository_mismatched_path( + world: &mut CliWorld, + repo_id: String, + package_id: String, +) { + create_custom_fixture_lua_repository( + world, + repo_id, + package_id, + "return { id = \"android/com.termux\", name = \"Termux\" }".to_owned(), + ); +} + +#[given(expr = "an incomplete Lua repository {string}")] +fn incomplete_lua_repository(world: &mut CliWorld, repo_id: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let repo_path = temp.path().join(format!("repo-{repo_id}")); + fs::create_dir_all(&repo_path).expect("create incomplete repo dir"); + fs::write( + repo_path.join("repo.toml"), + format!( + "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" + ), + ) + .expect("write repo.toml"); + world.fixture_repo_id = Some(repo_id); + world.fixture_repo_path = Some(repo_path); + world.fixture_package_id = None; +} + #[when("I run getter init for that directory")] fn run_getter_init(world: &mut CliWorld) { let output = run_getter(world, ["init".to_owned()]); @@ -144,6 +199,24 @@ fn run_getter_repo_eval(world: &mut CliWorld) { world.json = None; } +#[when("I run getter repo validate for that repository")] +fn run_getter_repo_validate(world: &mut CliWorld) { + let repo_path = world + .fixture_repo_path + .as_ref() + .expect("fixture repo path exists"); + let output = run_getter( + world, + [ + "repo".to_owned(), + "validate".to_owned(), + repo_path.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter package eval for that fixture package")] fn run_getter_package_eval(world: &mut CliWorld) { let repo_id = world @@ -282,6 +355,35 @@ fn output_contains_added_repository(world: &mut CliWorld) { assert_eq!(json["data"]["repository"]["id"], repo_id); } +#[then("the output reports a valid repository without network")] +fn output_reports_valid_repository_without_network(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "repo validate"); + assert_eq!(json["data"]["valid"], true); + assert_eq!(json["data"]["network_required"], false); + assert_eq!(json["data"]["diagnostics"], Value::Array(Vec::new())); + assert_eq!(json["data"]["package_count"], 1); +} + +#[then(expr = "the output reports repository diagnostic {string}")] +fn output_reports_repository_diagnostic(world: &mut CliWorld, code: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "repo validate"); + assert_eq!(json["data"]["valid"], false); + assert_eq!(json["data"]["network_required"], false); + let diagnostics = json["data"]["diagnostics"] + .as_array() + .expect("diagnostics array"); + assert!( + diagnostics + .iter() + .any(|diagnostic| diagnostic["code"].as_str() == Some(code.as_str())), + "diagnostics should contain {code}: {diagnostics:?}" + ); +} + #[then("the output contains the evaluated fixture package")] fn output_contains_evaluated_fixture_package(world: &mut CliWorld) { let package_id = world @@ -442,6 +544,38 @@ return package_def {{ world.fixture_package_id = Some(package_id); } +fn create_custom_fixture_lua_repository( + world: &mut CliWorld, + repo_id: String, + package_id: String, + package_source: String, +) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let repo_path = temp.path().join(format!("repo-{repo_id}")); + let package_name_path = package_id + .strip_prefix("android/") + .expect("fixture package id should be android package id"); + fs::create_dir_all(repo_path.join("packages/android")).expect("create packages dir"); + fs::create_dir(repo_path.join("lib")).expect("create lib dir"); + fs::create_dir(repo_path.join("templates")).expect("create templates dir"); + fs::write( + repo_path.join("repo.toml"), + format!( + "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" + ), + ) + .expect("write repo.toml"); + fs::write( + repo_path.join(format!("packages/android/{package_name_path}.lua")), + package_source, + ) + .expect("write package Lua"); + + world.fixture_repo_id = Some(repo_id); + world.fixture_repo_path = Some(repo_path); + world.fixture_package_id = Some(package_id); +} + fn run_getter_repo_add_with_priority(world: &mut CliWorld, repo_id: &str, priority: i32) { let temp = world.temp.as_ref().expect("tempdir exists"); let repo_path = temp.path().join(format!("repo-{repo_id}")); diff --git a/crates/getter-cli/tests/features/cli/repo_validate.feature b/crates/getter-cli/tests/features/cli/repo_validate.feature new file mode 100644 index 0000000..592d4bf --- /dev/null +++ b/crates/getter-cli/tests/features/cli/repo_validate.feature @@ -0,0 +1,35 @@ +Feature: Getter CLI repository validation + Scenario: User validates a fixture Lua repository offline + Given an initialized getter data directory + And a fixture Lua repository "official" with package "android/org.fdroid.fdroid" + When I run getter repo validate for that repository + Then the command succeeds + And the output reports a valid repository without network + + Scenario: User receives diagnostics for invalid Lua + Given an initialized getter data directory + And a fixture Lua repository "broken" with invalid Lua package "android/org.fdroid.fdroid" + When I run getter repo validate for that repository + Then the command succeeds + And the output reports repository diagnostic "package.lua_runtime" + + Scenario: User receives diagnostics for invalid package schema + Given an initialized getter data directory + And a fixture Lua repository "broken" with schema-invalid package "android/org.fdroid.fdroid" + When I run getter repo validate for that repository + Then the command succeeds + And the output reports repository diagnostic "package.schema" + + Scenario: User receives diagnostics for package path mismatch + Given an initialized getter data directory + And a fixture Lua repository "broken" with mismatched package path "android/org.fdroid.fdroid" + When I run getter repo validate for that repository + Then the command succeeds + And the output reports repository diagnostic "package.domain" + + Scenario: User receives diagnostics for an incomplete repository layout + Given an initialized getter data directory + And an incomplete Lua repository "broken" + When I run getter repo validate for that repository + Then the command succeeds + And the output reports repository diagnostic "repository.missing_directory" diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs new file mode 100644 index 0000000..745446f --- /dev/null +++ b/crates/getter-core/src/diagnostics.rs @@ -0,0 +1,309 @@ +//! Structured repository/package diagnostics for offline validation. +//! +//! Diagnostics are getter-owned DTOs used by CLI and future app bridges. They +//! describe what getter observed; Flutter should only render them. + +use crate::lua::{evaluate_package_file, LuaPackageError}; +use crate::repository::{RepositoryLayout, RepositoryLoadError}; +use crate::PackageId; +use serde::{Deserialize, Serialize}; +use std::path::{Path, PathBuf}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum DiagnosticSeverity { + Error, + Warning, + Info, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SourceLocation { + pub path: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub field: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PackageValidationDiagnostic { + pub severity: DiagnosticSeverity, + pub code: String, + pub message: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub package_id: Option, + pub location: SourceLocation, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct RepositoryValidationReport { + pub valid: bool, + pub diagnostics: Vec, + pub package_count: usize, + pub network_required: bool, +} + +impl RepositoryValidationReport { + pub fn new(package_count: usize, diagnostics: Vec) -> Self { + let valid = !diagnostics + .iter() + .any(|diagnostic| diagnostic.severity == DiagnosticSeverity::Error); + Self { + valid, + diagnostics, + package_count, + network_required: false, + } + } +} + +/// Validate a repository path offline. +/// +/// This intentionally loads and evaluates local Lua package files only. It must +/// not perform provider/network checks. +pub fn validate_repository_path(path: impl AsRef) -> RepositoryValidationReport { + let root = path.as_ref(); + let layout = match RepositoryLayout::load(root) { + Ok(layout) => layout, + Err(error) => { + return RepositoryValidationReport::new(0, vec![repository_load_diagnostic(error)]) + } + }; + + let mut diagnostics = Vec::new(); + let mut package_count = 0usize; + for package_file in &layout.packages { + match evaluate_package_file(&layout, &package_file.path) { + Ok(_) => package_count += 1, + Err(error) => diagnostics.push(lua_diagnostic(error, Some(package_file.id.clone()))), + } + } + + RepositoryValidationReport::new(package_count, diagnostics) +} + +fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDiagnostic { + let (code, path, message) = match error { + RepositoryLoadError::ReadRepoToml { path, source } => ( + "repository.read_repo_toml", + path, + format!("failed to read repo.toml: {source}"), + ), + RepositoryLoadError::ParseRepoToml { path, source } => ( + "repository.parse_repo_toml", + path, + format!("failed to parse repo.toml: {source}"), + ), + RepositoryLoadError::RepositoryId(source) => ( + "repository.invalid_id", + PathBuf::from("repo.toml"), + source.to_string(), + ), + RepositoryLoadError::UnsupportedApiVersion(version) => ( + "repository.unsupported_api_version", + PathBuf::from("repo.toml"), + format!("unsupported repository api_version '{version}'"), + ), + RepositoryLoadError::MissingDirectory { path, directory } => ( + "repository.missing_directory", + path, + format!("missing required directory '{directory}'"), + ), + RepositoryLoadError::ReadPackagesDir { path, source } => ( + "repository.read_packages_dir", + path, + format!("failed to read packages directory: {source}"), + ), + RepositoryLoadError::InvalidPackagePath { path, reason } => { + ("repository.invalid_package_path", path, reason) + } + RepositoryLoadError::PackageId { path, source } => ( + "repository.invalid_package_id", + path, + format!("invalid package id derived from path: {source}"), + ), + RepositoryLoadError::HashPackageFile { path, source } => ( + "repository.hash_package_file", + path, + format!("failed to hash package file: {source}"), + ), + }; + diagnostic(code, message, path, None, None) +} + +fn lua_diagnostic( + error: LuaPackageError, + fallback_package_id: Option, +) -> PackageValidationDiagnostic { + match error { + LuaPackageError::ReadFile { path, source } => diagnostic( + "package.read_file", + format!("failed to read Lua package file: {source}"), + path, + None, + fallback_package_id, + ), + LuaPackageError::Runtime { path, source } => diagnostic( + "package.lua_runtime", + format!("Lua runtime error: {source}"), + path, + None, + fallback_package_id, + ), + LuaPackageError::NotATable { path } => diagnostic( + "package.not_a_table", + "Lua package did not return a table".to_owned(), + path, + None, + fallback_package_id, + ), + LuaPackageError::UnsupportedValue { + path, + location, + value_type, + } => diagnostic( + "package.unsupported_value", + format!("unsupported Lua value at {location}: {value_type}"), + path, + Some(location), + fallback_package_id, + ), + LuaPackageError::Schema { path, message } => { + diagnostic("package.schema", message, path, None, fallback_package_id) + } + LuaPackageError::Domain { path, message } => { + diagnostic("package.domain", message, path, None, fallback_package_id) + } + } +} + +fn diagnostic( + code: impl Into, + message: impl Into, + path: PathBuf, + field: Option, + package_id: Option, +) -> PackageValidationDiagnostic { + PackageValidationDiagnostic { + severity: DiagnosticSeverity::Error, + code: code.into(), + message: message.into(), + package_id, + location: SourceLocation { + path: path.to_string_lossy().to_string(), + field, + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + + fn fixture_repo() -> tempfile::TempDir { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::write( + root.join("repo.toml"), + r#"id = "official" +name = "Official" +priority = 0 +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + fs::create_dir_all(root.join("packages/android")).unwrap(); + fs::create_dir(root.join("lib")).unwrap(); + fs::create_dir(root.join("templates")).unwrap(); + temp + } + + #[test] + fn valid_repository_has_no_diagnostics() { + let temp = fixture_repo(); + fs::write( + temp.path().join("packages/android/org.fdroid.fdroid.lua"), + r#"return package_def { id = "android/org.fdroid.fdroid", name = "F-Droid" }"#, + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + assert!(report.valid, "{report:?}"); + assert_eq!(report.package_count, 1); + assert!(report.diagnostics.is_empty()); + assert!(!report.network_required); + } + + #[test] + fn missing_directory_is_stable_repository_diagnostic() { + let temp = tempfile::tempdir().unwrap(); + fs::write( + temp.path().join("repo.toml"), + r#"id = "official" +name = "Official" +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + assert!(!report.valid); + assert_eq!(report.diagnostics[0].code, "repository.missing_directory"); + } + + #[test] + fn lua_schema_error_is_stable_package_diagnostic() { + let temp = fixture_repo(); + fs::write( + temp.path().join("packages/android/org.fdroid.fdroid.lua"), + r#"return { id = "android/org.fdroid.fdroid" }"#, + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + assert!(!report.valid); + assert_eq!(report.diagnostics[0].code, "package.schema"); + assert_eq!( + report.diagnostics[0] + .package_id + .as_ref() + .unwrap() + .to_string(), + "android/org.fdroid.fdroid" + ); + } + + #[test] + fn package_id_path_mismatch_is_domain_diagnostic() { + let temp = fixture_repo(); + fs::write( + temp.path().join("packages/android/org.fdroid.fdroid.lua"), + r#"return { id = "android/com.termux", name = "Termux" }"#, + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + assert!(!report.valid); + assert_eq!(report.diagnostics[0].code, "package.domain"); + } + + #[test] + fn unsupported_api_version_is_stable_repository_diagnostic() { + let temp = tempfile::tempdir().unwrap(); + fs::write( + temp.path().join("repo.toml"), + r#"id = "official" +name = "Official" +api_version = "getter.repo.v2" +"#, + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + assert!(!report.valid); + assert_eq!( + report.diagnostics[0].code, + "repository.unsupported_api_version" + ); + } +} diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index da959a3..32c110b 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -9,6 +9,7 @@ use std::cmp::Ordering; use std::fmt; use std::str::FromStr; +pub mod diagnostics; pub mod lua; pub mod repository; pub mod update; diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index fe9fa21..c383acc 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -1,7 +1,7 @@ //! Repository layout loading for Lua package repositories. use crate::{PackageId, PackageIdError, RepositoryId, RepositoryIdError, RepositoryPriority}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::fs; use std::path::{Path, PathBuf}; @@ -31,6 +31,14 @@ pub struct PackageFile { pub path: PathBuf, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct RepositoryPackageCacheKey { + pub repository_id: RepositoryId, + pub package_id: PackageId, + pub api_version: String, + pub package_file_hash: String, +} + #[derive(Debug, thiserror::Error)] pub enum RepositoryLoadError { #[error("failed to read repo.toml at {path}: {source}")] @@ -68,6 +76,12 @@ pub enum RepositoryLoadError { #[source] source: PackageIdError, }, + #[error("failed to hash package file {path}: {source}")] + HashPackageFile { + path: PathBuf, + #[source] + source: std::io::Error, + }, } impl RepositoryLayout { @@ -224,6 +238,36 @@ pub fn package_id_from_path( }) } +pub fn package_cache_key( + repository: &RepositoryLayout, + package: &PackageFile, +) -> Result { + Ok(RepositoryPackageCacheKey { + repository_id: repository.metadata.id.clone(), + package_id: package.id.clone(), + api_version: repository.metadata.api_version.clone(), + package_file_hash: package_file_content_hash(&package.path)?, + }) +} + +pub fn package_file_content_hash(path: impl AsRef) -> Result { + let path = path.as_ref(); + let bytes = fs::read(path).map_err(|source| RepositoryLoadError::HashPackageFile { + path: path.to_path_buf(), + source, + })?; + Ok(format!("{:016x}", fnv1a64(&bytes))) +} + +fn fnv1a64(bytes: &[u8]) -> u64 { + let mut hash = 0xcbf29ce484222325u64; + for byte in bytes { + hash ^= u64::from(*byte); + hash = hash.wrapping_mul(0x100000001b3); + } + hash +} + pub fn highest_priority(items: &[T], priority: F) -> Option<&T> where F: Fn(&T) -> RepositoryPriority, @@ -275,6 +319,46 @@ api_version = "getter.repo.v1" ); } + #[test] + fn package_cache_key_changes_when_package_file_content_changes() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::write( + root.join("repo.toml"), + r#"id = "official" +name = "UpgradeAll Official" +priority = 0 +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + fs::create_dir(root.join("packages")).unwrap(); + fs::create_dir(root.join("packages/android")).unwrap(); + fs::create_dir(root.join("lib")).unwrap(); + fs::create_dir(root.join("templates")).unwrap(); + let package_path = root.join("packages/android/org.fdroid.fdroid.lua"); + fs::write( + &package_path, + r#"return { id = "android/org.fdroid.fdroid", name = "F-Droid" }"#, + ) + .unwrap(); + let layout = RepositoryLayout::load(root).unwrap(); + let first = package_cache_key(&layout, &layout.packages[0]).unwrap(); + + fs::write( + &package_path, + r#"return { id = "android/org.fdroid.fdroid", name = "F-Droid Nightly" }"#, + ) + .unwrap(); + let layout = RepositoryLayout::load(root).unwrap(); + let second = package_cache_key(&layout, &layout.packages[0]).unwrap(); + + assert_eq!(first.repository_id.as_str(), "official"); + assert_eq!(first.package_id.to_string(), "android/org.fdroid.fdroid"); + assert_eq!(first.api_version, REPO_API_VERSION_V1); + assert_ne!(first.package_file_hash, second.package_file_hash); + } + #[test] fn highest_priority_selects_larger_number() { let priorities = [ From f1035d3ae88198dfd0de872b6f9af010a0b0cd3f Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 20:30:12 +0800 Subject: [PATCH 11/71] fix(core): include schema field diagnostics Populate the diagnostic location field for schema errors when getter can infer the failing field from validation messages. --- crates/getter-core/src/diagnostics.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index 745446f..7c58881 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -168,7 +168,8 @@ fn lua_diagnostic( fallback_package_id, ), LuaPackageError::Schema { path, message } => { - diagnostic("package.schema", message, path, None, fallback_package_id) + let field = schema_field_from_message(&message); + diagnostic("package.schema", message, path, field, fallback_package_id) } LuaPackageError::Domain { path, message } => { diagnostic("package.domain", message, path, None, fallback_package_id) @@ -176,6 +177,13 @@ fn lua_diagnostic( } } +fn schema_field_from_message(message: &str) -> Option { + let marker = "field '"; + let start = message.find(marker)? + marker.len(); + let end = message[start..].find('\'')?; + Some(message[start..start + end].to_owned()) +} + fn diagnostic( code: impl Into, message: impl Into, @@ -263,6 +271,10 @@ api_version = "getter.repo.v1" let report = validate_repository_path(temp.path()); assert!(!report.valid); assert_eq!(report.diagnostics[0].code, "package.schema"); + assert_eq!( + report.diagnostics[0].location.field.as_deref(), + Some("name") + ); assert_eq!( report.diagnostics[0] .package_id From 255a9c7c25174355be24053c0e3876de7cf087db Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 20:41:12 +0800 Subject: [PATCH 12/71] fix(core): constrain Lua validation environment Remove process/filesystem-sensitive Lua globals during package evaluation and assert the repo validator exposes full diagnostic shape for schema errors. --- crates/getter-cli/tests/bdd_cli.rs | 17 +++++--- crates/getter-core/src/diagnostics.rs | 6 ++- crates/getter-core/src/lua.rs | 56 ++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 4f084c7..ddc42f2 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -376,12 +376,17 @@ fn output_reports_repository_diagnostic(world: &mut CliWorld, code: String) { let diagnostics = json["data"]["diagnostics"] .as_array() .expect("diagnostics array"); - assert!( - diagnostics - .iter() - .any(|diagnostic| diagnostic["code"].as_str() == Some(code.as_str())), - "diagnostics should contain {code}: {diagnostics:?}" - ); + let diagnostic = diagnostics + .iter() + .find(|diagnostic| diagnostic["code"].as_str() == Some(code.as_str())) + .unwrap_or_else(|| panic!("diagnostics should contain {code}: {diagnostics:?}")); + assert_eq!(diagnostic["severity"], "error"); + assert!(diagnostic["message"].as_str().is_some()); + assert!(diagnostic["location"]["path"].as_str().is_some()); + if code == "package.schema" { + assert_eq!(diagnostic["package_id"], "android/org.fdroid.fdroid"); + assert_eq!(diagnostic["location"]["field"], "name"); + } } #[then("the output contains the evaluated fixture package")] diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index 7c58881..62ed870 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -4,7 +4,7 @@ //! describe what getter observed; Flutter should only render them. use crate::lua::{evaluate_package_file, LuaPackageError}; -use crate::repository::{RepositoryLayout, RepositoryLoadError}; +use crate::repository::{package_cache_key, RepositoryLayout, RepositoryLoadError}; use crate::PackageId; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; @@ -72,6 +72,10 @@ pub fn validate_repository_path(path: impl AsRef) -> RepositoryValidationR let mut diagnostics = Vec::new(); let mut package_count = 0usize; for package_file in &layout.packages { + if let Err(error) = package_cache_key(&layout, package_file) { + diagnostics.push(repository_load_diagnostic(error)); + continue; + } match evaluate_package_file(&layout, &package_file.path) { Ok(_) => package_count += 1, Err(error) => diagnostics.push(lua_diagnostic(error, Some(package_file.id.clone()))), diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index c6b8f96..a31107d 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -61,6 +61,10 @@ pub fn evaluate_package_source( path: path.clone(), source, })?; + remove_unsafe_globals(&lua).map_err(|source| LuaPackageError::Runtime { + path: path.clone(), + source, + })?; install_helpers(&lua).map_err(|source| LuaPackageError::Runtime { path: path.clone(), source, @@ -92,7 +96,32 @@ fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Res nested_lib_pattern.to_string_lossy() ); package.set("path", new_path)?; - install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone()) + package.set("cpath", "")?; + package.set("loadlib", Value::Nil)?; + install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone())?; + disable_native_module_searchers(&package) +} + +fn disable_native_module_searchers(package: &Table) -> mlua::Result<()> { + let searchers: Table = package.get("searchers")?; + let len = searchers.raw_len(); + for index in 4..=len { + searchers.raw_set(index, Value::Nil)?; + } + Ok(()) +} + +fn remove_unsafe_globals(lua: &Lua) -> mlua::Result<()> { + let globals = lua.globals(); + let package: Table = globals.get("package")?; + let loaded: Table = package.get("loaded")?; + for name in ["os", "io", "debug"] { + loaded.set(name, Value::Nil)?; + } + for name in ["os", "io", "debug", "dofile", "loadfile", "package"] { + globals.set(name, Value::Nil)?; + } + Ok(()) } fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> mlua::Result<()> { @@ -583,6 +612,31 @@ return { assert_eq!(package.name, "F-Droid"); } + #[test] + fn lua_environment_does_not_expose_process_or_file_system_globals() { + let temp = tempfile::tempdir().unwrap(); + let side_effect = temp.path().join("side-effect"); + let (_repo_temp, layout, package_path) = fixture_repo(); + fs::write( + &package_path, + format!( + r#" +return {{ + id = "android/org.fdroid.fdroid", + name = (os == nil and io == nil and debug == nil and dofile == nil and loadfile == nil and package == nil) and "F-Droid" or "leaked", + side_effect = rawget(_G, "os") and os.execute("touch {}"), +}} +"#, + side_effect.display() + ), + ) + .unwrap(); + + let package = evaluate_package_file(&layout, &package_path).unwrap(); + assert_eq!(package.name, "F-Droid"); + assert!(!side_effect.exists()); + } + #[test] fn repository_root_is_not_exposed_even_when_cwd_is_repository_root() { let (_temp, layout, package_path) = fixture_repo(); From 17b0e281914d3c7d646aa0605bee283d31ffd60b Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Mon, 22 Jun 2026 22:22:48 +0800 Subject: [PATCH 13/71] feat(cli): import legacy Room databases --- Cargo.lock | 2 + crates/getter-cli/Cargo.toml | 1 + crates/getter-cli/src/lib.rs | 277 +++++++++- crates/getter-cli/tests/bdd_cli.rs | 276 +++++++++- .../cli/legacy_import_room_db.feature | 70 +++ crates/getter-storage/Cargo.toml | 1 + crates/getter-storage/src/legacy_room.rs | 486 +++++++++++++++++- crates/getter-storage/src/lib.rs | 115 ++++- 8 files changed, 1169 insertions(+), 59 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/legacy_import_room_db.feature diff --git a/Cargo.lock b/Cargo.lock index 5d73f4c..6119744 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -521,6 +521,7 @@ dependencies = [ "cucumber", "getter-core", "getter-storage", + "rusqlite", "serde", "serde_json", "tempfile", @@ -581,6 +582,7 @@ version = "0.1.0" dependencies = [ "getter-core", "rusqlite", + "serde_json", "tempfile", "thiserror 1.0.69", ] diff --git a/crates/getter-cli/Cargo.toml b/crates/getter-cli/Cargo.toml index ca03088..ef7d105 100644 --- a/crates/getter-cli/Cargo.toml +++ b/crates/getter-cli/Cargo.toml @@ -12,6 +12,7 @@ thiserror = "1" [dev-dependencies] cucumber = "0.23" +rusqlite = { version = "0.32", features = ["bundled"] } serde_json = "1" tempfile = "3" tokio = { version = "1", features = ["macros", "rt-multi-thread"] } diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 22fb675..a15af83 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -9,11 +9,13 @@ use getter_core::lua::evaluate_package_file; use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; use getter_storage::legacy_room::{ - map_legacy_app, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, + map_legacy_app, read_legacy_room_database, LegacyAppKind, LegacyAppRecord, + LegacyExtraAppRecord, LegacyPackageResolution, LegacyRoomDbImport, LegacyRoomImportWarning, + LegacyRoomReadError, }; use getter_storage::{ - CacheDb, MainDb, StorageError, StoredPackageResolution, StoredRepository, StoredTrackedPackage, - TrackedPackageUpsert, + CacheDb, MainDb, MigrationRecordUpsert, StorageError, StoredPackageResolution, + StoredRepository, StoredTrackedPackage, TrackedPackageUpsert, }; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; @@ -23,6 +25,7 @@ use std::path::{Path, PathBuf}; const MAIN_DB_FILE: &str = "main.db"; const CACHE_DB_FILE: &str = "cache.db"; const MIGRATION_REPORTS_DIR: &str = "migration-reports"; +const LEGACY_ROOM_MIGRATION_ID: &str = "legacy-room-v17"; #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliInvocation { @@ -55,6 +58,9 @@ pub enum CliCommand { LegacyImportRoomBundle { bundle: PathBuf, }, + LegacyImportRoomDb { + db: PathBuf, + }, LegacyReportList, } @@ -87,6 +93,10 @@ pub enum CliError { InvalidLegacyBundle { report_path: PathBuf }, #[error("Legacy Room bundle import is not implemented yet")] UnsupportedLegacyBundle { report_path: PathBuf }, + #[error("Legacy Room database is invalid")] + InvalidLegacyDb { report_path: PathBuf }, + #[error("Legacy Room database version is not supported")] + UnsupportedLegacyDb { report_path: PathBuf }, } impl CliError { @@ -95,9 +105,10 @@ impl CliError { Self::Usage(_) => ExitCode::Usage, Self::Storage(_) => ExitCode::Storage, Self::Repository(_) | Self::PackageEval(_) => ExitCode::GenericFailure, - Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } => { - ExitCode::Migration - } + Self::InvalidLegacyBundle { .. } + | Self::UnsupportedLegacyBundle { .. } + | Self::InvalidLegacyDb { .. } + | Self::UnsupportedLegacyDb { .. } => ExitCode::Migration, } } @@ -109,6 +120,8 @@ impl CliError { Self::PackageEval(_) => "package.eval_error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", + Self::InvalidLegacyDb { .. } => "migration.invalid_db", + Self::UnsupportedLegacyDb { .. } => "migration.unsupported_db", } } @@ -122,6 +135,8 @@ impl CliError { Self::UnsupportedLegacyBundle { .. } => { "Legacy Room bundle import is not implemented yet" } + Self::InvalidLegacyDb { .. } => "Legacy Room database is invalid", + Self::UnsupportedLegacyDb { .. } => "Legacy Room database version is not supported", } } @@ -131,14 +146,19 @@ impl CliError { | Self::Storage(detail) | Self::Repository(detail) | Self::PackageEval(detail) => Some(detail.as_str()), - Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } => None, + Self::InvalidLegacyBundle { .. } + | Self::UnsupportedLegacyBundle { .. } + | Self::InvalidLegacyDb { .. } + | Self::UnsupportedLegacyDb { .. } => None, } } fn report_path(&self) -> Option<&Path> { match self { Self::InvalidLegacyBundle { report_path } - | Self::UnsupportedLegacyBundle { report_path } => Some(report_path.as_path()), + | Self::UnsupportedLegacyBundle { report_path } + | Self::InvalidLegacyDb { report_path } + | Self::UnsupportedLegacyDb { report_path } => Some(report_path.as_path()), Self::Usage(_) | Self::Storage(_) | Self::Repository(_) | Self::PackageEval(_) => None, } } @@ -272,6 +292,11 @@ where bundle: PathBuf::from(bundle), } } + [domain, command, db] if domain == "legacy" && command == "import-room-db" => { + CliCommand::LegacyImportRoomDb { + db: PathBuf::from(db), + } + } [domain, command] if domain == "legacy" && command == "report-list" => { CliCommand::LegacyReportList } @@ -367,6 +392,14 @@ fn execute(invocation: CliInvocation) -> Result { } CliCommand::LegacyImportRoomBundle { bundle } => { let db = open_main_db(&invocation.data_dir)?; + if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { + let records = db.tracked_packages()?; + return Ok(json!({ + "already_imported": true, + "imported_records": 0, + "apps": tracked_packages_json(records), + })); + } let bytes = fs::read(&bundle).map_err(|source| { match create_migration_report( &invocation.data_dir, @@ -375,6 +408,7 @@ fn execute(invocation: CliInvocation) -> Result { &format!("failed to read bundle: {source}"), 0, 0, + &[], ) { Ok(report_path) => CliError::InvalidLegacyBundle { report_path }, Err(error) => error, @@ -388,6 +422,7 @@ fn execute(invocation: CliInvocation) -> Result { &format!("failed to parse JSON bundle: {source}"), 0, 0, + &[], ) { Ok(report_path) => CliError::InvalidLegacyBundle { report_path }, Err(error) => error, @@ -404,6 +439,7 @@ fn execute(invocation: CliInvocation) -> Result { ), 0, 0, + &[], )?; return Err(CliError::UnsupportedLegacyBundle { report_path }); } @@ -415,6 +451,7 @@ fn execute(invocation: CliInvocation) -> Result { "Legacy Room bundle imported", parsed.apps.len() as u64, parsed.apps.len() as u64, + &[], )?; let records = db.tracked_packages()?; Ok(json!({ @@ -423,6 +460,62 @@ fn execute(invocation: CliInvocation) -> Result { "apps": tracked_packages_json(records), })) } + CliCommand::LegacyImportRoomDb { db: legacy_db } => { + let db = open_main_db(&invocation.data_dir)?; + if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { + let records = db.tracked_packages()?; + return Ok(json!({ + "already_imported": true, + "imported_records": 0, + "source_counts": { + "app_rows": 0, + "extra_app_rows": 0, + "hub_rows": 0, + "extra_hub_rows": 0, + }, + "warnings": [], + "apps": tracked_packages_json(records), + })); + } + let import = read_legacy_room_database(&legacy_db).map_err(|source| { + legacy_db_import_error(&invocation.data_dir, &legacy_db, source) + })?; + let imported_records = import.apps.len(); + let source_counts = source_counts_json(&import); + let warnings = import_warnings_json(&import.warnings); + if import.source_counts.app_rows > 0 && imported_records == 0 { + let report_path = create_migration_report_with_source_counts( + &invocation.data_dir, + &legacy_db, + "migration.invalid_db", + "Legacy Room database has app rows but no importable app rows", + 0, + 0, + &warnings, + Some(&source_counts), + )?; + return Err(CliError::InvalidLegacyDb { report_path }); + } + import_legacy_room_db(&db, &import)?; + let report_path = create_migration_report_with_source_counts( + &invocation.data_dir, + &legacy_db, + "migration.imported", + "Legacy Room database imported", + imported_records as u64, + imported_records as u64, + &warnings, + Some(&source_counts), + )?; + let records = db.tracked_packages()?; + Ok(json!({ + "report_path": report_path, + "imported_records": imported_records, + "source_counts": source_counts, + "warnings": warnings, + "apps": tracked_packages_json(records), + })) + } CliCommand::LegacyReportList => { open_initialized_storage(&invocation.data_dir)?; Ok(json!({ "reports": list_migration_reports(&invocation.data_dir)? })) @@ -574,10 +667,11 @@ fn tracked_packages_json(packages: Vec) -> Vec { } fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<(), CliError> { + let mut packages = Vec::new(); for app in &bundle.apps { let mapping = map_legacy_app( &LegacyAppRecord { - kind: app.kind.to_legacy_kind()?, + kind: app.kind.to_legacy_kind(), installed_id: app.installed_id.clone(), official_package_available: app.official_package_available, common_conversion_available: app.common_conversion_available, @@ -588,14 +682,7 @@ fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<( }), ) .map_err(|source| CliError::Storage(source.to_string()))?; - db.upsert_tracked_package(&TrackedPackageUpsert { - package_id: mapping.package_id, - enabled: true, - favorite: mapping.user_state.favorite, - ignored_version: mapping.user_state.ignored_version, - repository_id: None, - package_resolution: stored_resolution(mapping.package_resolution), - })?; + packages.push(tracked_package_upsert(mapping)); } let report_json = json!({ @@ -604,10 +691,58 @@ fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<( "imported_records": bundle.apps.len(), }) .to_string(); - db.insert_migration_record("legacy-room-v17", "legacy-room-bundle", &report_json)?; + db.import_tracked_packages_with_migration_record( + &packages, + &MigrationRecordUpsert { + id: LEGACY_ROOM_MIGRATION_ID, + source: "legacy-room-bundle", + report_json: &report_json, + }, + )?; Ok(()) } +fn import_legacy_room_db(db: &MainDb, import: &LegacyRoomDbImport) -> Result<(), CliError> { + let mut packages = Vec::new(); + for app in &import.apps { + let mapping = map_legacy_app(&app.app, Some(&app.user_state)) + .map_err(|source| CliError::Storage(source.to_string()))?; + packages.push(tracked_package_upsert(mapping)); + } + + let report_json = json!({ + "ok": true, + "source": "legacy-room-db", + "version": import.version, + "imported_records": import.apps.len(), + "source_counts": source_counts_json(import), + "warnings": import_warnings_json(&import.warnings), + }) + .to_string(); + db.import_tracked_packages_with_migration_record( + &packages, + &MigrationRecordUpsert { + id: LEGACY_ROOM_MIGRATION_ID, + source: "legacy-room-db", + report_json: &report_json, + }, + )?; + Ok(()) +} + +fn tracked_package_upsert( + mapping: getter_storage::legacy_room::LegacyAppMapping, +) -> TrackedPackageUpsert { + TrackedPackageUpsert { + package_id: mapping.package_id, + enabled: true, + favorite: mapping.user_state.favorite, + ignored_version: mapping.user_state.ignored_version, + repository_id: None, + package_resolution: stored_resolution(mapping.package_resolution), + } +} + fn stored_resolution(resolution: LegacyPackageResolution) -> StoredPackageResolution { match resolution { LegacyPackageResolution::OfficialRepositoryPackage => { @@ -622,6 +757,54 @@ fn stored_resolution(resolution: LegacyPackageResolution) -> StoredPackageResolu } } +fn legacy_db_import_error( + data_dir: &Path, + legacy_db: &Path, + source: LegacyRoomReadError, +) -> CliError { + let (code, unsupported) = match source { + LegacyRoomReadError::UnsupportedVersion { .. } => ("migration.unsupported_db", true), + LegacyRoomReadError::Sqlite(_) | LegacyRoomReadError::MissingRequiredTable(_) => { + ("migration.invalid_db", false) + } + }; + match create_migration_report_with_source_counts( + data_dir, + legacy_db, + code, + &source.to_string(), + 0, + 0, + &[], + None, + ) { + Ok(report_path) if unsupported => CliError::UnsupportedLegacyDb { report_path }, + Ok(report_path) => CliError::InvalidLegacyDb { report_path }, + Err(error) => error, + } +} + +fn source_counts_json(import: &LegacyRoomDbImport) -> Value { + json!({ + "app_rows": import.source_counts.app_rows, + "extra_app_rows": import.source_counts.extra_app_rows, + "hub_rows": import.source_counts.hub_rows, + "extra_hub_rows": import.source_counts.extra_hub_rows, + }) +} + +fn import_warnings_json(warnings: &[LegacyRoomImportWarning]) -> Vec { + warnings + .iter() + .map(|warning| { + json!({ + "code": warning.code(), + "message": warning.message(), + }) + }) + .collect() +} + fn main_db_path(data_dir: &Path) -> PathBuf { data_dir.join(MAIN_DB_FILE) } @@ -632,11 +815,35 @@ fn cache_db_path(data_dir: &Path) -> PathBuf { fn create_migration_report( data_dir: &Path, - bundle: &Path, + source_file: &Path, code: &str, detail: &str, imported_records: u64, tracked_records: u64, + warnings: &[Value], +) -> Result { + create_migration_report_with_source_counts( + data_dir, + source_file, + code, + detail, + imported_records, + tracked_records, + warnings, + None, + ) +} + +#[allow(clippy::too_many_arguments)] +fn create_migration_report_with_source_counts( + data_dir: &Path, + source_file: &Path, + code: &str, + detail: &str, + imported_records: u64, + tracked_records: u64, + warnings: &[Value], + source_counts: Option<&Value>, ) -> Result { let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); fs::create_dir_all(&reports_dir).map_err(|source| { @@ -651,13 +858,17 @@ fn create_migration_report( message: match code { "migration.invalid_bundle" => "Legacy Room export bundle is invalid", "migration.unsupported_bundle" => "Legacy Room bundle import is not implemented yet", - "migration.imported" => "Legacy Room bundle imported", + "migration.invalid_db" => "Legacy Room database is invalid", + "migration.unsupported_db" => "Legacy Room database version is not supported", + "migration.imported" => "Legacy Room data imported", _ => "Legacy migration failed", }, - bundle_file_name: bundle.file_name().and_then(|name| name.to_str()), + source_file_name: source_file.file_name().and_then(|name| name.to_str()), detail, imported_records, tracked_records, + warnings, + source_counts, }; let bytes = serde_json::to_vec_pretty(&report) .map_err(|source| CliError::Storage(format!("failed to serialize report: {source}")))?; @@ -708,9 +919,17 @@ fn list_migration_reports(data_dir: &Path) -> Result, CliError> { "ok": report.get("ok").and_then(Value::as_bool).unwrap_or(false), "code": report.get("code").and_then(Value::as_str).unwrap_or("migration.unknown"), "message": report.get("message").and_then(Value::as_str).unwrap_or("Legacy migration report"), - "bundle_file_name": report.get("bundle_file_name").and_then(Value::as_str), + "source_file_name": report + .get("source_file_name") + .or_else(|| report.get("bundle_file_name")) + .and_then(Value::as_str), "imported_records": report.get("imported_records").and_then(Value::as_u64).unwrap_or(0), "tracked_records": report.get("tracked_records").and_then(Value::as_u64).unwrap_or(0), + "warnings": report + .get("warnings") + .cloned() + .unwrap_or_else(|| Value::Array(Vec::new())), + "source_counts": report.get("source_counts").cloned().unwrap_or(Value::Null), })) }) .collect() @@ -721,10 +940,13 @@ struct MigrationReport<'a> { ok: bool, code: &'a str, message: &'a str, - bundle_file_name: Option<&'a str>, + source_file_name: Option<&'a str>, detail: &'a str, imported_records: u64, tracked_records: u64, + warnings: &'a [Value], + #[serde(skip_serializing_if = "Option::is_none")] + source_counts: Option<&'a Value>, } fn success_envelope(command: &str, data: Value) -> String { @@ -762,7 +984,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|legacy import-room-bundle |legacy report-list>\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() } #[derive(Debug, Deserialize)] @@ -795,11 +1017,11 @@ enum LegacyBundleAppKind { } impl LegacyBundleAppKind { - fn to_legacy_kind(&self) -> Result { - Ok(match self { + const fn to_legacy_kind(&self) -> LegacyAppKind { + match self { Self::Android => LegacyAppKind::Android, Self::Magisk => LegacyAppKind::Magisk, - }) + } } } @@ -816,6 +1038,7 @@ impl CliCommand { Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", + Self::LegacyImportRoomDb { .. } => "legacy import-room-db", Self::LegacyReportList => "legacy report-list", } } diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index ddc42f2..5d07d58 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -1,4 +1,5 @@ use cucumber::{given, then, when, World as _}; +use rusqlite::Connection; use serde_json::Value; use std::fs; use std::path::PathBuf; @@ -10,6 +11,7 @@ struct CliWorld { temp: Option, data_dir: Option, bundle: Option, + legacy_db: Option, fixture_repo_id: Option, fixture_repo_path: Option, fixture_package_id: Option, @@ -65,6 +67,48 @@ fn valid_legacy_export_bundle_with_android_app(world: &mut CliWorld) { world.bundle = Some(bundle); } +#[given("a legacy Room v17 database with an Android app and extra app state")] +fn legacy_room_v17_database_with_android_app(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let legacy_db = temp.path().join("app_metadata_database.db"); + create_fixture_legacy_room_db(&legacy_db, 17, true); + world.legacy_db = Some(legacy_db); +} + +#[given("an unsupported legacy Room database")] +fn unsupported_legacy_room_database(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let legacy_db = temp.path().join("app_metadata_database.db"); + create_fixture_legacy_room_db(&legacy_db, 16, true); + world.legacy_db = Some(legacy_db); +} + +#[given("a malformed legacy Room database")] +fn malformed_legacy_room_database(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let legacy_db = temp.path().join("app_metadata_database.db"); + create_fixture_legacy_room_db(&legacy_db, 17, false); + world.legacy_db = Some(legacy_db); +} + +#[given("a legacy Room v17 database with only unsupported app rows")] +fn legacy_room_v17_database_with_only_unsupported_app_rows(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let legacy_db = temp.path().join("app_metadata_database.db"); + create_fixture_legacy_room_db(&legacy_db, 17, true); + let conn = Connection::open(&legacy_db).expect("open legacy Room fixture"); + conn.execute("DELETE FROM extra_app", []) + .expect("delete fixture extra_app"); + conn.execute("DELETE FROM app", []) + .expect("delete fixture app"); + conn.execute( + "INSERT INTO app(id, name, app_id, ignore_version_number, star) VALUES (1, 'Unsupported', ?1, NULL, 0)", + [r#"{"unknown_provider":"com.example.unsupported"}"#], + ) + .expect("insert unsupported app row"); + world.legacy_db = Some(legacy_db); +} + #[given(expr = "a fixture Lua repository {string} with package {string}")] fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); @@ -263,6 +307,21 @@ fn run_getter_legacy_import(world: &mut CliWorld) { world.json = None; } +#[when("I run getter legacy import-room-db for that database")] +fn run_getter_legacy_import_db(world: &mut CliWorld) { + let legacy_db = world.legacy_db.as_ref().expect("legacy db exists"); + let output = run_getter( + world, + [ + "legacy".to_owned(), + "import-room-db".to_owned(), + legacy_db.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter legacy report-list for that directory")] fn run_getter_legacy_report_list(world: &mut CliWorld) { let output = run_getter(world, ["legacy".to_owned(), "report-list".to_owned()]); @@ -282,8 +341,31 @@ fn command_fails_with_migration_error(world: &mut CliWorld) { assert_eq!(output.status.code(), Some(20)); let json = parse_stdout(output); assert_eq!(json["ok"], false); - assert_eq!(json["command"], "legacy import-room-bundle"); - assert_eq!(json["error"]["code"], "migration.invalid_bundle"); + assert!( + json["command"] == "legacy import-room-bundle" + || json["command"] == "legacy import-room-db" + ); + assert!(matches!( + json["error"]["code"].as_str(), + Some("migration.invalid_bundle" | "migration.invalid_db" | "migration.unsupported_db") + )); + world.json = Some(json); +} + +#[then(expr = "the command fails with direct DB migration error {string}")] +fn command_fails_with_direct_db_migration_error(world: &mut CliWorld, code: String) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(20)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["command"], "legacy import-room-db"); + assert_eq!(json["error"]["code"], code); + let report_path = json["error"]["report_path"] + .as_str() + .expect("report_path should be a string"); + let report = fs::read_to_string(report_path).expect("report should be readable"); + let report_json: Value = serde_json::from_str(&report).expect("report should be JSON"); + assert_eq!(report_json["code"], code); world.json = Some(json); } @@ -451,7 +533,10 @@ fn sanitized_migration_report_available(world: &mut CliWorld) { let report_json: Value = serde_json::from_str(&report).expect("report should be JSON"); assert_eq!(report_json["ok"], false); assert_eq!(report_json["imported_records"], 0); - assert!(report_json.get("bundle_file_name").is_some()); + assert!( + report_json.get("source_file_name").is_some() + || report_json.get("bundle_file_name").is_some() + ); assert!( report_json.get("raw_bundle").is_none(), "report must not include raw bundle content" @@ -462,7 +547,10 @@ fn sanitized_migration_report_available(world: &mut CliWorld) { fn import_reports_one_tracked_app(world: &mut CliWorld) { let json = current_json(world); assert_eq!(json["ok"], true); - assert_eq!(json["command"], "legacy import-room-bundle"); + assert!( + json["command"] == "legacy import-room-bundle" + || json["command"] == "legacy import-room-db" + ); assert_eq!(json["data"]["imported_records"], 1); assert_eq!( json["data"]["apps"].as_array().expect("apps array").len(), @@ -470,6 +558,64 @@ fn import_reports_one_tracked_app(world: &mut CliWorld) { ); } +#[then("the direct migration reports dropped legacy hub warnings")] +fn direct_migration_reports_dropped_hub_warnings(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "legacy import-room-db"); + assert_eq!(json["data"]["source_counts"]["hub_rows"], 1); + assert_eq!(json["data"]["source_counts"]["extra_hub_rows"], 1); + let warnings = json["data"]["warnings"].as_array().expect("warnings array"); + assert!(warnings + .iter() + .any(|warning| { warning["code"].as_str() == Some("legacy.dropped_hub_rows") })); + assert!(warnings + .iter() + .any(|warning| { warning["code"].as_str() == Some("legacy.dropped_extra_hub_rows") })); +} + +#[then("the direct migration report stays sanitized")] +fn direct_migration_report_stays_sanitized(world: &mut CliWorld) { + let json = current_json(world); + let stdout = serde_json::to_string(json).expect("JSON output serializes"); + assert_sanitized_direct_room_report_text(&stdout); + let report_path = json["data"]["report_path"] + .as_str() + .expect("report_path should be a string"); + let report = fs::read_to_string(report_path).expect("report should be readable"); + assert_sanitized_direct_room_report_text(&report); + let report_json: Value = serde_json::from_str(&report).expect("report should be JSON"); + assert_eq!(report_json["source_counts"]["hub_rows"], 1); + assert_eq!(report_json["source_counts"]["extra_hub_rows"], 1); + assert_report_has_drop_warnings(&report_json); +} + +#[then("the output reports the legacy Room migration was already completed")] +fn output_reports_legacy_room_migration_already_completed(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "legacy import-room-db"); + assert_eq!(json["data"]["already_imported"], true); + assert_eq!(json["data"]["imported_records"], 0); + assert_eq!( + json["data"]["apps"].as_array().expect("apps array").len(), + 1 + ); +} + +#[then("the bundle output reports the legacy Room migration was already completed")] +fn bundle_output_reports_legacy_room_migration_already_completed(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "legacy import-room-bundle"); + assert_eq!(json["data"]["already_imported"], true); + assert_eq!(json["data"]["imported_records"], 0); + assert_eq!( + json["data"]["apps"].as_array().expect("apps array").len(), + 1 + ); +} + #[then(expr = "the output lists migration report {string}")] fn output_lists_migration_report(world: &mut CliWorld, code: String) { let json = current_json(world); @@ -484,6 +630,21 @@ fn output_lists_migration_report(world: &mut CliWorld, code: String) { ); } +#[then("the direct migration report list stays sanitized")] +fn direct_migration_report_list_stays_sanitized(world: &mut CliWorld) { + let json = current_json(world); + let report_list = serde_json::to_string(json).expect("report list serializes"); + assert_sanitized_direct_room_report_text(&report_list); + let reports = json["data"]["reports"].as_array().expect("reports array"); + let imported = reports + .iter() + .find(|report| report["code"].as_str() == Some("migration.imported")) + .expect("imported report exists"); + assert_eq!(imported["source_counts"]["hub_rows"], 1); + assert_eq!(imported["source_counts"]["extra_hub_rows"], 1); + assert_report_has_drop_warnings(imported); +} + #[then(expr = "the app list contains imported package {string}")] fn app_list_contains_imported_package(world: &mut CliWorld, package_id: String) { let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); @@ -501,6 +662,23 @@ fn app_list_contains_imported_package(world: &mut CliWorld, package_id: String) world.json = Some(json); } +#[then(expr = "the app list contains directly imported package {string}")] +fn app_list_contains_directly_imported_package(world: &mut CliWorld, package_id: String) { + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + assert_success(&output); + let json = parse_stdout(&output); + let apps = json["data"]["apps"].as_array().expect("apps array"); + let app = apps + .iter() + .find(|app| app["id"].as_str() == Some(package_id.as_str())) + .unwrap_or_else(|| panic!("app list should contain {package_id}: {apps:?}")); + assert_eq!(app["favorite"], true); + assert_eq!(app["ignored_version"], "1.20.0"); + assert_eq!(app["package_resolution"], "missing_package_definition"); + world.output = Some(output); + world.json = Some(json); +} + fn current_json(world: &mut CliWorld) -> &Value { world .json @@ -549,6 +727,96 @@ return package_def {{ world.fixture_package_id = Some(package_id); } +fn create_fixture_legacy_room_db(path: &PathBuf, version: u32, include_app_table: bool) { + let conn = Connection::open(path).expect("create legacy Room fixture"); + conn.pragma_update(None, "user_version", version) + .expect("set user_version"); + if include_app_table { + conn.execute_batch( + r#" +CREATE TABLE app ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + app_id TEXT NOT NULL, + invalid_version_number_field_regex TEXT, + include_version_number_field_regex TEXT, + ignore_version_number TEXT, + cloud_config TEXT, + enable_hub_list TEXT, + star INTEGER +); +CREATE TABLE extra_app ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + app_id TEXT NOT NULL, + mark_version_number TEXT +); +CREATE TABLE hub ( + uuid TEXT PRIMARY KEY, + hub_config TEXT NOT NULL, + auth TEXT NOT NULL, + ignore_app_id_list TEXT NOT NULL, + applications_mode INTEGER NOT NULL DEFAULT 0, + user_ignore_app_id_list TEXT NOT NULL, + sort_point INTEGER NOT NULL DEFAULT 0 +); +CREATE TABLE extra_hub ( + id TEXT PRIMARY KEY, + enable_global INTEGER NOT NULL DEFAULT 0, + url_replace_search TEXT, + url_replace_string TEXT +); +"#, + ) + .expect("create legacy tables"); + let app_id = r#"{"android_app_package":"org.fdroid.fdroid"}"#; + conn.execute( + "INSERT INTO app(id, name, app_id, ignore_version_number, star) VALUES (1, 'F-Droid', ?1, '1.10.0', 1)", + [app_id], + ) + .expect("insert app"); + conn.execute( + "INSERT INTO extra_app(id, app_id, mark_version_number) VALUES (1, ?1, '1.20.0')", + [app_id], + ) + .expect("insert extra app"); + conn.execute( + "INSERT INTO hub(uuid, hub_config, auth, ignore_app_id_list, user_ignore_app_id_list) VALUES ('legacy-hub', '{\"secret\":\"UA_DIRECT_DB_SENTINEL_SECRET\"}', '{\"token\":\"UA_DIRECT_DB_SENTINEL_TOKEN\"}', '[]', '[]')", + [], + ) + .expect("insert hub"); + conn.execute( + "INSERT INTO extra_hub(id, enable_global, url_replace_search, url_replace_string) VALUES ('GLOBAL', 1, 'UA_DIRECT_DB_SENTINEL_SEARCH', 'UA_DIRECT_DB_SENTINEL_REPLACE')", + [], + ) + .expect("insert extra hub"); + } +} + +fn assert_report_has_drop_warnings(report: &Value) { + let warnings = report["warnings"].as_array().expect("warnings array"); + assert!(warnings + .iter() + .any(|warning| warning["code"].as_str() == Some("legacy.dropped_hub_rows"))); + assert!(warnings + .iter() + .any(|warning| warning["code"].as_str() == Some("legacy.dropped_extra_hub_rows"))); +} + +fn assert_sanitized_direct_room_report_text(text: &str) { + for forbidden in [ + "UA_DIRECT_DB_SENTINEL_SECRET", + "UA_DIRECT_DB_SENTINEL_TOKEN", + "UA_DIRECT_DB_SENTINEL_SEARCH", + "UA_DIRECT_DB_SENTINEL_REPLACE", + "legacy-hub", + ] { + assert!( + !text.contains(forbidden), + "direct Room migration report must not expose {forbidden}: {text}" + ); + } +} + fn create_custom_fixture_lua_repository( world: &mut CliWorld, repo_id: String, diff --git a/crates/getter-cli/tests/features/cli/legacy_import_room_db.feature b/crates/getter-cli/tests/features/cli/legacy_import_room_db.feature new file mode 100644 index 0000000..8ddcf2b --- /dev/null +++ b/crates/getter-cli/tests/features/cli/legacy_import_room_db.feature @@ -0,0 +1,70 @@ +@getter-cli @migration +Feature: Direct legacy Room database import + Scenario: User imports a supported legacy Room database into tracked app state + Given an initialized getter data directory + And a legacy Room v17 database with an Android app and extra app state + When I run getter legacy import-room-db for that database + Then the command succeeds + And the output is valid JSON + And the import reports one tracked app + And the direct migration reports dropped legacy hub warnings + And the direct migration report stays sanitized + And the app list contains directly imported package "android/org.fdroid.fdroid" + When I run getter legacy report-list for that directory + Then the output lists migration report "migration.imported" + And the direct migration report list stays sanitized + + Scenario: User does not rerun a completed direct legacy Room migration + Given an initialized getter data directory + And a legacy Room v17 database with an Android app and extra app state + When I run getter legacy import-room-db for that database + Then the command succeeds + When I run getter legacy import-room-db for that database + Then the command succeeds + And the output reports the legacy Room migration was already completed + + Scenario: User cannot apply a bridge bundle after completed direct legacy Room migration + Given an initialized getter data directory + And a legacy Room v17 database with an Android app and extra app state + And a syntactically valid legacy export bundle with an Android app + When I run getter legacy import-room-db for that database + Then the command succeeds + When I run getter legacy import-room-bundle for that bundle + Then the command succeeds + And the bundle output reports the legacy Room migration was already completed + And the app list contains directly imported package "android/org.fdroid.fdroid" + + Scenario: User cannot apply a direct legacy Room database after completed bridge bundle migration + Given an initialized getter data directory + And a syntactically valid legacy export bundle with an Android app + And a legacy Room v17 database with an Android app and extra app state + When I run getter legacy import-room-bundle for that bundle + Then the command succeeds + When I run getter legacy import-room-db for that database + Then the command succeeds + And the output reports the legacy Room migration was already completed + And the app list contains imported package "android/org.fdroid.fdroid" + + Scenario: User receives a recovery report for an unsupported legacy Room database + Given an initialized getter data directory + And an unsupported legacy Room database + When I run getter legacy import-room-db for that database + Then the command fails with direct DB migration error "migration.unsupported_db" + And no partially usable imported state is created + And a sanitized migration report is available + + Scenario: User receives a recovery report for a malformed legacy Room database + Given an initialized getter data directory + And a malformed legacy Room database + When I run getter legacy import-room-db for that database + Then the command fails with direct DB migration error "migration.invalid_db" + And no partially usable imported state is created + And a sanitized migration report is available + + Scenario: User receives a recovery report when no legacy app rows can be mapped + Given an initialized getter data directory + And a legacy Room v17 database with only unsupported app rows + When I run getter legacy import-room-db for that database + Then the command fails with direct DB migration error "migration.invalid_db" + And no partially usable imported state is created + And a sanitized migration report is available diff --git a/crates/getter-storage/Cargo.toml b/crates/getter-storage/Cargo.toml index 6a561a4..bc0964b 100644 --- a/crates/getter-storage/Cargo.toml +++ b/crates/getter-storage/Cargo.toml @@ -6,6 +6,7 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core" } rusqlite = { version = "0.32", features = ["bundled"] } +serde_json = "1" thiserror = "1" [dev-dependencies] diff --git a/crates/getter-storage/src/legacy_room.rs b/crates/getter-storage/src/legacy_room.rs index 798809e..711ea1f 100644 --- a/crates/getter-storage/src/legacy_room.rs +++ b/crates/getter-storage/src/legacy_room.rs @@ -1,10 +1,18 @@ //! Legacy Room migration mapping tests and pure mapping helpers. //! -//! This module intentionally contains no Android Room/database reader. It is the -//! TDD boundary for source->target mapping rules before the full migration -//! implementation is added. +//! This module owns Rust-side interpretation of legacy Room data. Android and +//! Flutter adapters may locate/checkpoint/copy the old SQLite files, but package +//! identity, user-state mapping, warnings, and target semantics live here. use getter_core::PackageId; +use rusqlite::{Connection, OpenFlags}; +use serde_json::Value; +use std::collections::HashMap; +use std::path::Path; + +pub const LEGACY_ROOM_SUPPORTED_VERSION: u32 = 17; +const LEGACY_ANDROID_APP_TYPE: &str = "android_app_package"; +const LEGACY_ANDROID_MAGISK_MODULE_TYPE: &str = "android_magisk_module"; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum LegacyAppKind { @@ -52,6 +60,64 @@ pub enum LegacyMigrationWarning { MissingPackageDefinition { package_id: PackageId }, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LegacyRoomDbImport { + pub version: u32, + pub source_counts: LegacyRoomSourceCounts, + pub apps: Vec, + pub warnings: Vec, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct LegacyRoomSourceCounts { + pub app_rows: u64, + pub extra_app_rows: u64, + pub hub_rows: u64, + pub extra_hub_rows: u64, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LegacyRoomImportedApp { + pub app: LegacyAppRecord, + pub user_state: LegacyExtraAppRecord, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum LegacyRoomImportWarning { + SkippedApp { row_id: i64, reason: String }, + SkippedExtraApp { row_id: i64, reason: String }, + DroppedHubRows { rows: u64 }, + DroppedExtraHubRows { rows: u64 }, +} + +impl LegacyRoomImportWarning { + pub fn code(&self) -> &'static str { + match self { + Self::SkippedApp { .. } => "legacy.skipped_app", + Self::SkippedExtraApp { .. } => "legacy.skipped_extra_app", + Self::DroppedHubRows { .. } => "legacy.dropped_hub_rows", + Self::DroppedExtraHubRows { .. } => "legacy.dropped_extra_hub_rows", + } + } + + pub fn message(&self) -> String { + match self { + Self::SkippedApp { row_id, reason } => { + format!("Skipped legacy app row {row_id}: {reason}") + } + Self::SkippedExtraApp { row_id, reason } => { + format!("Skipped legacy extra_app row {row_id}: {reason}") + } + Self::DroppedHubRows { rows } => { + format!("Legacy hub rows are not imported in this slice: {rows}") + } + Self::DroppedExtraHubRows { rows } => { + format!("Legacy extra_hub rows are not imported in this slice: {rows}") + } + } + } +} + #[derive(Debug, thiserror::Error)] pub enum LegacyRoomMappingError { #[error("legacy installed id is empty")] @@ -60,6 +126,16 @@ pub enum LegacyRoomMappingError { PackageId(#[from] getter_core::PackageIdError), } +#[derive(Debug, thiserror::Error)] +pub enum LegacyRoomReadError { + #[error("sqlite error: {0}")] + Sqlite(#[from] rusqlite::Error), + #[error("unsupported legacy Room database version {found}; expected {expected}")] + UnsupportedVersion { found: u32, expected: u32 }, + #[error("legacy Room database is missing required table '{0}'")] + MissingRequiredTable(&'static str), +} + pub fn map_legacy_app( app: &LegacyAppRecord, extra: Option<&LegacyExtraAppRecord>, @@ -107,6 +183,227 @@ pub fn map_legacy_package_id( Ok(format!("{prefix}/{installed_id}").parse()?) } +pub fn read_legacy_room_database(path: &Path) -> Result { + let conn = Connection::open_with_flags(path, OpenFlags::SQLITE_OPEN_READ_ONLY)?; + let version = legacy_user_version(&conn)?; + if version != LEGACY_ROOM_SUPPORTED_VERSION { + return Err(LegacyRoomReadError::UnsupportedVersion { + found: version, + expected: LEGACY_ROOM_SUPPORTED_VERSION, + }); + } + if !table_exists(&conn, "app")? { + return Err(LegacyRoomReadError::MissingRequiredTable("app")); + } + + let mut warnings = Vec::new(); + let extra_apps = read_extra_apps(&conn, &mut warnings)?; + let apps = read_apps(&conn, &extra_apps, &mut warnings)?; + let source_counts = LegacyRoomSourceCounts { + app_rows: table_row_count(&conn, "app")?.unwrap_or(0), + extra_app_rows: table_row_count(&conn, "extra_app")?.unwrap_or(0), + hub_rows: table_row_count(&conn, "hub")?.unwrap_or(0), + extra_hub_rows: table_row_count(&conn, "extra_hub")?.unwrap_or(0), + }; + if source_counts.hub_rows > 0 { + warnings.push(LegacyRoomImportWarning::DroppedHubRows { + rows: source_counts.hub_rows, + }); + } + if source_counts.extra_hub_rows > 0 { + warnings.push(LegacyRoomImportWarning::DroppedExtraHubRows { + rows: source_counts.extra_hub_rows, + }); + } + + Ok(LegacyRoomDbImport { + version, + source_counts, + apps, + warnings, + }) +} + +fn legacy_user_version(conn: &Connection) -> Result { + let version: u32 = conn.query_row("PRAGMA user_version", [], |row| row.get(0))?; + Ok(version) +} + +fn read_apps( + conn: &Connection, + extra_apps: &HashMap, + warnings: &mut Vec, +) -> Result, LegacyRoomReadError> { + let mut stmt = conn.prepare( + r#" +SELECT id, app_id, ignore_version_number, star +FROM app +ORDER BY id ASC +"#, + )?; + let rows = stmt.query_map([], |row| { + Ok(( + row.get::<_, i64>(0)?, + row.get::<_, String>(1)?, + row.get::<_, Option>(2)?, + row.get::<_, Option>(3)?, + )) + })?; + + let mut apps = Vec::new(); + for row in rows { + let (row_id, app_id_json, ignored_version, star) = row?; + let app_id = match parse_app_id_map(&app_id_json) { + Ok(app_id) => app_id, + Err(reason) => { + warnings.push(LegacyRoomImportWarning::SkippedApp { row_id, reason }); + continue; + } + }; + let (kind, installed_id) = match installed_id_from_app_id(&app_id) { + Ok(target) => target, + Err(reason) => { + warnings.push(LegacyRoomImportWarning::SkippedApp { row_id, reason }); + continue; + } + }; + let package_key = match map_legacy_package_id(kind, &installed_id) { + Ok(package_id) => package_id.to_string(), + Err(error) => { + warnings.push(LegacyRoomImportWarning::SkippedApp { + row_id, + reason: error.to_string(), + }); + continue; + } + }; + let extra = extra_apps.get(&package_key); + apps.push(LegacyRoomImportedApp { + app: LegacyAppRecord { + kind, + installed_id, + official_package_available: false, + common_conversion_available: false, + }, + user_state: LegacyExtraAppRecord { + ignored_version: extra + .and_then(|extra| extra.ignored_version.clone()) + .or(ignored_version), + favorite: star.unwrap_or(0) != 0, + }, + }); + } + Ok(apps) +} + +fn read_extra_apps( + conn: &Connection, + warnings: &mut Vec, +) -> Result, LegacyRoomReadError> { + if !table_exists(conn, "extra_app")? { + return Ok(HashMap::new()); + } + let mut stmt = conn.prepare( + r#" +SELECT id, app_id, mark_version_number +FROM extra_app +ORDER BY id ASC +"#, + )?; + let rows = stmt.query_map([], |row| { + Ok(( + row.get::<_, i64>(0)?, + row.get::<_, String>(1)?, + row.get::<_, Option>(2)?, + )) + })?; + + let mut extras = HashMap::new(); + for row in rows { + let (row_id, app_id_json, ignored_version) = row?; + let app_id = match parse_app_id_map(&app_id_json) { + Ok(app_id) => app_id, + Err(reason) => { + warnings.push(LegacyRoomImportWarning::SkippedExtraApp { row_id, reason }); + continue; + } + }; + let (kind, installed_id) = match installed_id_from_app_id(&app_id) { + Ok(target) => target, + Err(reason) => { + warnings.push(LegacyRoomImportWarning::SkippedExtraApp { row_id, reason }); + continue; + } + }; + let package_id = match map_legacy_package_id(kind, &installed_id) { + Ok(package_id) => package_id, + Err(error) => { + warnings.push(LegacyRoomImportWarning::SkippedExtraApp { + row_id, + reason: error.to_string(), + }); + continue; + } + }; + extras.insert( + package_id.to_string(), + LegacyExtraAppRecord { + ignored_version, + favorite: false, + }, + ); + } + Ok(extras) +} + +fn parse_app_id_map(value: &str) -> Result, String> { + let value: Value = serde_json::from_str(value).map_err(|error| error.to_string())?; + let object = value + .as_object() + .ok_or_else(|| "app_id must be a JSON object".to_owned())?; + let mut app_id = HashMap::new(); + for (key, value) in object { + if let Some(value) = value.as_str().filter(|value| !value.is_empty()) { + app_id.insert(key.clone(), value.to_owned()); + } + } + Ok(app_id) +} + +fn installed_id_from_app_id( + app_id: &HashMap, +) -> Result<(LegacyAppKind, String), String> { + if let Some(module_id) = app_id.get(LEGACY_ANDROID_MAGISK_MODULE_TYPE) { + return Ok((LegacyAppKind::Magisk, module_id.clone())); + } + if let Some(package_name) = app_id.get(LEGACY_ANDROID_APP_TYPE) { + return Ok((LegacyAppKind::Android, package_name.clone())); + } + Err(format!( + "app_id has no supported '{}' or '{}' value", + LEGACY_ANDROID_APP_TYPE, LEGACY_ANDROID_MAGISK_MODULE_TYPE + )) +} + +fn table_exists(conn: &Connection, table: &str) -> Result { + let exists: i64 = conn.query_row( + "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = ?1", + [table], + |row| row.get(0), + )?; + Ok(exists != 0) +} + +fn table_row_count(conn: &Connection, table: &str) -> Result, LegacyRoomReadError> { + if !table_exists(conn, table)? { + return Ok(None); + } + let count: u64 = conn.query_row(&format!("SELECT COUNT(*) FROM {table}"), [], |row| { + row.get(0) + })?; + Ok(Some(count)) +} + #[cfg(test)] mod tests { use super::*; @@ -212,4 +509,187 @@ mod tests { assert_eq!(mapping.user_state.ignored_version.as_deref(), Some("1.2.3")); assert!(mapping.user_state.favorite); } + + #[test] + fn direct_room_reader_imports_app_rows_and_extra_app_state() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("legacy.db"); + create_fixture_legacy_db(&db_path, LEGACY_ROOM_SUPPORTED_VERSION); + + let import = read_legacy_room_database(&db_path).unwrap(); + + assert_eq!(import.version, LEGACY_ROOM_SUPPORTED_VERSION); + assert_eq!(import.apps.len(), 1); + assert_eq!(import.source_counts.app_rows, 1); + assert_eq!(import.source_counts.extra_app_rows, 1); + assert_eq!(import.source_counts.hub_rows, 1); + assert_eq!(import.source_counts.extra_hub_rows, 1); + assert_eq!(import.apps[0].app.kind, LegacyAppKind::Android); + assert_eq!(import.apps[0].app.installed_id, "org.fdroid.fdroid"); + assert_eq!( + import.apps[0].user_state.ignored_version.as_deref(), + Some("1.20.0") + ); + assert!(import.apps[0].user_state.favorite); + assert!(import + .warnings + .iter() + .any(|warning| matches!(warning, LegacyRoomImportWarning::DroppedHubRows { rows: 1 }))); + assert!(import.warnings.iter().any(|warning| matches!( + warning, + LegacyRoomImportWarning::DroppedExtraHubRows { rows: 1 } + ))); + } + + #[test] + fn direct_room_reader_maps_magisk_app_id_rows() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("legacy.db"); + create_fixture_legacy_db(&db_path, LEGACY_ROOM_SUPPORTED_VERSION); + let conn = Connection::open(&db_path).unwrap(); + conn.execute("DELETE FROM app", []).unwrap(); + conn.execute("DELETE FROM extra_app", []).unwrap(); + conn.execute( + "INSERT INTO app(id, name, app_id, ignore_version_number, star) VALUES (1, 'Zygisk Next', ?1, NULL, 0)", + [r#"{"android_magisk_module":"zygisk-next"}"#], + ) + .unwrap(); + drop(conn); + + let import = read_legacy_room_database(&db_path).unwrap(); + + assert_eq!(import.apps.len(), 1); + assert_eq!(import.apps[0].app.kind, LegacyAppKind::Magisk); + assert_eq!(import.apps[0].app.installed_id, "zygisk-next"); + let mapping = + map_legacy_app(&import.apps[0].app, Some(&import.apps[0].user_state)).unwrap(); + assert_eq!(mapping.package_id.to_string(), "magisk/zygisk-next"); + } + + #[test] + fn direct_room_reader_skips_invalid_app_rows_without_dropping_valid_apps() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("legacy.db"); + create_fixture_legacy_db(&db_path, LEGACY_ROOM_SUPPORTED_VERSION); + let conn = Connection::open(&db_path).unwrap(); + conn.execute( + "INSERT INTO app(id, name, app_id, ignore_version_number, star) VALUES (2, 'Broken', 'not-json', NULL, 0)", + [], + ) + .unwrap(); + drop(conn); + + let import = read_legacy_room_database(&db_path).unwrap(); + + assert_eq!(import.source_counts.app_rows, 2); + assert_eq!(import.apps.len(), 1); + assert_eq!(import.apps[0].app.installed_id, "org.fdroid.fdroid"); + assert!(import.warnings.iter().any(|warning| matches!( + warning, + LegacyRoomImportWarning::SkippedApp { row_id: 2, .. } + ))); + } + + #[test] + fn direct_room_reader_skips_malformed_optional_extra_app_rows() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("legacy.db"); + create_fixture_legacy_db(&db_path, LEGACY_ROOM_SUPPORTED_VERSION); + let conn = Connection::open(&db_path).unwrap(); + conn.execute( + "INSERT INTO extra_app(id, app_id, mark_version_number) VALUES (2, 'not-json', '9.9.9')", + [], + ) + .unwrap(); + drop(conn); + + let import = read_legacy_room_database(&db_path).unwrap(); + + assert_eq!(import.apps.len(), 1); + assert!(import.warnings.iter().any(|warning| { + matches!( + warning, + LegacyRoomImportWarning::SkippedExtraApp { row_id: 2, .. } + ) + })); + } + + #[test] + fn direct_room_reader_rejects_unsupported_schema_version() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("legacy.db"); + create_fixture_legacy_db(&db_path, 16); + + let error = read_legacy_room_database(&db_path).unwrap_err(); + + assert!(matches!( + error, + LegacyRoomReadError::UnsupportedVersion { + found: 16, + expected: LEGACY_ROOM_SUPPORTED_VERSION, + } + )); + } + + fn create_fixture_legacy_db(path: &Path, version: u32) { + let conn = Connection::open(path).unwrap(); + conn.pragma_update(None, "user_version", version).unwrap(); + conn.execute_batch( + r#" +CREATE TABLE app ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + app_id TEXT NOT NULL, + invalid_version_number_field_regex TEXT, + include_version_number_field_regex TEXT, + ignore_version_number TEXT, + cloud_config TEXT, + enable_hub_list TEXT, + star INTEGER +); +CREATE TABLE extra_app ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + app_id TEXT NOT NULL, + mark_version_number TEXT +); +CREATE TABLE hub ( + uuid TEXT PRIMARY KEY, + hub_config TEXT NOT NULL, + auth TEXT NOT NULL, + ignore_app_id_list TEXT NOT NULL, + applications_mode INTEGER NOT NULL DEFAULT 0, + user_ignore_app_id_list TEXT NOT NULL, + sort_point INTEGER NOT NULL DEFAULT 0 +); +CREATE TABLE extra_hub ( + id TEXT PRIMARY KEY, + enable_global INTEGER NOT NULL DEFAULT 0, + url_replace_search TEXT, + url_replace_string TEXT +); +"#, + ) + .unwrap(); + let app_id = r#"{"android_app_package":"org.fdroid.fdroid"}"#; + conn.execute( + "INSERT INTO app(id, name, app_id, ignore_version_number, star) VALUES (1, 'F-Droid', ?1, '1.10.0', 1)", + [app_id], + ) + .unwrap(); + conn.execute( + "INSERT INTO extra_app(id, app_id, mark_version_number) VALUES (1, ?1, '1.20.0')", + [app_id], + ) + .unwrap(); + conn.execute( + "INSERT INTO hub(uuid, hub_config, auth, ignore_app_id_list, user_ignore_app_id_list) VALUES ('legacy-hub', '{}', '{}', '[]', '[]')", + [], + ) + .unwrap(); + conn.execute( + "INSERT INTO extra_hub(id, enable_global, url_replace_search, url_replace_string) VALUES ('GLOBAL', 1, 'http://', 'https://')", + [], + ) + .unwrap(); + } } diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index 6eed591..a6f0e56 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -4,7 +4,7 @@ pub mod legacy_room; use getter_core::repository::RepositoryMetadata; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; -use rusqlite::{params, Connection}; +use rusqlite::{params, Connection, Params, Transaction}; use std::path::Path; use std::str::FromStr; @@ -180,33 +180,31 @@ ON CONFLICT(id) DO UPDATE SET &self, package: &TrackedPackageUpsert, ) -> Result<(), StorageError> { - self.conn.execute( + execute_tracked_package_upsert(&self.conn, package)?; + Ok(()) + } + + pub fn import_tracked_packages_with_migration_record( + &self, + packages: &[TrackedPackageUpsert], + record: &MigrationRecordUpsert<'_>, + ) -> Result<(), StorageError> { + let tx = self.conn.unchecked_transaction()?; + for package in packages { + execute_tracked_package_upsert(&tx, package)?; + } + tx.execute( r#" -INSERT INTO tracked_packages( - package_id, - enabled, - favorite, - ignored_version, - repository_id, - package_resolution -) -VALUES (?1, ?2, ?3, ?4, ?5, ?6) -ON CONFLICT(package_id) DO UPDATE SET - enabled = excluded.enabled, - favorite = excluded.favorite, - ignored_version = excluded.ignored_version, - repository_id = excluded.repository_id, - package_resolution = excluded.package_resolution +INSERT INTO migration_records(id, source, report_json) +VALUES (?1, ?2, ?3) +ON CONFLICT(id) DO UPDATE SET + source = excluded.source, + completed_at_unix = unixepoch(), + report_json = excluded.report_json "#, - params![ - package.package_id.to_string(), - bool_to_i64(package.enabled), - bool_to_i64(package.favorite), - package.ignored_version.as_deref(), - package.repository_id.as_ref().map(RepositoryId::as_str), - package.package_resolution.as_str(), - ], + params![record.id, record.source, record.report_json], )?; + tx.commit()?; Ok(()) } @@ -263,6 +261,15 @@ ON CONFLICT(id) DO UPDATE SET Ok(()) } + pub fn migration_record_exists(&self, id: &str) -> Result { + let count: i64 = self.conn.query_row( + "SELECT COUNT(*) FROM migration_records WHERE id = ?1", + [id], + |row| row.get(0), + )?; + Ok(count != 0) + } + pub fn migration_records(&self) -> Result, StorageError> { let mut stmt = self.conn.prepare( "SELECT id, source, report_json FROM migration_records ORDER BY completed_at_unix ASC, id ASC", @@ -370,6 +377,13 @@ pub struct StoredMigrationRecord { pub report_json: String, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MigrationRecordUpsert<'a> { + pub id: &'a str, + pub source: &'a str, + pub report_json: &'a str, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum StoredPackageResolution { OfficialRepositoryPackage, @@ -400,6 +414,55 @@ impl FromStr for StoredPackageResolution { } } +trait SqlExecutor { + fn execute_statement(&self, sql: &str, params: P) -> Result; +} + +impl SqlExecutor for Connection { + fn execute_statement(&self, sql: &str, params: P) -> Result { + self.execute(sql, params) + } +} + +impl SqlExecutor for Transaction<'_> { + fn execute_statement(&self, sql: &str, params: P) -> Result { + self.execute(sql, params) + } +} + +fn execute_tracked_package_upsert( + conn: &impl SqlExecutor, + package: &TrackedPackageUpsert, +) -> Result { + conn.execute_statement( + r#" +INSERT INTO tracked_packages( + package_id, + enabled, + favorite, + ignored_version, + repository_id, + package_resolution +) +VALUES (?1, ?2, ?3, ?4, ?5, ?6) +ON CONFLICT(package_id) DO UPDATE SET + enabled = excluded.enabled, + favorite = excluded.favorite, + ignored_version = excluded.ignored_version, + repository_id = excluded.repository_id, + package_resolution = excluded.package_resolution +"#, + params![ + package.package_id.to_string(), + bool_to_i64(package.enabled), + bool_to_i64(package.favorite), + package.ignored_version.as_deref(), + package.repository_id.as_ref().map(RepositoryId::as_str), + package.package_resolution.as_str(), + ], + ) +} + fn bool_to_i64(value: bool) -> i64 { if value { 1 @@ -475,6 +538,7 @@ mod tests { #[test] fn main_db_records_migration_completion() { let db = MainDb::open_in_memory().unwrap(); + assert!(!db.migration_record_exists("legacy-room-v17").unwrap()); db.insert_migration_record("legacy-room-v17", "legacy-room-bundle", r#"{"ok":true}"#) .unwrap(); @@ -482,6 +546,7 @@ mod tests { assert_eq!(records.len(), 1); assert_eq!(records[0].id, "legacy-room-v17"); assert_eq!(records[0].source, "legacy-room-bundle"); + assert!(db.migration_record_exists("legacy-room-v17").unwrap()); } #[test] From 05397e1446f19e728737b5f94b8f8d96036c83f4 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 23 Jun 2026 00:16:46 +0800 Subject: [PATCH 14/71] feat(cli): add installed app autogen --- crates/getter-cli/src/lib.rs | 716 +++++++++++++++++- crates/getter-cli/tests/bdd_cli.rs | 372 +++++++++ .../features/cli/autogen_installed.feature | 92 +++ crates/getter-core/src/autogen.rs | 536 +++++++++++++ crates/getter-core/src/lib.rs | 1 + crates/getter-storage/src/lib.rs | 171 +++++ 6 files changed, 1882 insertions(+), 6 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/autogen_installed.feature create mode 100644 crates/getter-core/src/autogen.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index a15af83..72d92fc 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -4,9 +4,15 @@ //! while durable state is initialized through `getter-storage` so the command //! surface exercises the same Rust-owned SQLite direction used by embedders. +use getter_core::autogen::{ + content_hash, local_autogen_repo_toml, local_repo_toml, plan_local_autogen, AutogenManifest, + AutogenManifestEntry, AutogenPlan, AutogenSkipReason, InstalledInventory, + LOCAL_AUTOGEN_REPOSITORY_ID, LOCAL_AUTOGEN_REPOSITORY_NAME, LOCAL_REPOSITORY_ID, + LOCAL_REPOSITORY_NAME, +}; use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; -use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; +use getter_core::repository::{RepositoryLayout, RepositoryMetadata, REPO_API_VERSION_V1}; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; use getter_storage::legacy_room::{ map_legacy_app, read_legacy_room_database, LegacyAppKind, LegacyAppRecord, @@ -19,13 +25,15 @@ use getter_storage::{ }; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; +use std::collections::{BTreeSet, HashMap}; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::{Component, Path, PathBuf}; const MAIN_DB_FILE: &str = "main.db"; const CACHE_DB_FILE: &str = "cache.db"; const MIGRATION_REPORTS_DIR: &str = "migration-reports"; const LEGACY_ROOM_MIGRATION_ID: &str = "legacy-room-v17"; +const AUTOGEN_MANIFEST_FILE: &str = "autogen-manifest.json"; #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliInvocation { @@ -55,6 +63,20 @@ pub enum CliCommand { repo_id: Option, }, StorageValidate, + AutogenInstalledPreview { + inventory: PathBuf, + }, + AutogenInstalledApply { + preview: PathBuf, + acceptance: AutogenAcceptance, + }, + AutogenCleanupPreview { + inventory: PathBuf, + }, + AutogenCleanupApply { + preview: PathBuf, + acceptance: AutogenAcceptance, + }, LegacyImportRoomBundle { bundle: PathBuf, }, @@ -64,6 +86,12 @@ pub enum CliCommand { LegacyReportList, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AutogenAcceptance { + AcceptAll, + Accept(Vec), +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ExitCode { Success = 0, @@ -89,6 +117,8 @@ pub enum CliError { Repository(String), #[error("package evaluation error: {0}")] PackageEval(String), + #[error("autogen error: {0}")] + Autogen(String), #[error("Legacy Room export bundle is invalid")] InvalidLegacyBundle { report_path: PathBuf }, #[error("Legacy Room bundle import is not implemented yet")] @@ -104,7 +134,9 @@ impl CliError { match self { Self::Usage(_) => ExitCode::Usage, Self::Storage(_) => ExitCode::Storage, - Self::Repository(_) | Self::PackageEval(_) => ExitCode::GenericFailure, + Self::Repository(_) | Self::PackageEval(_) | Self::Autogen(_) => { + ExitCode::GenericFailure + } Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } | Self::InvalidLegacyDb { .. } @@ -118,6 +150,7 @@ impl CliError { Self::Storage(_) => "storage.error", Self::Repository(_) => "repository.error", Self::PackageEval(_) => "package.eval_error", + Self::Autogen(_) => "autogen.error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", Self::InvalidLegacyDb { .. } => "migration.invalid_db", @@ -131,6 +164,7 @@ impl CliError { Self::Storage(_) => "Getter storage operation failed", Self::Repository(_) => "Getter repository operation failed", Self::PackageEval(_) => "Getter package evaluation failed", + Self::Autogen(_) => "Getter autogen operation failed", Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", Self::UnsupportedLegacyBundle { .. } => { "Legacy Room bundle import is not implemented yet" @@ -145,7 +179,8 @@ impl CliError { Self::Usage(detail) | Self::Storage(detail) | Self::Repository(detail) - | Self::PackageEval(detail) => Some(detail.as_str()), + | Self::PackageEval(detail) + | Self::Autogen(detail) => Some(detail.as_str()), Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } | Self::InvalidLegacyDb { .. } @@ -159,7 +194,11 @@ impl CliError { | Self::UnsupportedLegacyBundle { report_path } | Self::InvalidLegacyDb { report_path } | Self::UnsupportedLegacyDb { report_path } => Some(report_path.as_path()), - Self::Usage(_) | Self::Storage(_) | Self::Repository(_) | Self::PackageEval(_) => None, + Self::Usage(_) + | Self::Storage(_) + | Self::Repository(_) + | Self::PackageEval(_) + | Self::Autogen(_) => None, } } } @@ -287,6 +326,48 @@ where [domain, command] if domain == "storage" && command == "validate" => { CliCommand::StorageValidate } + [domain, subject, action, flag, inventory] + if domain == "autogen" + && subject == "installed" + && action == "preview" + && flag == "--inventory" => + { + CliCommand::AutogenInstalledPreview { + inventory: PathBuf::from(inventory), + } + } + [domain, subject, action, flag, preview, rest @ ..] + if domain == "autogen" + && subject == "installed" + && action == "apply" + && flag == "--preview" => + { + CliCommand::AutogenInstalledApply { + preview: PathBuf::from(preview), + acceptance: parse_autogen_acceptance(rest)?, + } + } + [domain, subject, action, flag, inventory] + if domain == "autogen" + && subject == "cleanup" + && action == "preview" + && flag == "--inventory" => + { + CliCommand::AutogenCleanupPreview { + inventory: PathBuf::from(inventory), + } + } + [domain, subject, action, flag, preview, rest @ ..] + if domain == "autogen" + && subject == "cleanup" + && action == "apply" + && flag == "--preview" => + { + CliCommand::AutogenCleanupApply { + preview: PathBuf::from(preview), + acceptance: parse_autogen_acceptance(rest)?, + } + } [domain, command, bundle] if domain == "legacy" && command == "import-room-bundle" => { CliCommand::LegacyImportRoomBundle { bundle: PathBuf::from(bundle), @@ -390,6 +471,33 @@ fn execute(invocation: CliInvocation) -> Result { "cache_db": cache_db_path(&invocation.data_dir), })) } + CliCommand::AutogenInstalledPreview { inventory } => { + let db = open_main_db(&invocation.data_dir)?; + let inventory = read_installed_inventory(&inventory)?; + let plan = build_local_autogen_plan(&db, &inventory)?; + Ok(autogen_installed_preview_json(&invocation.data_dir, &plan)) + } + CliCommand::AutogenInstalledApply { + preview, + acceptance, + } => { + let db = open_main_db(&invocation.data_dir)?; + let preview = read_autogen_preview(&preview, "installed.preview")?; + apply_autogen_installed_preview(&invocation.data_dir, &db, &preview, &acceptance) + } + CliCommand::AutogenCleanupPreview { inventory } => { + let db = open_main_db(&invocation.data_dir)?; + let inventory = read_installed_inventory(&inventory)?; + cleanup_preview_json(&invocation.data_dir, &db, &inventory) + } + CliCommand::AutogenCleanupApply { + preview, + acceptance, + } => { + let db = open_main_db(&invocation.data_dir)?; + let preview = read_autogen_preview(&preview, "cleanup.preview")?; + apply_autogen_cleanup_preview(&invocation.data_dir, &db, &preview, &acceptance) + } CliCommand::LegacyImportRoomBundle { bundle } => { let db = open_main_db(&invocation.data_dir)?; if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { @@ -558,6 +666,33 @@ fn parse_priority(value: &str) -> Result { .map_err(|source| CliError::Usage(format!("invalid repository priority: {source}"))) } +fn parse_autogen_acceptance(args: &[String]) -> Result { + match args { + [flag] if flag == "--accept-all" => Ok(AutogenAcceptance::AcceptAll), + rest if !rest.is_empty() => { + let mut ids = Vec::new(); + let mut index = 0; + while index < rest.len() { + if rest[index] != "--accept" { + return Err(CliError::Usage( + "autogen apply requires --accept-all or repeated --accept " + .to_owned(), + )); + } + let package_id = rest + .get(index + 1) + .ok_or_else(|| CliError::Usage("--accept requires a package id".to_owned()))?; + ids.push(parse_package_id(package_id)?); + index += 2; + } + Ok(AutogenAcceptance::Accept(ids)) + } + _ => Err(CliError::Usage( + "autogen apply requires --accept-all or --accept ".to_owned(), + )), + } +} + fn load_repository_layout(path: &Path) -> Result { RepositoryLayout::load(path).map_err(|source| CliError::Repository(source.to_string())) } @@ -612,6 +747,571 @@ fn evaluate_highest_priority_package( ))) } +fn read_installed_inventory(path: &Path) -> Result { + let bytes = fs::read(path) + .map_err(|source| CliError::Autogen(format!("failed to read inventory: {source}")))?; + serde_json::from_slice(&bytes) + .map_err(|source| CliError::Autogen(format!("failed to parse inventory JSON: {source}"))) +} + +fn build_local_autogen_plan( + db: &MainDb, + inventory: &InstalledInventory, +) -> Result { + let covered = higher_priority_package_coverage(db)?; + plan_local_autogen(inventory, &covered).map_err(|source| CliError::Autogen(source.to_string())) +} + +fn higher_priority_package_coverage( + db: &MainDb, +) -> Result, CliError> { + let mut covered = HashMap::new(); + for repo in db.repositories()? { + if repo.id.as_str() == LOCAL_AUTOGEN_REPOSITORY_ID { + continue; + } + if repo.priority <= RepositoryPriority::LOCAL_AUTOGEN { + continue; + } + let Some(path) = repo.path.as_ref() else { + continue; + }; + let layout = load_repository_layout(Path::new(path))?; + for package in layout.packages { + covered.entry(package.id).or_insert_with(|| repo.id.clone()); + } + } + Ok(covered) +} + +fn autogen_installed_preview_json(data_dir: &Path, plan: &AutogenPlan) -> Value { + let candidates: Vec = plan.candidates.iter().map(autogen_candidate_json).collect(); + let skipped: Vec = plan.skipped.iter().map(autogen_skip_json).collect(); + json!({ + "operation": "installed.preview", + "target_repo_id": plan.repository_id.as_str(), + "target_repo_path": local_autogen_repo_path(data_dir), + "summary": { + "candidate_count": candidates.len(), + "skipped_count": skipped.len(), + "write_count": candidates.len(), + "delete_count": 0, + }, + "candidates": candidates, + "skipped": skipped, + "diagnostics": [], + }) +} + +fn autogen_candidate_json(candidate: &getter_core::autogen::AutogenCandidate) -> Value { + json!({ + "package_id": candidate.package_id.to_string(), + "kind": candidate.package_id.kind().as_str(), + "display_name": candidate.name, + "installed_target": candidate.installed, + "action": "create", + "output_relative_path": candidate.relative_path, + "content_hash": candidate.content_hash, + "content": candidate.content, + }) +} + +fn autogen_skip_json(skip: &getter_core::autogen::AutogenSkip) -> Value { + json!({ + "package_id": skip.package_id.to_string(), + "reason": match skip.reason { + AutogenSkipReason::DuplicateInventoryItem => "duplicate_inventory_item", + AutogenSkipReason::CoveredByHigherPriorityRepository => "covered_by_higher_priority_repo", + }, + "covering_repo_id": skip.repository_id.as_ref().map(RepositoryId::as_str), + }) +} + +fn read_autogen_preview(path: &Path, expected_operation: &str) -> Result { + let bytes = fs::read(path) + .map_err(|source| CliError::Autogen(format!("failed to read autogen preview: {source}")))?; + let raw: Value = serde_json::from_slice(&bytes).map_err(|source| { + CliError::Autogen(format!("failed to parse autogen preview JSON: {source}")) + })?; + let payload = if raw.get("ok").is_some() && raw.get("data").is_some() { + raw.get("data").cloned().unwrap_or(Value::Null) + } else { + raw + }; + if payload.get("operation").and_then(Value::as_str) != Some(expected_operation) { + return Err(CliError::Autogen(format!( + "autogen preview operation must be '{expected_operation}'" + ))); + } + Ok(payload) +} + +fn apply_autogen_installed_preview( + data_dir: &Path, + db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, +) -> Result { + let repo_path = local_autogen_repo_path(data_dir); + ensure_local_autogen_repository(data_dir, db)?; + let accepted = accepted_preview_candidates(preview, acceptance)?; + let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); + let mut applied = Vec::new(); + let mut preserved = Vec::new(); + + for candidate in accepted { + let package_id = preview_package_id(candidate)?; + let relative_path = preview_relative_path(candidate)?; + let content = candidate + .get("content") + .and_then(Value::as_str) + .ok_or_else(|| CliError::Autogen("preview candidate missing content".to_owned()))?; + let expected_hash = candidate + .get("content_hash") + .and_then(Value::as_str) + .ok_or_else(|| { + CliError::Autogen("preview candidate missing content_hash".to_owned()) + })?; + if content_hash(content) != expected_hash { + return Err(CliError::Autogen(format!( + "preview content hash mismatch for {package_id}" + ))); + } + let target = safe_join(&repo_path, &relative_path)?; + if target.exists() { + let current = fs::read_to_string(&target).map_err(|source| { + CliError::Autogen(format!( + "failed to read existing autogen file '{}': {source}", + target.display() + )) + })?; + let current_hash = content_hash(¤t); + let known_hash = manifest + .package(&package_id) + .map(|entry| entry.content_hash.as_str()); + if known_hash != Some(current_hash.as_str()) { + preserved.push(preserve_autogen_file_in_local( + data_dir, + db, + &package_id, + ¤t, + )?); + } + } + if let Some(parent) = target.parent() { + fs::create_dir_all(parent).map_err(|source| { + CliError::Autogen(format!( + "failed to create autogen package directory '{}': {source}", + parent.display() + )) + })?; + } + fs::write(&target, content).map_err(|source| { + CliError::Autogen(format!( + "failed to write autogen package '{}': {source}", + target.display() + )) + })?; + upsert_manifest_entry( + &mut manifest, + AutogenManifestEntry { + package_id: package_id.clone(), + relative_path: relative_path.clone(), + content_hash: expected_hash.to_owned(), + }, + ); + db.upsert_generated_tracked_package_preserving_user_state( + &package_id, + &RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + )?; + applied.push(json!({ + "package_id": package_id.to_string(), + "output_relative_path": relative_path, + })); + } + + write_autogen_manifest(&repo_path, &manifest)?; + Ok(json!({ + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "applied_count": applied.len(), + "applied": applied, + "preserved_to_local": preserved, + })) +} + +fn cleanup_preview_json( + data_dir: &Path, + db: &MainDb, + inventory: &InstalledInventory, +) -> Result { + let repo_path = local_autogen_repo_path(data_dir); + let Some(manifest) = read_autogen_manifest(&repo_path)? else { + return Ok(json!({ + "operation": "cleanup.preview", + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "summary": { "candidate_count": 0, "skipped_count": 0, "write_count": 0, "delete_count": 0 }, + "candidates": [], + "skipped": [], + "diagnostics": [], + })); + }; + let plan = build_local_autogen_plan(db, inventory)?; + let installed_ids: BTreeSet = plan + .candidates + .iter() + .map(|candidate| candidate.package_id.to_string()) + .chain(plan.skipped.iter().map(|skip| skip.package_id.to_string())) + .collect(); + let mut candidates = Vec::new(); + for entry in manifest.packages { + if !installed_ids.contains(&entry.package_id.to_string()) { + candidates.push(json!({ + "package_id": entry.package_id.to_string(), + "action": "delete", + "output_relative_path": entry.relative_path, + "content_hash": entry.content_hash, + "reason": "not_in_installed_inventory", + })); + } + } + candidates.sort_by_key(|candidate| { + candidate + .get("package_id") + .and_then(Value::as_str) + .unwrap_or_default() + .to_owned() + }); + Ok(json!({ + "operation": "cleanup.preview", + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "summary": { + "candidate_count": candidates.len(), + "skipped_count": 0, + "write_count": 0, + "delete_count": candidates.len(), + }, + "candidates": candidates, + "skipped": [], + "diagnostics": [], + })) +} + +fn apply_autogen_cleanup_preview( + data_dir: &Path, + db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, +) -> Result { + if preview.get("target_repo_id").and_then(Value::as_str) != Some(LOCAL_AUTOGEN_REPOSITORY_ID) { + return Err(CliError::Autogen(format!( + "cleanup preview target_repo_id must be '{LOCAL_AUTOGEN_REPOSITORY_ID}'" + ))); + } + let repo_path = local_autogen_repo_path(data_dir); + let accepted = accepted_preview_candidates(preview, acceptance)?; + let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); + let mut deleted = Vec::new(); + let mut preserved = Vec::new(); + let local_autogen_id = RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"); + for candidate in accepted { + let package_id = preview_package_id(candidate)?; + let relative_path = preview_relative_path(candidate)?; + let expected_hash = candidate + .get("content_hash") + .and_then(Value::as_str) + .ok_or_else(|| { + CliError::Autogen("cleanup preview candidate missing content_hash".to_owned()) + })?; + let manifest_entry = manifest.package(&package_id).ok_or_else(|| { + CliError::Autogen(format!( + "cleanup preview candidate {package_id} is not managed by local_autogen manifest" + )) + })?; + if manifest_entry.relative_path != relative_path + || manifest_entry.content_hash != expected_hash + { + return Err(CliError::Autogen(format!( + "cleanup preview candidate {package_id} does not match local_autogen manifest" + ))); + } + let target = safe_join(&repo_path, &relative_path)?; + if target.exists() { + let current = fs::read_to_string(&target).map_err(|source| { + CliError::Autogen(format!( + "failed to read existing autogen file '{}': {source}", + target.display() + )) + })?; + if content_hash(¤t) != expected_hash { + preserved.push(preserve_autogen_file_in_local( + data_dir, + db, + &package_id, + ¤t, + )?); + } + fs::remove_file(&target).map_err(|source| { + CliError::Autogen(format!( + "failed to delete autogen package '{}': {source}", + target.display() + )) + })?; + } + manifest + .packages + .retain(|entry| entry.package_id != package_id); + db.delete_generated_tracked_package(&package_id, &local_autogen_id)?; + deleted.push(json!({ + "package_id": package_id.to_string(), + "output_relative_path": relative_path, + })); + } + write_autogen_manifest(&repo_path, &manifest)?; + Ok(json!({ + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "deleted_count": deleted.len(), + "deleted": deleted, + "preserved_to_local": preserved, + })) +} + +fn accepted_preview_candidates<'a>( + preview: &'a Value, + acceptance: &AutogenAcceptance, +) -> Result, CliError> { + let candidates = preview + .get("candidates") + .and_then(Value::as_array) + .ok_or_else(|| CliError::Autogen("autogen preview missing candidates".to_owned()))?; + match acceptance { + AutogenAcceptance::AcceptAll => Ok(candidates.iter().collect()), + AutogenAcceptance::Accept(ids) => { + let accepted: BTreeSet = ids.iter().map(ToString::to_string).collect(); + Ok(candidates + .iter() + .filter(|candidate| { + candidate + .get("package_id") + .and_then(Value::as_str) + .is_some_and(|id| accepted.contains(id)) + }) + .collect()) + } + } +} + +fn preview_package_id(candidate: &Value) -> Result { + candidate + .get("package_id") + .and_then(Value::as_str) + .ok_or_else(|| CliError::Autogen("preview candidate missing package_id".to_owned()))? + .parse() + .map_err(|source: getter_core::PackageIdError| CliError::Autogen(source.to_string())) +} + +fn preview_relative_path(candidate: &Value) -> Result { + candidate + .get("output_relative_path") + .and_then(Value::as_str) + .map(PathBuf::from) + .ok_or_else(|| { + CliError::Autogen("preview candidate missing output_relative_path".to_owned()) + }) +} + +fn ensure_local_autogen_repository(data_dir: &Path, db: &MainDb) -> Result { + let repo_path = local_autogen_repo_path(data_dir); + ensure_repository_layout(&repo_path, &local_autogen_repo_toml())?; + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + name: LOCAL_AUTOGEN_REPOSITORY_NAME.to_owned(), + priority: RepositoryPriority::LOCAL_AUTOGEN, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + )?; + Ok(repo_path) +} + +fn ensure_local_repository(data_dir: &Path, db: &MainDb) -> Result { + let local_id = RepositoryId::new(LOCAL_REPOSITORY_ID).expect("valid id"); + if let Ok(existing) = find_repository(db, &local_id) { + let repo_path = repo_path(&existing)?; + ensure_repository_layout(&repo_path, &local_repo_toml())?; + return Ok(repo_path); + } + + let repo_path = data_dir.join("repositories").join(LOCAL_REPOSITORY_ID); + ensure_repository_layout(&repo_path, &local_repo_toml())?; + db.upsert_repository( + &RepositoryMetadata { + id: local_id, + name: LOCAL_REPOSITORY_NAME.to_owned(), + priority: RepositoryPriority::LOCAL, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + )?; + Ok(repo_path) +} + +fn ensure_repository_layout(repo_path: &Path, repo_toml: &str) -> Result<(), CliError> { + fs::create_dir_all(repo_path.join("packages")).map_err(|source| { + CliError::Autogen(format!( + "failed to create repository packages dir '{}': {source}", + repo_path.display() + )) + })?; + fs::create_dir_all(repo_path.join("lib")).map_err(|source| { + CliError::Autogen(format!( + "failed to create repository lib dir '{}': {source}", + repo_path.display() + )) + })?; + fs::create_dir_all(repo_path.join("templates")).map_err(|source| { + CliError::Autogen(format!( + "failed to create repository templates dir '{}': {source}", + repo_path.display() + )) + })?; + let repo_toml_path = repo_path.join("repo.toml"); + if !repo_toml_path.exists() { + fs::write(&repo_toml_path, repo_toml).map_err(|source| { + CliError::Autogen(format!( + "failed to write repo.toml '{}': {source}", + repo_toml_path.display() + )) + })?; + } + Ok(()) +} + +fn preserve_autogen_file_in_local( + data_dir: &Path, + db: &MainDb, + package_id: &PackageId, + content: &str, +) -> Result { + let local_repo = ensure_local_repository(data_dir, db)?; + let primary_relative = getter_core::autogen::package_relative_path(package_id); + let primary_target = safe_join(&local_repo, &primary_relative)?; + let relative_path = if primary_target.exists() { + let backup = PathBuf::from("autogen-preserved") + .join(package_id.kind().as_str()) + .join(format!( + "{}.{}.lua", + package_id.name(), + content_hash(content).replace(':', "-") + )); + safe_join(&local_repo, &backup)?; + backup + } else { + primary_relative + }; + let target = safe_join(&local_repo, &relative_path)?; + if let Some(parent) = target.parent() { + fs::create_dir_all(parent).map_err(|source| { + CliError::Autogen(format!( + "failed to create local preservation directory '{}': {source}", + parent.display() + )) + })?; + } + fs::write(&target, content).map_err(|source| { + CliError::Autogen(format!( + "failed to preserve modified autogen file '{}': {source}", + target.display() + )) + })?; + Ok(json!({ + "package_id": package_id.to_string(), + "repository_id": LOCAL_REPOSITORY_ID, + "relative_path": relative_path, + })) +} + +fn read_autogen_manifest(repo_path: &Path) -> Result, CliError> { + let path = repo_path.join(AUTOGEN_MANIFEST_FILE); + if !path.exists() { + return Ok(None); + } + let bytes = fs::read(&path).map_err(|source| { + CliError::Autogen(format!( + "failed to read autogen manifest '{}': {source}", + path.display() + )) + })?; + serde_json::from_slice(&bytes) + .map(Some) + .map_err(|source| CliError::Autogen(format!("failed to parse autogen manifest: {source}"))) +} + +fn write_autogen_manifest(repo_path: &Path, manifest: &AutogenManifest) -> Result<(), CliError> { + fs::create_dir_all(repo_path).map_err(|source| { + CliError::Autogen(format!( + "failed to create autogen repository '{}': {source}", + repo_path.display() + )) + })?; + let path = repo_path.join(AUTOGEN_MANIFEST_FILE); + let bytes = serde_json::to_vec_pretty(manifest) + .map_err(|source| CliError::Autogen(format!("failed to serialize manifest: {source}")))?; + fs::write(&path, bytes).map_err(|source| { + CliError::Autogen(format!( + "failed to write autogen manifest '{}': {source}", + path.display() + )) + }) +} + +fn empty_autogen_manifest() -> AutogenManifest { + AutogenManifest { + version: getter_core::autogen::AUTOGEN_MANIFEST_VERSION, + repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + packages: Vec::new(), + } +} + +fn upsert_manifest_entry(manifest: &mut AutogenManifest, entry: AutogenManifestEntry) { + manifest + .packages + .retain(|existing| existing.package_id != entry.package_id); + manifest.packages.push(entry); + manifest + .packages + .sort_by_key(|existing| existing.package_id.to_string()); +} + +fn safe_join(root: &Path, relative: &Path) -> Result { + if relative.is_absolute() + || relative.components().any(|component| { + matches!( + component, + Component::ParentDir | Component::RootDir | Component::Prefix(_) + ) + }) + { + return Err(CliError::Autogen(format!( + "unsafe relative path '{}'", + relative.display() + ))); + } + Ok(root.join(relative)) +} + +fn local_autogen_repo_path(data_dir: &Path) -> PathBuf { + data_dir + .join("repositories") + .join(LOCAL_AUTOGEN_REPOSITORY_ID) +} + fn repo_path(repo: &StoredRepository) -> Result { repo.path .as_ref() @@ -984,7 +1684,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1037,6 +1737,10 @@ impl CliCommand { Self::RepoValidate { .. } => "repo validate", Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", + Self::AutogenInstalledPreview { .. } => "autogen installed preview", + Self::AutogenInstalledApply { .. } => "autogen installed apply", + Self::AutogenCleanupPreview { .. } => "autogen cleanup preview", + Self::AutogenCleanupApply { .. } => "autogen cleanup apply", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", Self::LegacyImportRoomDb { .. } => "legacy import-room-db", Self::LegacyReportList => "legacy report-list", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 5d07d58..dfe31e2 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -12,6 +12,8 @@ struct CliWorld { data_dir: Option, bundle: Option, legacy_db: Option, + inventory: Option, + autogen_preview: Option, fixture_repo_id: Option, fixture_repo_path: Option, fixture_package_id: Option, @@ -109,6 +111,82 @@ fn legacy_room_v17_database_with_only_unsupported_app_rows(world: &mut CliWorld) world.legacy_db = Some(legacy_db); } +#[given(expr = "an installed inventory with Android app {string} labeled {string}")] +fn installed_inventory_with_android_app(world: &mut CliWorld, package_name: String, label: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let inventory = temp.path().join("installed-inventory.json"); + fs::write( + &inventory, + serde_json::to_vec_pretty(&serde_json::json!({ + "format": "upgradeall-installed-inventory", + "version": 1, + "items": [ + { + "kind": "android", + "package_name": package_name, + "label": label, + "version_name": "1.0.0", + "version_code": 1, + } + ] + })) + .expect("inventory serializes"), + ) + .expect("write inventory"); + world.inventory = Some(inventory); +} + +#[given("an empty installed inventory")] +fn empty_installed_inventory(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let inventory = temp.path().join("installed-inventory.json"); + fs::write( + &inventory, + serde_json::to_vec_pretty(&serde_json::json!({ + "format": "upgradeall-installed-inventory", + "version": 1, + "items": [] + })) + .expect("inventory serializes"), + ) + .expect("write inventory"); + world.inventory = Some(inventory); +} + +#[given(expr = "a tampered autogen cleanup preview for package {string}")] +fn tampered_autogen_cleanup_preview_for_package(world: &mut CliWorld, package_id: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let preview = temp.path().join("tampered-cleanup-preview.json"); + fs::write( + &preview, + serde_json::to_vec_pretty(&serde_json::json!({ + "operation": "cleanup.preview", + "target_repo_id": "local_autogen", + "target_repo_path": local_autogen_repo_path(world), + "summary": { + "candidate_count": 1, + "skipped_count": 0, + "write_count": 0, + "delete_count": 1, + }, + "candidates": [ + { + "package_id": package_id, + "action": "delete", + "output_relative_path": package_relative_path(&package_id), + "content_hash": "fnv1a64:0000000000000000", + "reason": "not_in_installed_inventory", + } + ], + "skipped": [], + "diagnostics": [], + })) + .expect("preview serializes"), + ) + .expect("write tampered cleanup preview"); + world.autogen_preview = Some(preview); +} + #[given(expr = "a fixture Lua repository {string} with package {string}")] fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); @@ -329,6 +407,113 @@ fn run_getter_legacy_report_list(world: &mut CliWorld) { world.json = None; } +#[when("I run getter autogen installed preview for that inventory")] +fn run_getter_autogen_installed_preview(world: &mut CliWorld) { + let inventory = world.inventory.as_ref().expect("inventory exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "installed".to_owned(), + "preview".to_owned(), + "--inventory".to_owned(), + inventory.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter autogen installed apply for that preview with accept-all")] +fn run_getter_autogen_installed_apply_accept_all(world: &mut CliWorld) { + let preview = world + .autogen_preview + .as_ref() + .expect("autogen preview exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "installed".to_owned(), + "apply".to_owned(), + "--preview".to_owned(), + preview.to_string_lossy().to_string(), + "--accept-all".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter autogen cleanup preview for that inventory")] +fn run_getter_autogen_cleanup_preview(world: &mut CliWorld) { + let inventory = world.inventory.as_ref().expect("inventory exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "cleanup".to_owned(), + "preview".to_owned(), + "--inventory".to_owned(), + inventory.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter autogen cleanup apply for that preview with accept-all")] +fn run_getter_autogen_cleanup_apply_accept_all(world: &mut CliWorld) { + let preview = world + .autogen_preview + .as_ref() + .expect("autogen preview exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "cleanup".to_owned(), + "apply".to_owned(), + "--preview".to_owned(), + preview.to_string_lossy().to_string(), + "--accept-all".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter repo validate for local_autogen")] +fn run_getter_repo_validate_for_local_autogen(world: &mut CliWorld) { + let repo_path = local_autogen_repo_path(world); + let output = run_getter( + world, + [ + "repo".to_owned(), + "validate".to_owned(), + repo_path.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when(expr = "I run getter package eval for package {string} from local_autogen")] +fn run_getter_package_eval_from_local_autogen(world: &mut CliWorld, package_id: String) { + let output = run_getter( + world, + [ + "package".to_owned(), + "eval".to_owned(), + package_id, + "--repo".to_owned(), + "local_autogen".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[then("the command succeeds")] fn command_succeeds(world: &mut CliWorld) { let output = world.output.as_ref().expect("command output exists"); @@ -352,6 +537,17 @@ fn command_fails_with_migration_error(world: &mut CliWorld) { world.json = Some(json); } +#[then("the command fails with an autogen error")] +fn command_fails_with_autogen_error(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(1)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["command"], "autogen cleanup apply"); + assert_eq!(json["error"]["code"], "autogen.error"); + world.json = Some(json); +} + #[then(expr = "the command fails with direct DB migration error {string}")] fn command_fails_with_direct_db_migration_error(world: &mut CliWorld, code: String) { let output = world.output.as_ref().expect("command output exists"); @@ -515,6 +711,164 @@ fn output_contains_named_package(world: &mut CliWorld, package_id: String, packa assert_eq!(json["data"]["package"]["name"], package_name); } +#[then(expr = "the autogen preview contains candidate {string}")] +fn autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "autogen installed preview"); + assert_eq!(json["data"]["operation"], "installed.preview"); + let candidates = json["data"]["candidates"] + .as_array() + .expect("candidates array"); + assert!( + candidates + .iter() + .any(|candidate| candidate["package_id"].as_str() == Some(package_id.as_str())), + "preview should contain {package_id}: {candidates:?}" + ); +} + +#[then("the local_autogen repository has not been written")] +fn local_autogen_repository_has_not_been_written(world: &mut CliWorld) { + assert!( + !local_autogen_repo_path(world).exists(), + "preview must not create local_autogen" + ); +} + +#[then("I save the autogen preview to a file")] +fn save_autogen_preview_to_file(world: &mut CliWorld) { + let data = current_json(world) + .get("data") + .expect("autogen command data exists") + .clone(); + let temp = world.temp.as_ref().expect("tempdir exists"); + let preview = temp.path().join("autogen-preview.json"); + fs::write( + &preview, + serde_json::to_vec_pretty(&data).expect("preview serializes"), + ) + .expect("write autogen preview"); + world.autogen_preview = Some(preview); +} + +#[then(expr = "the local_autogen repository contains generated package {string}")] +fn local_autogen_repository_contains_generated_package(world: &mut CliWorld, package_id: String) { + let path = local_autogen_repo_path(world).join(package_relative_path(&package_id)); + assert!(path.is_file(), "generated package should exist: {path:?}"); + let content = fs::read_to_string(&path).expect("generated package readable"); + assert!(content.contains("@generated by UpgradeAll getter local_autogen")); +} + +#[then(expr = "the app list contains autogen tracked package {string}")] +fn app_list_contains_autogen_tracked_package(world: &mut CliWorld, package_id: String) { + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + assert_success(&output); + let json = parse_stdout(&output); + let apps = json["data"]["apps"].as_array().expect("apps array"); + let app = apps + .iter() + .find(|app| app["id"].as_str() == Some(package_id.as_str())) + .unwrap_or_else(|| panic!("app list should contain {package_id}: {apps:?}")); + assert_eq!(app["repository_id"], "local_autogen"); + assert_eq!(app["package_resolution"], "generate_local_package"); +} + +#[then(expr = "the autogen preview skips package {string} because repository {string} covers it")] +fn autogen_preview_skips_package_because_repository_covers_it( + world: &mut CliWorld, + package_id: String, + repository_id: String, +) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "autogen installed preview"); + let skipped = json["data"]["skipped"].as_array().expect("skipped array"); + let skip = skipped + .iter() + .find(|skip| skip["package_id"].as_str() == Some(package_id.as_str())) + .unwrap_or_else(|| panic!("skipped should contain {package_id}: {skipped:?}")); + assert_eq!(skip["reason"], "covered_by_higher_priority_repo"); + assert_eq!(skip["covering_repo_id"], repository_id); +} + +#[then(expr = "the autogen cleanup preview contains delete candidate {string}")] +fn autogen_cleanup_preview_contains_delete_candidate(world: &mut CliWorld, package_id: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "autogen cleanup preview"); + assert_eq!(json["data"]["operation"], "cleanup.preview"); + let candidates = json["data"]["candidates"] + .as_array() + .expect("candidates array"); + let candidate = candidates + .iter() + .find(|candidate| candidate["package_id"].as_str() == Some(package_id.as_str())) + .unwrap_or_else(|| { + panic!("cleanup candidates should contain {package_id}: {candidates:?}") + }); + assert_eq!(candidate["action"], "delete"); +} + +#[then(expr = "the local_autogen repository does not contain generated package {string}")] +fn local_autogen_repository_does_not_contain_generated_package( + world: &mut CliWorld, + package_id: String, +) { + let path = local_autogen_repo_path(world).join(package_relative_path(&package_id)); + assert!( + !path.exists(), + "generated package should be deleted: {path:?}" + ); +} + +#[then(expr = "the app list does not contain package {string}")] +fn app_list_does_not_contain_package(world: &mut CliWorld, package_id: String) { + let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); + assert_success(&output); + let json = parse_stdout(&output); + let apps = json["data"]["apps"].as_array().expect("apps array"); + assert!( + apps.iter() + .all(|app| app["id"].as_str() != Some(package_id.as_str())), + "app list must not contain {package_id}: {apps:?}" + ); +} + +#[then(expr = "I replace generated autogen package {string} with user-edited content")] +fn replace_generated_autogen_package_with_user_edited_content( + world: &mut CliWorld, + package_id: String, +) { + let path = local_autogen_repo_path(world).join(package_relative_path(&package_id)); + assert!(path.is_file(), "generated package should exist before edit"); + fs::write( + &path, + format!( + "-- user edited\nreturn package_def {{ id = {id:?}, name = \"Edited Autogen\" }}\n", + id = package_id + ), + ) + .expect("overwrite generated package with user edit"); +} + +#[then(expr = "local repository contains preserved package {string}")] +fn local_repository_contains_preserved_package(world: &mut CliWorld, package_id: String) { + let local_path = world + .data_dir + .as_ref() + .expect("data dir exists") + .join("repositories") + .join("local") + .join(package_relative_path(&package_id)); + assert!( + local_path.is_file(), + "modified autogen file should be preserved into local: {local_path:?}" + ); + let content = fs::read_to_string(local_path).expect("preserved local file readable"); + assert!(content.contains("Edited Autogen")); +} + #[then("no partially usable imported state is created")] fn no_partially_usable_imported_state(world: &mut CliWorld) { let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); @@ -685,6 +1039,24 @@ fn current_json(world: &mut CliWorld) -> &Value { .get_or_insert_with(|| parse_stdout(world.output.as_ref().expect("command output exists"))) } +fn local_autogen_repo_path(world: &CliWorld) -> PathBuf { + world + .data_dir + .as_ref() + .expect("data dir exists") + .join("repositories") + .join("local_autogen") +} + +fn package_relative_path(package_id: &str) -> PathBuf { + let (kind, name) = package_id + .split_once('/') + .expect("test package id has kind/name"); + PathBuf::from("packages") + .join(kind) + .join(format!("{name}.lua")) +} + fn create_fixture_lua_repository( world: &mut CliWorld, repo_id: String, diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature new file mode 100644 index 0000000..fc3c16a --- /dev/null +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -0,0 +1,92 @@ +@getter-cli @autogen +Feature: Installed app autogen + Scenario: User previews installed app fallback generation without writing files + Given an initialized getter data directory + And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" + When I run getter autogen installed preview for that inventory + Then the command succeeds + And the output is valid JSON + And the autogen preview contains candidate "android/com.example.autogen" + And the local_autogen repository has not been written + + Scenario: User applies installed app autogen and evaluates the generated fallback package + Given an initialized getter data directory + And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" + When I run getter autogen installed preview for that inventory + Then the command succeeds + And I save the autogen preview to a file + When I run getter autogen installed apply for that preview with accept-all + Then the command succeeds + And the local_autogen repository contains generated package "android/com.example.autogen" + And the app list contains autogen tracked package "android/com.example.autogen" + When I run getter repo validate for local_autogen + Then the output reports a valid repository without network + When I run getter package eval for package "android/com.example.autogen" from local_autogen + Then the output contains package "android/com.example.autogen" named "Example Autogen" + + Scenario: Higher-priority repositories suppress installed app autogen candidates + Given an initialized getter data directory + And a fixture Lua repository "official" with package "android/com.example.autogen" named "Official Example" + And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" + When I run getter repo add for that repository with priority 0 + Then the command succeeds + When I run getter autogen installed preview for that inventory + Then the command succeeds + And the autogen preview skips package "android/com.example.autogen" because repository "official" covers it + + Scenario: Cleanup deletes only accepted generated packages missing from installed inventory + Given an initialized getter data directory + And an installed inventory with Android app "com.example.old" labeled "Old App" + When I run getter autogen installed preview for that inventory + Then the command succeeds + And I save the autogen preview to a file + When I run getter autogen installed apply for that preview with accept-all + Then the command succeeds + Given an empty installed inventory + When I run getter autogen cleanup preview for that inventory + Then the command succeeds + And the autogen cleanup preview contains delete candidate "android/com.example.old" + And I save the autogen preview to a file + When I run getter autogen cleanup apply for that preview with accept-all + Then the command succeeds + And the local_autogen repository does not contain generated package "android/com.example.old" + And the app list does not contain package "android/com.example.old" + + Scenario: Cleanup rejects tampered previews for non-autogen tracked packages + Given an initialized getter data directory + And a syntactically valid legacy export bundle with an Android app + And a tampered autogen cleanup preview for package "android/org.fdroid.fdroid" + When I run getter legacy import-room-bundle for that bundle + Then the command succeeds + When I run getter autogen cleanup apply for that preview with accept-all + Then the command fails with an autogen error + And the app list contains imported package "android/org.fdroid.fdroid" + + Scenario: Applying autogen preserves existing tracked user state + Given an initialized getter data directory + And a syntactically valid legacy export bundle with an Android app + When I run getter legacy import-room-bundle for that bundle + Then the command succeeds + Given an installed inventory with Android app "org.fdroid.fdroid" labeled "F-Droid" + When I run getter autogen installed preview for that inventory + Then the command succeeds + And I save the autogen preview to a file + When I run getter autogen installed apply for that preview with accept-all + Then the command succeeds + And the app list contains imported package "android/org.fdroid.fdroid" + + Scenario: Applying over modified autogen preserves the user-edited file into local + Given an initialized getter data directory + And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" + When I run getter autogen installed preview for that inventory + Then the command succeeds + And I save the autogen preview to a file + When I run getter autogen installed apply for that preview with accept-all + Then the command succeeds + And I replace generated autogen package "android/com.example.autogen" with user-edited content + When I run getter autogen installed preview for that inventory + Then the command succeeds + And I save the autogen preview to a file + When I run getter autogen installed apply for that preview with accept-all + Then the command succeeds + And local repository contains preserved package "android/com.example.autogen" diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs new file mode 100644 index 0000000..265c6be --- /dev/null +++ b/crates/getter-core/src/autogen.rs @@ -0,0 +1,536 @@ +//! Installed-inventory autogen planning for generated fallback repositories. +//! +//! This module is pure domain logic: platform adapters provide installed +//! inventory facts, while getter decides package ids, generated Lua shape, +//! deterministic paths, and preview DTOs. + +use crate::repository::REPO_API_VERSION_V1; +use crate::{InstalledTarget, PackageId, PackageKind, RepositoryId, RepositoryPriority}; +use serde::{Deserialize, Serialize}; +use std::collections::{BTreeSet, HashMap}; +use std::path::PathBuf; + +pub const INSTALLED_INVENTORY_FORMAT: &str = "upgradeall-installed-inventory"; +pub const INSTALLED_INVENTORY_VERSION: u32 = 1; +pub const AUTOGEN_MANIFEST_VERSION: u32 = 1; +pub const LOCAL_REPOSITORY_ID: &str = "local"; +pub const LOCAL_REPOSITORY_NAME: &str = "Local"; +pub const LOCAL_AUTOGEN_REPOSITORY_ID: &str = "local_autogen"; +pub const LOCAL_AUTOGEN_REPOSITORY_NAME: &str = "UpgradeAll Local Autogen"; +pub const GENERATED_MARKER: &str = "@generated by UpgradeAll getter local_autogen"; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct InstalledInventory { + pub format: String, + pub version: u32, + #[serde(default)] + pub items: Vec, +} + +impl InstalledInventory { + pub fn new(items: Vec) -> Self { + Self { + format: INSTALLED_INVENTORY_FORMAT.to_owned(), + version: INSTALLED_INVENTORY_VERSION, + items, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum InstalledInventoryItem { + #[serde(alias = "android")] + AndroidPackage { + package_name: String, + #[serde(default)] + label: Option, + #[serde(default)] + version_name: Option, + #[serde(default)] + version_code: Option, + }, + #[serde(alias = "magisk")] + MagiskModule { + module_id: String, + #[serde(default)] + name: Option, + #[serde(default)] + version_name: Option, + #[serde(default)] + version_code: Option, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AutogenPlan { + pub repository_id: RepositoryId, + pub repository_name: String, + pub repository_priority: RepositoryPriority, + pub candidates: Vec, + pub skipped: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AutogenCandidate { + pub package_id: PackageId, + pub name: String, + pub installed: InstalledTarget, + pub relative_path: PathBuf, + pub content: String, + pub content_hash: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AutogenSkip { + pub package_id: PackageId, + pub reason: AutogenSkipReason, + #[serde(default)] + pub repository_id: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum AutogenSkipReason { + DuplicateInventoryItem, + CoveredByHigherPriorityRepository, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AutogenManifest { + pub version: u32, + pub repository_id: RepositoryId, + pub packages: Vec, +} + +impl AutogenManifest { + pub fn from_candidates(candidates: &[AutogenCandidate]) -> Self { + Self { + version: AUTOGEN_MANIFEST_VERSION, + repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID) + .expect("local_autogen is a valid repository id"), + packages: candidates + .iter() + .map(|candidate| AutogenManifestEntry { + package_id: candidate.package_id.clone(), + relative_path: candidate.relative_path.clone(), + content_hash: candidate.content_hash.clone(), + }) + .collect(), + } + } + + pub fn package(&self, package_id: &PackageId) -> Option<&AutogenManifestEntry> { + self.packages + .iter() + .find(|entry| &entry.package_id == package_id) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AutogenManifestEntry { + pub package_id: PackageId, + pub relative_path: PathBuf, + pub content_hash: String, +} + +#[derive(Debug, thiserror::Error)] +pub enum AutogenError { + #[error("unsupported installed inventory format '{0}'")] + UnsupportedInventoryFormat(String), + #[error("unsupported installed inventory version {found}; expected {expected}")] + UnsupportedInventoryVersion { found: u32, expected: u32 }, + #[error("installed inventory item has an empty identity")] + EmptyIdentity, + #[error("failed to construct package id from installed inventory: {0}")] + PackageId(#[from] crate::PackageIdError), +} + +/// Build a deterministic local_autogen preview from installed inventory. +/// +/// `covered_packages` contains package ids already supplied by repositories with +/// priority higher than `local_autogen`. Those packages are skipped so generated +/// fallback files do not compete with user-authored `local` or upstream package +/// files. +pub fn plan_local_autogen( + inventory: &InstalledInventory, + covered_packages: &HashMap, +) -> Result { + validate_installed_inventory(inventory)?; + let mut seen = BTreeSet::new(); + let mut candidates = Vec::new(); + let mut skipped = Vec::new(); + + for item in &inventory.items { + let candidate = candidate_from_inventory_item(item)?; + if !seen.insert(candidate.package_id.to_string()) { + skipped.push(AutogenSkip { + package_id: candidate.package_id, + reason: AutogenSkipReason::DuplicateInventoryItem, + repository_id: None, + }); + continue; + } + if let Some(repository_id) = covered_packages.get(&candidate.package_id) { + skipped.push(AutogenSkip { + package_id: candidate.package_id, + reason: AutogenSkipReason::CoveredByHigherPriorityRepository, + repository_id: Some(repository_id.clone()), + }); + continue; + } + candidates.push(candidate); + } + + candidates.sort_by_key(|candidate| candidate.package_id.to_string()); + skipped.sort_by_key(|skip| skip.package_id.to_string()); + + Ok(AutogenPlan { + repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID) + .expect("local_autogen is a valid repository id"), + repository_name: LOCAL_AUTOGEN_REPOSITORY_NAME.to_owned(), + repository_priority: RepositoryPriority::LOCAL_AUTOGEN, + candidates, + skipped, + }) +} + +pub fn validate_installed_inventory(inventory: &InstalledInventory) -> Result<(), AutogenError> { + if inventory.format != INSTALLED_INVENTORY_FORMAT { + return Err(AutogenError::UnsupportedInventoryFormat( + inventory.format.clone(), + )); + } + if inventory.version != INSTALLED_INVENTORY_VERSION { + return Err(AutogenError::UnsupportedInventoryVersion { + found: inventory.version, + expected: INSTALLED_INVENTORY_VERSION, + }); + } + Ok(()) +} + +pub fn local_autogen_repo_toml() -> String { + repo_toml( + LOCAL_AUTOGEN_REPOSITORY_ID, + LOCAL_AUTOGEN_REPOSITORY_NAME, + RepositoryPriority::LOCAL_AUTOGEN, + ) +} + +pub fn local_repo_toml() -> String { + repo_toml( + LOCAL_REPOSITORY_ID, + LOCAL_REPOSITORY_NAME, + RepositoryPriority::LOCAL, + ) +} + +fn repo_toml(id: &str, name: &str, priority: RepositoryPriority) -> String { + format!( + "id = \"{id}\"\nname = \"{name}\"\npriority = {}\napi_version = \"{REPO_API_VERSION_V1}\"\n", + priority.value() + ) +} + +pub fn generated_file_is_managed(content: &str) -> bool { + content + .lines() + .next() + .is_some_and(|line| line.contains(GENERATED_MARKER)) +} + +pub fn content_hash(content: &str) -> String { + format!("fnv1a64:{:016x}", fnv1a64(content.as_bytes())) +} + +fn candidate_from_inventory_item( + item: &InstalledInventoryItem, +) -> Result { + match item { + InstalledInventoryItem::AndroidPackage { + package_name, + label, + .. + } => { + let package_name = non_empty(package_name)?; + let package_id = PackageId::new(PackageKind::Android, package_name)?; + let name = label + .as_deref() + .filter(|label| !label.trim().is_empty()) + .unwrap_or(package_name) + .to_owned(); + let installed = InstalledTarget::AndroidPackage { + package_name: package_name.to_owned(), + }; + let relative_path = package_relative_path(&package_id); + let content = render_package_lua(&package_id, &name, &installed); + let content_hash = content_hash(&content); + Ok(AutogenCandidate { + package_id, + name, + installed, + relative_path, + content, + content_hash, + }) + } + InstalledInventoryItem::MagiskModule { + module_id, name, .. + } => { + let module_id = non_empty(module_id)?; + let package_id = PackageId::new(PackageKind::Magisk, module_id)?; + let name = name + .as_deref() + .filter(|name| !name.trim().is_empty()) + .unwrap_or(module_id) + .to_owned(); + let installed = InstalledTarget::MagiskModule { + module_id: module_id.to_owned(), + }; + let relative_path = package_relative_path(&package_id); + let content = render_package_lua(&package_id, &name, &installed); + let content_hash = content_hash(&content); + Ok(AutogenCandidate { + package_id, + name, + installed, + relative_path, + content, + content_hash, + }) + } + } +} + +fn non_empty(value: &str) -> Result<&str, AutogenError> { + let value = value.trim(); + if value.is_empty() { + Err(AutogenError::EmptyIdentity) + } else { + Ok(value) + } +} + +pub fn package_relative_path(package_id: &PackageId) -> PathBuf { + let mut path = PathBuf::from("packages"); + path.push(package_id.kind().as_str()); + path.push(format!("{}.lua", package_id.name())); + path +} + +fn render_package_lua(package_id: &PackageId, name: &str, installed: &InstalledTarget) -> String { + let installed_lua = match installed { + InstalledTarget::AndroidPackage { package_name } => format!( + "{{ kind = \"android_package\", package_name = {} }}", + lua_string(package_name) + ), + InstalledTarget::MagiskModule { module_id } => format!( + "{{ kind = \"magisk_module\", module_id = {} }}", + lua_string(module_id) + ), + InstalledTarget::Generic { id } => { + format!("{{ kind = \"generic\", id = {} }}", lua_string(id)) + } + }; + + format!( + "-- {GENERATED_MARKER}\nreturn package_def {{\n id = {},\n name = {},\n installed = {{\n {installed_lua},\n }},\n}}\n", + lua_string(&package_id.to_string()), + lua_string(name) + ) +} + +fn lua_string(value: &str) -> String { + let mut escaped = String::from("\""); + for ch in value.chars() { + match ch { + '\\' => escaped.push_str("\\\\"), + '"' => escaped.push_str("\\\""), + '\n' => escaped.push_str("\\n"), + '\r' => escaped.push_str("\\r"), + '\t' => escaped.push_str("\\t"), + other => escaped.push(other), + } + } + escaped.push('"'); + escaped +} + +fn fnv1a64(bytes: &[u8]) -> u64 { + let mut hash = 0xcbf29ce484222325u64; + for byte in bytes { + hash ^= u64::from(*byte); + hash = hash.wrapping_mul(0x100000001b3); + } + hash +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::lua::evaluate_package_file; + use crate::repository::RepositoryLayout; + use std::fs; + + #[test] + fn preview_generates_deterministic_android_package_lua() { + let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + label: Some("F-Droid".to_owned()), + version_name: Some("1.20.0".to_owned()), + version_code: Some(1020000), + }]); + + let plan = plan_local_autogen(&inventory, &HashMap::new()).unwrap(); + + assert_eq!(plan.repository_id.as_str(), LOCAL_AUTOGEN_REPOSITORY_ID); + assert_eq!(plan.repository_priority, RepositoryPriority::LOCAL_AUTOGEN); + assert_eq!(plan.candidates.len(), 1); + let candidate = &plan.candidates[0]; + assert_eq!( + candidate.package_id.to_string(), + "android/org.fdroid.fdroid" + ); + assert_eq!( + candidate.relative_path, + PathBuf::from("packages/android/org.fdroid.fdroid.lua") + ); + assert!(candidate.content.contains(GENERATED_MARKER)); + assert!(candidate.content.contains("package_def")); + assert_eq!(candidate.content_hash, content_hash(&candidate.content)); + } + + #[test] + fn preview_skips_packages_covered_by_higher_priority_repositories() { + let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + let mut covered = HashMap::new(); + covered.insert(package_id, RepositoryId::new("official").unwrap()); + let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + label: Some("F-Droid".to_owned()), + version_name: None, + version_code: None, + }]); + + let plan = plan_local_autogen(&inventory, &covered).unwrap(); + + assert!(plan.candidates.is_empty()); + assert_eq!(plan.skipped.len(), 1); + assert_eq!( + plan.skipped[0].reason, + AutogenSkipReason::CoveredByHigherPriorityRepository + ); + assert_eq!( + plan.skipped[0] + .repository_id + .as_ref() + .map(RepositoryId::as_str), + Some("official") + ); + } + + #[test] + fn preview_deduplicates_inventory_items() { + let inventory = InstalledInventory::new(vec![ + InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + label: Some("F-Droid".to_owned()), + version_name: None, + version_code: None, + }, + InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + label: Some("F-Droid Duplicate".to_owned()), + version_name: None, + version_code: None, + }, + ]); + + let plan = plan_local_autogen(&inventory, &HashMap::new()).unwrap(); + + assert_eq!(plan.candidates.len(), 1); + assert_eq!(plan.skipped.len(), 1); + assert_eq!( + plan.skipped[0].reason, + AutogenSkipReason::DuplicateInventoryItem + ); + } + + #[test] + fn rejects_unsupported_inventory_contracts() { + let inventory = InstalledInventory { + format: "other".to_owned(), + version: INSTALLED_INVENTORY_VERSION, + items: Vec::new(), + }; + + assert!(matches!( + plan_local_autogen(&inventory, &HashMap::new()), + Err(AutogenError::UnsupportedInventoryFormat(_)) + )); + } + + #[test] + fn manifest_records_generated_package_hashes() { + let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + label: Some("F-Droid".to_owned()), + version_name: None, + version_code: None, + }]); + let plan = plan_local_autogen(&inventory, &HashMap::new()).unwrap(); + + let manifest = AutogenManifest::from_candidates(&plan.candidates); + + assert_eq!(manifest.version, AUTOGEN_MANIFEST_VERSION); + assert_eq!(manifest.repository_id.as_str(), LOCAL_AUTOGEN_REPOSITORY_ID); + assert_eq!(manifest.packages.len(), 1); + assert_eq!( + manifest.packages[0].content_hash, + content_hash(&plan.candidates[0].content) + ); + } + + #[test] + fn generated_lua_evaluates_through_package_boundary() { + let temp = tempfile::tempdir().unwrap(); + let repo = temp.path(); + fs::write(repo.join("repo.toml"), local_autogen_repo_toml()).unwrap(); + fs::create_dir_all(repo.join("packages/android")).unwrap(); + fs::create_dir(repo.join("lib")).unwrap(); + fs::create_dir(repo.join("templates")).unwrap(); + let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + label: Some("F-Droid \"client\"".to_owned()), + version_name: None, + version_code: None, + }]); + let candidate = plan_local_autogen(&inventory, &HashMap::new()) + .unwrap() + .candidates + .remove(0); + let package_path = repo.join(&candidate.relative_path); + fs::write(&package_path, candidate.content).unwrap(); + + let layout = RepositoryLayout::load(repo).unwrap(); + let package = evaluate_package_file(&layout, &package_path).unwrap(); + + assert_eq!(package.id.to_string(), "android/org.fdroid.fdroid"); + assert_eq!(package.name, "F-Droid \"client\""); + assert_eq!( + package.installed, + vec![InstalledTarget::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned(), + }] + ); + } + + #[test] + fn generated_file_marker_identifies_managed_files_only() { + assert!(generated_file_is_managed(&format!( + "-- {GENERATED_MARKER}\nreturn {{}}" + ))); + assert!(!generated_file_is_managed("return {}")); + } +} diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 32c110b..642e36f 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -9,6 +9,7 @@ use std::cmp::Ordering; use std::fmt; use std::str::FromStr; +pub mod autogen; pub mod diagnostics; pub mod lua; pub mod repository; diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index a6f0e56..f1285c2 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -184,6 +184,43 @@ ON CONFLICT(id) DO UPDATE SET Ok(()) } + pub fn upsert_generated_tracked_package_preserving_user_state( + &self, + package_id: &PackageId, + repository_id: &RepositoryId, + ) -> Result<(), StorageError> { + self.conn.execute( + r#" +INSERT INTO tracked_packages( + package_id, + enabled, + favorite, + ignored_version, + repository_id, + package_resolution +) +VALUES (?1, 1, 0, NULL, ?2, ?3) +ON CONFLICT(package_id) DO UPDATE SET + repository_id = CASE + WHEN tracked_packages.package_resolution = 'missing_package_definition' + THEN excluded.repository_id + ELSE tracked_packages.repository_id + END, + package_resolution = CASE + WHEN tracked_packages.package_resolution = 'missing_package_definition' + THEN excluded.package_resolution + ELSE tracked_packages.package_resolution + END +"#, + params![ + package_id.to_string(), + repository_id.as_str(), + StoredPackageResolution::GenerateLocalPackage.as_str(), + ], + )?; + Ok(()) + } + pub fn import_tracked_packages_with_migration_record( &self, packages: &[TrackedPackageUpsert], @@ -208,6 +245,27 @@ ON CONFLICT(id) DO UPDATE SET Ok(()) } + pub fn delete_generated_tracked_package( + &self, + package_id: &PackageId, + repository_id: &RepositoryId, + ) -> Result { + let deleted = self.conn.execute( + r#" +DELETE FROM tracked_packages +WHERE package_id = ?1 + AND repository_id = ?2 + AND package_resolution = ?3 +"#, + params![ + package_id.to_string(), + repository_id.as_str(), + StoredPackageResolution::GenerateLocalPackage.as_str(), + ], + )?; + Ok(deleted != 0) + } + pub fn tracked_packages(&self) -> Result, StorageError> { let mut stmt = self.conn.prepare( r#" @@ -535,6 +593,119 @@ mod tests { ); } + #[test] + fn generated_tracking_preserves_existing_user_state_on_conflict() { + let db = MainDb::open_in_memory().unwrap(); + let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + let local_autogen = RepositoryId::new("local_autogen").unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: package_id.clone(), + enabled: false, + favorite: true, + ignored_version: Some("9.9.9".to_owned()), + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &local_autogen) + .unwrap(); + + let packages = db.tracked_packages().unwrap(); + assert_eq!(packages.len(), 1); + assert!(!packages[0].enabled); + assert!(packages[0].favorite); + assert_eq!(packages[0].ignored_version.as_deref(), Some("9.9.9")); + assert_eq!(packages[0].repository_id, None); + assert_eq!( + packages[0].package_resolution, + StoredPackageResolution::OfficialRepositoryPackage + ); + } + + #[test] + fn generated_tracking_fills_unresolved_tracking_metadata() { + let db = MainDb::open_in_memory().unwrap(); + let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + let local_autogen = insert_local_autogen_repo(&db); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: package_id.clone(), + enabled: false, + favorite: true, + ignored_version: Some("9.9.9".to_owned()), + repository_id: None, + package_resolution: StoredPackageResolution::MissingPackageDefinition, + }) + .unwrap(); + + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &local_autogen) + .unwrap(); + + let packages = db.tracked_packages().unwrap(); + assert_eq!(packages.len(), 1); + assert!(!packages[0].enabled); + assert!(packages[0].favorite); + assert_eq!(packages[0].ignored_version.as_deref(), Some("9.9.9")); + assert_eq!(packages[0].repository_id.as_ref(), Some(&local_autogen)); + assert_eq!( + packages[0].package_resolution, + StoredPackageResolution::GenerateLocalPackage + ); + } + + #[test] + fn generated_tracking_delete_is_guarded_by_repo_and_resolution() { + let db = MainDb::open_in_memory().unwrap(); + let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + let local_autogen = insert_local_autogen_repo(&db); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: package_id.clone(), + enabled: true, + favorite: true, + ignored_version: Some("9.9.9".to_owned()), + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + + assert!(!db + .delete_generated_tracked_package(&package_id, &local_autogen) + .unwrap()); + assert_eq!(db.tracked_packages().unwrap().len(), 1); + + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: package_id.clone(), + enabled: true, + favorite: true, + ignored_version: Some("9.9.9".to_owned()), + repository_id: None, + package_resolution: StoredPackageResolution::MissingPackageDefinition, + }) + .unwrap(); + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &local_autogen) + .unwrap(); + assert!(db + .delete_generated_tracked_package(&package_id, &local_autogen) + .unwrap()); + assert!(db.tracked_packages().unwrap().is_empty()); + } + + fn insert_local_autogen_repo(db: &MainDb) -> RepositoryId { + let local_autogen = RepositoryId::new("local_autogen").unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: local_autogen.clone(), + name: "Local Autogen".to_owned(), + priority: RepositoryPriority::LOCAL_AUTOGEN, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + None, + None, + ) + .unwrap(); + local_autogen + } + #[test] fn main_db_records_migration_completion() { let db = MainDb::open_in_memory().unwrap(); From 196cd8c2c449a17ef4c58f4fa401a2f6d93efe7b Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 23 Jun 2026 00:56:49 +0800 Subject: [PATCH 15/71] feat(cli): add offline update check --- crates/getter-cli/src/lib.rs | 43 ++- crates/getter-cli/tests/bdd_cli.rs | 202 +++++++++++ .../tests/features/cli/update_check.feature | 68 ++++ crates/getter-core/src/update.rs | 319 +++++++++++++++++- 4 files changed, 628 insertions(+), 4 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/update_check.feature diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 72d92fc..0732d41 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -13,6 +13,7 @@ use getter_core::autogen::{ use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; use getter_core::repository::{RepositoryLayout, RepositoryMetadata, REPO_API_VERSION_V1}; +use getter_core::update::{run_offline_update_check, OfflineUpdateCheckFixture}; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; use getter_storage::legacy_room::{ map_legacy_app, read_legacy_room_database, LegacyAppKind, LegacyAppRecord, @@ -63,6 +64,9 @@ pub enum CliCommand { repo_id: Option, }, StorageValidate, + UpdateCheck { + fixture: PathBuf, + }, AutogenInstalledPreview { inventory: PathBuf, }, @@ -117,6 +121,8 @@ pub enum CliError { Repository(String), #[error("package evaluation error: {0}")] PackageEval(String), + #[error("update check error: {0}")] + Update(String), #[error("autogen error: {0}")] Autogen(String), #[error("Legacy Room export bundle is invalid")] @@ -134,7 +140,7 @@ impl CliError { match self { Self::Usage(_) => ExitCode::Usage, Self::Storage(_) => ExitCode::Storage, - Self::Repository(_) | Self::PackageEval(_) | Self::Autogen(_) => { + Self::Repository(_) | Self::PackageEval(_) | Self::Update(_) | Self::Autogen(_) => { ExitCode::GenericFailure } Self::InvalidLegacyBundle { .. } @@ -150,6 +156,7 @@ impl CliError { Self::Storage(_) => "storage.error", Self::Repository(_) => "repository.error", Self::PackageEval(_) => "package.eval_error", + Self::Update(_) => "update.check_error", Self::Autogen(_) => "autogen.error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", @@ -164,6 +171,7 @@ impl CliError { Self::Storage(_) => "Getter storage operation failed", Self::Repository(_) => "Getter repository operation failed", Self::PackageEval(_) => "Getter package evaluation failed", + Self::Update(_) => "Getter update check failed", Self::Autogen(_) => "Getter autogen operation failed", Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", Self::UnsupportedLegacyBundle { .. } => { @@ -180,6 +188,7 @@ impl CliError { | Self::Storage(detail) | Self::Repository(detail) | Self::PackageEval(detail) + | Self::Update(detail) | Self::Autogen(detail) => Some(detail.as_str()), Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } @@ -198,6 +207,7 @@ impl CliError { | Self::Storage(_) | Self::Repository(_) | Self::PackageEval(_) + | Self::Update(_) | Self::Autogen(_) => None, } } @@ -326,6 +336,13 @@ where [domain, command] if domain == "storage" && command == "validate" => { CliCommand::StorageValidate } + [domain, command, flag, fixture] + if domain == "update" && command == "check" && flag == "--fixture" => + { + CliCommand::UpdateCheck { + fixture: PathBuf::from(fixture), + } + } [domain, subject, action, flag, inventory] if domain == "autogen" && subject == "installed" @@ -471,6 +488,16 @@ fn execute(invocation: CliInvocation) -> Result { "cache_db": cache_db_path(&invocation.data_dir), })) } + CliCommand::UpdateCheck { fixture } => { + open_initialized_storage(&invocation.data_dir)?; + let fixture = read_update_check_fixture(&fixture)?; + serde_json::to_value(run_offline_update_check(fixture).map_err(|source| { + CliError::Update(format!("offline update check failed: {source}")) + })?) + .map_err(|source| { + CliError::Update(format!("failed to serialize update check: {source}")) + }) + } CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; @@ -754,6 +781,17 @@ fn read_installed_inventory(path: &Path) -> Result .map_err(|source| CliError::Autogen(format!("failed to parse inventory JSON: {source}"))) } +fn read_update_check_fixture(path: &Path) -> Result { + let bytes = fs::read(path).map_err(|source| { + CliError::Update(format!("failed to read update check fixture: {source}")) + })?; + serde_json::from_slice(&bytes).map_err(|source| { + CliError::Update(format!( + "failed to parse update check fixture JSON: {source}" + )) + }) +} + fn build_local_autogen_plan( db: &MainDb, inventory: &InstalledInventory, @@ -1684,7 +1722,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|update check --fixture |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1737,6 +1775,7 @@ impl CliCommand { Self::RepoValidate { .. } => "repo validate", Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", + Self::UpdateCheck { .. } => "update check", Self::AutogenInstalledPreview { .. } => "autogen installed preview", Self::AutogenInstalledApply { .. } => "autogen installed apply", Self::AutogenCleanupPreview { .. } => "autogen cleanup preview", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index dfe31e2..fdef3e5 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -14,6 +14,7 @@ struct CliWorld { legacy_db: Option, inventory: Option, autogen_preview: Option, + update_fixture: Option, fixture_repo_id: Option, fixture_repo_path: Option, fixture_package_id: Option, @@ -187,6 +188,79 @@ fn tampered_autogen_cleanup_preview_for_package(world: &mut CliWorld, package_id world.autogen_preview = Some(preview); } +#[given( + expr = "an offline update fixture for package {string} installed version {string} with candidate versions {string}" +)] +fn offline_update_fixture_with_installed_version( + world: &mut CliWorld, + package_id: String, + installed_version: String, + versions: String, +) { + write_offline_update_fixture(world, package_id, Some(installed_version), None, versions); +} + +#[given( + expr = "an offline update fixture for package {string} installed version {string} ignored version {string} with candidate versions {string}" +)] +fn offline_update_fixture_with_ignored_version( + world: &mut CliWorld, + package_id: String, + installed_version: String, + ignored_version: String, + versions: String, +) { + write_offline_update_fixture( + world, + package_id, + Some(installed_version), + Some(ignored_version), + versions, + ); +} + +#[given( + expr = "an offline update fixture for package {string} without installed version with candidate versions {string}" +)] +fn offline_update_fixture_without_installed_version( + world: &mut CliWorld, + package_id: String, + versions: String, +) { + write_offline_update_fixture(world, package_id, None, None, versions); +} + +#[given( + expr = "an offline update fixture for package {string} installed version {string} with artifactless candidate version {string}" +)] +fn offline_update_fixture_with_artifactless_candidate( + world: &mut CliWorld, + package_id: String, + installed_version: String, + candidate_version: String, +) { + write_offline_update_fixture_with_candidates( + world, + package_id, + Some(installed_version), + None, + vec![serde_json::json!({ + "version": candidate_version, + "channel": "stable", + "source": "offline-fixture", + "artifacts": [], + })], + ); +} + +#[given("a malformed offline update fixture")] +fn malformed_offline_update_fixture(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let fixture = temp.path().join("malformed-update-fixture.json"); + fs::write(&fixture, "not-json").expect("write malformed fixture"); + world.update_fixture = Some(fixture); +} + #[given(expr = "a fixture Lua repository {string} with package {string}")] fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); @@ -407,6 +481,25 @@ fn run_getter_legacy_report_list(world: &mut CliWorld) { world.json = None; } +#[when("I run getter update check for that fixture")] +fn run_getter_update_check(world: &mut CliWorld) { + let fixture = world + .update_fixture + .as_ref() + .expect("update fixture exists"); + let output = run_getter( + world, + [ + "update".to_owned(), + "check".to_owned(), + "--fixture".to_owned(), + fixture.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen installed preview for that inventory")] fn run_getter_autogen_installed_preview(world: &mut CliWorld) { let inventory = world.inventory.as_ref().expect("inventory exists"); @@ -548,6 +641,17 @@ fn command_fails_with_autogen_error(world: &mut CliWorld) { world.json = Some(json); } +#[then("the command fails with an update check error")] +fn command_fails_with_update_check_error(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(1)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["command"], "update check"); + assert_eq!(json["error"]["code"], "update.check_error"); + world.json = Some(json); +} + #[then(expr = "the command fails with direct DB migration error {string}")] fn command_fails_with_direct_db_migration_error(world: &mut CliWorld, code: String) { let output = world.output.as_ref().expect("command output exists"); @@ -711,6 +815,44 @@ fn output_contains_named_package(world: &mut CliWorld, package_id: String, packa assert_eq!(json["data"]["package"]["name"], package_name); } +#[then(expr = "the update check status is {string}")] +fn update_check_status_is(world: &mut CliWorld, status: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "update check"); + assert_eq!(json["data"]["network_required"], false); + assert_eq!(json["data"]["status"], status); +} + +#[then(expr = "the selected update version is {string}")] +fn selected_update_version_is(world: &mut CliWorld, version: String) { + let json = current_json(world); + assert_eq!(json["data"]["selected"]["candidate"]["version"], version); +} + +#[then(expr = "the update check actions download file {string} and request installer {string}")] +fn update_check_actions_download_and_install( + world: &mut CliWorld, + file_name: String, + installer: String, +) { + let json = current_json(world); + let actions = json["data"]["actions"].as_array().expect("actions array"); + assert_eq!(actions.len(), 2); + assert_eq!(actions[0]["type"], "download"); + assert_eq!(actions[0]["file_name"], file_name); + assert_eq!(actions[1]["type"], "install"); + assert_eq!(actions[1]["installer"], installer); + assert_eq!(actions[1]["file"], file_name); +} + +#[then("the update check has no selected update")] +fn update_check_has_no_selected_update(world: &mut CliWorld) { + let json = current_json(world); + assert!(json["data"]["selected"].is_null()); + assert_eq!(json["data"]["actions"], Value::Array(Vec::new())); +} + #[then(expr = "the autogen preview contains candidate {string}")] fn autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) { let json = current_json(world); @@ -1099,6 +1241,66 @@ return package_def {{ world.fixture_package_id = Some(package_id); } +fn write_offline_update_fixture( + world: &mut CliWorld, + package_id: String, + installed_version: Option, + ignored_version: Option, + versions: String, +) { + let candidates: Vec = versions + .split(',') + .map(str::trim) + .filter(|version| !version.is_empty()) + .map(|version| { + serde_json::json!({ + "version": version, + "channel": "stable", + "source": "offline-fixture", + "artifacts": [ + { + "name": "APK", + "url": format!("https://example.invalid/{version}.apk"), + "file_name": "app.apk", + } + ], + }) + }) + .collect(); + write_offline_update_fixture_with_candidates( + world, + package_id, + installed_version, + ignored_version, + candidates, + ); +} + +fn write_offline_update_fixture_with_candidates( + world: &mut CliWorld, + package_id: String, + installed_version: Option, + ignored_version: Option, + candidates: Vec, +) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let fixture = temp.path().join("offline-update-fixture.json"); + fs::write( + &fixture, + serde_json::to_vec_pretty(&serde_json::json!({ + "format": "getter-offline-update-check", + "version": 1, + "package_id": package_id, + "installed_version": installed_version, + "ignored_version": ignored_version, + "candidates": candidates, + })) + .expect("fixture serializes"), + ) + .expect("write offline update fixture"); + world.update_fixture = Some(fixture); +} + fn create_fixture_legacy_room_db(path: &PathBuf, version: u32, include_app_table: bool) { let conn = Connection::open(path).expect("create legacy Room fixture"); conn.pragma_update(None, "user_version", version) diff --git a/crates/getter-cli/tests/features/cli/update_check.feature b/crates/getter-cli/tests/features/cli/update_check.feature new file mode 100644 index 0000000..e5d4828 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/update_check.feature @@ -0,0 +1,68 @@ +@getter-cli @update +Feature: Offline update check + Scenario: User checks an offline fixture with an available update + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" with candidate versions "1.0.1,1.2.0" + When I run getter update check for that fixture + Then the command succeeds + And the output is valid JSON + And the update check status is "update_available" + And the selected update version is "1.2.0" + And the update check actions download file "app.apk" and request installer "android_package" + + Scenario: User checks an offline fixture that is already up to date + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "2.0.0" with candidate versions "1.9.0,2.0.0" + When I run getter update check for that fixture + Then the command succeeds + And the output is valid JSON + And the update check status is "up_to_date" + And the update check has no selected update + + Scenario: User checks an offline fixture where the latest update is ignored + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" ignored version "1.2.0" with candidate versions "1.1.0,1.2.0" + When I run getter update check for that fixture + Then the command succeeds + And the output is valid JSON + And the update check status is "update_available" + And the selected update version is "1.1.0" + + Scenario: User checks an offline fixture where the only update is ignored + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" ignored version "1.2.0" with candidate versions "1.2.0" + When I run getter update check for that fixture + Then the command succeeds + And the output is valid JSON + And the update check status is "ignored" + And the update check has no selected update + + Scenario: User checks an offline fixture without an installed version + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" without installed version with candidate versions "1.0.0-beta,1.0.0" + When I run getter update check for that fixture + Then the command succeeds + And the output is valid JSON + And the update check status is "update_available" + And the selected update version is "1.0.0" + + Scenario: User checks an offline fixture with no candidates + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" with candidate versions "" + When I run getter update check for that fixture + Then the command succeeds + And the output is valid JSON + And the update check status is "no_candidates" + And the update check has no selected update + + Scenario: User receives structured errors when the selected update has no artifact + Given an initialized getter data directory + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" with artifactless candidate version "1.2.0" + When I run getter update check for that fixture + Then the command fails with an update check error + + Scenario: User receives structured errors for malformed offline update fixtures + Given an initialized getter data directory + And a malformed offline update fixture + When I run getter update check for that fixture + Then the command fails with an update check error diff --git a/crates/getter-core/src/update.rs b/crates/getter-core/src/update.rs index ab3256c..72acfa6 100644 --- a/crates/getter-core/src/update.rs +++ b/crates/getter-core/src/update.rs @@ -1,13 +1,198 @@ //! Update selection helpers owned by getter core. -use crate::{PackageId, SelectedUpdate, UpdateArtifact, UpdateCandidate}; +use crate::{ + PackageId, PackageKind, SelectedUpdate, UpdateAction, UpdateArtifact, UpdateCandidate, +}; +use serde::{Deserialize, Serialize}; use std::cmp::Ordering; +pub const OFFLINE_UPDATE_CHECK_FORMAT: &str = "getter-offline-update-check"; +pub const OFFLINE_UPDATE_CHECK_VERSION: u32 = 1; + /// User state that affects update selection. -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct UpdateSelectionPolicy { /// Candidate version the user chose to ignore/mark as skipped. + #[serde(default)] + pub ignored_version: Option, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct OfflineUpdateCheckFixture { + pub format: String, + pub version: u32, + pub package_id: PackageId, + #[serde(default)] + pub installed_version: Option, + #[serde(default)] pub ignored_version: Option, + #[serde(default)] + pub candidates: Vec, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct OfflineUpdateCheckResult { + pub network_required: bool, + pub package_id: PackageId, + #[serde(default)] + pub installed_version: Option, + pub policy: UpdateSelectionPolicy, + pub status: UpdateCheckStatus, + #[serde(default)] + pub selected: Option, + pub actions: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum UpdateCheckStatus { + UpdateAvailable, + UpToDate, + NoCandidates, + Ignored, +} + +#[derive(Debug, thiserror::Error)] +pub enum OfflineUpdateCheckError { + #[error("unsupported update check fixture format '{0}'")] + UnsupportedFormat(String), + #[error("unsupported update check fixture version {found}; expected {expected}")] + UnsupportedVersion { found: u32, expected: u32 }, + #[error("selected update candidate '{version}' has no actionable artifacts")] + MissingSelectedArtifact { version: String }, +} + +pub fn run_offline_update_check( + fixture: OfflineUpdateCheckFixture, +) -> Result { + if fixture.format != OFFLINE_UPDATE_CHECK_FORMAT { + return Err(OfflineUpdateCheckError::UnsupportedFormat(fixture.format)); + } + if fixture.version != OFFLINE_UPDATE_CHECK_VERSION { + return Err(OfflineUpdateCheckError::UnsupportedVersion { + found: fixture.version, + expected: OFFLINE_UPDATE_CHECK_VERSION, + }); + } + + let policy = UpdateSelectionPolicy { + ignored_version: fixture.ignored_version, + }; + check_updates_offline( + fixture.package_id, + fixture.installed_version, + fixture.candidates, + policy, + ) +} + +pub fn check_updates_offline( + package_id: PackageId, + installed_version: Option, + candidates: Vec, + policy: UpdateSelectionPolicy, +) -> Result { + let selected = select_update( + package_id.clone(), + installed_version.as_deref(), + &candidates, + &policy, + ); + let status = update_check_status( + selected.as_ref(), + installed_version.as_deref(), + &candidates, + &policy, + ); + let actions = match selected.as_ref() { + Some(selected) => { + if selected.artifact.is_none() { + return Err(OfflineUpdateCheckError::MissingSelectedArtifact { + version: selected.candidate.version.clone(), + }); + } + update_actions_for_selected(selected) + } + None => Vec::new(), + }; + + Ok(OfflineUpdateCheckResult { + network_required: false, + package_id, + installed_version, + policy, + status, + selected, + actions, + }) +} + +pub fn update_actions_for_selected(selected: &SelectedUpdate) -> Vec { + let Some(artifact) = selected.artifact.as_ref() else { + return Vec::new(); + }; + let file_name = artifact_file_name(artifact); + vec![ + UpdateAction::Download { + url: artifact.url.clone(), + file_name: file_name.clone(), + }, + UpdateAction::Install { + installer: installer_for_package_kind(selected.package_id.kind()).to_owned(), + file: file_name, + }, + ] +} + +fn artifact_file_name(artifact: &UpdateArtifact) -> String { + artifact + .file_name + .as_ref() + .filter(|value| !value.trim().is_empty()) + .cloned() + .unwrap_or_else(|| artifact.name.clone()) +} + +fn installer_for_package_kind(kind: PackageKind) -> &'static str { + match kind { + PackageKind::Android => "android_package", + PackageKind::Magisk => "magisk_module", + PackageKind::Generic => "generic_file", + } +} + +fn update_check_status( + selected: Option<&SelectedUpdate>, + installed_version: Option<&str>, + candidates: &[UpdateCandidate], + policy: &UpdateSelectionPolicy, +) -> UpdateCheckStatus { + if candidates.is_empty() { + UpdateCheckStatus::NoCandidates + } else if selected.is_some() { + UpdateCheckStatus::UpdateAvailable + } else if ignored_candidate_would_have_been_update(installed_version, candidates, policy) { + UpdateCheckStatus::Ignored + } else { + UpdateCheckStatus::UpToDate + } +} + +fn ignored_candidate_would_have_been_update( + installed_version: Option<&str>, + candidates: &[UpdateCandidate], + policy: &UpdateSelectionPolicy, +) -> bool { + let Some(ignored) = policy.ignored_version.as_deref() else { + return false; + }; + + candidates.iter().any(|candidate| { + compare_versions(&candidate.version, ignored) == Ordering::Equal + && installed_version.is_none_or(|installed| { + compare_versions(&candidate.version, installed) == Ordering::Greater + }) + }) } /// Compare human-facing version strings using a deterministic token ordering. @@ -257,6 +442,136 @@ mod tests { ); } + #[test] + fn offline_update_check_reports_update_available_with_actions() { + let result = run_offline_update_check(OfflineUpdateCheckFixture { + format: OFFLINE_UPDATE_CHECK_FORMAT.to_owned(), + version: OFFLINE_UPDATE_CHECK_VERSION, + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + installed_version: Some("1.0.0".to_owned()), + ignored_version: None, + candidates: vec![candidate("1.0.1"), candidate("1.2.0")], + }) + .unwrap(); + + assert_eq!(result.status, UpdateCheckStatus::UpdateAvailable); + assert_eq!(result.selected.as_ref().unwrap().candidate.version, "1.2.0"); + assert_eq!( + result.actions, + vec![ + UpdateAction::Download { + url: "https://example.invalid/1.2.0.apk".to_owned(), + file_name: "app.apk".to_owned() + }, + UpdateAction::Install { + installer: "android_package".to_owned(), + file: "app.apk".to_owned() + } + ] + ); + } + + #[test] + fn offline_update_check_reports_up_to_date() { + let result = check_updates_offline( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("2.0.0".to_owned()), + vec![candidate("1.9.0"), candidate("2.0.0")], + UpdateSelectionPolicy::default(), + ) + .unwrap(); + + assert_eq!(result.status, UpdateCheckStatus::UpToDate); + assert!(result.selected.is_none()); + assert!(result.actions.is_empty()); + } + + #[test] + fn offline_update_check_reports_ignored_when_only_update_is_ignored() { + let result = check_updates_offline( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("1.0.0".to_owned()), + vec![candidate("1.2.0")], + UpdateSelectionPolicy { + ignored_version: Some("1.2.0".to_owned()), + }, + ) + .unwrap(); + + assert_eq!(result.status, UpdateCheckStatus::Ignored); + assert!(result.selected.is_none()); + } + + #[test] + fn offline_update_check_falls_back_below_ignored_latest() { + let result = check_updates_offline( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("1.0.0".to_owned()), + vec![candidate("1.1.0"), candidate("1.2.0")], + UpdateSelectionPolicy { + ignored_version: Some("1.2.0".to_owned()), + }, + ) + .unwrap(); + + assert_eq!(result.status, UpdateCheckStatus::UpdateAvailable); + assert_eq!(result.selected.as_ref().unwrap().candidate.version, "1.1.0"); + } + + #[test] + fn offline_update_check_reports_no_candidates() { + let result = check_updates_offline( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("1.0.0".to_owned()), + Vec::new(), + UpdateSelectionPolicy::default(), + ) + .unwrap(); + + assert_eq!(result.status, UpdateCheckStatus::NoCandidates); + assert!(result.selected.is_none()); + assert!(result.actions.is_empty()); + } + + #[test] + fn offline_update_check_rejects_selected_candidate_without_artifacts() { + let error = check_updates_offline( + "android/org.fdroid.fdroid".parse().unwrap(), + Some("1.0.0".to_owned()), + vec![UpdateCandidate { + version: "1.2.0".to_owned(), + channel: None, + source: None, + artifacts: Vec::new(), + }], + UpdateSelectionPolicy::default(), + ) + .unwrap_err(); + + assert!(matches!( + error, + OfflineUpdateCheckError::MissingSelectedArtifact { .. } + )); + } + + #[test] + fn offline_update_check_rejects_wrong_contract() { + let error = run_offline_update_check(OfflineUpdateCheckFixture { + format: "wrong".to_owned(), + version: OFFLINE_UPDATE_CHECK_VERSION, + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + installed_version: None, + ignored_version: None, + candidates: Vec::new(), + }) + .unwrap_err(); + + assert!(matches!( + error, + OfflineUpdateCheckError::UnsupportedFormat(_) + )); + } + fn candidate(version: &str) -> UpdateCandidate { UpdateCandidate { version: version.to_owned(), From cb4cb655f3aefa69200ac23cfcc1e770d678d664 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 23 Jun 2026 10:53:49 +0800 Subject: [PATCH 16/71] feat(cli): add offline task lifecycle --- Cargo.lock | 3 + crates/getter-cli/Cargo.toml | 1 + crates/getter-cli/src/lib.rs | 169 ++++- crates/getter-cli/tests/bdd_cli.rs | 259 +++++++ .../tests/features/cli/task_lifecycle.feature | 89 +++ crates/getter-core/src/lib.rs | 1 + crates/getter-core/src/task.rs | 266 +++++++ crates/getter-downloader/Cargo.toml | 2 + crates/getter-downloader/src/lib.rs | 166 ++++- crates/getter-storage/src/lib.rs | 674 +++++++++++++++++- 10 files changed, 1626 insertions(+), 4 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/task_lifecycle.feature create mode 100644 crates/getter-core/src/task.rs diff --git a/Cargo.lock b/Cargo.lock index 6119744..a23b19e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -520,6 +520,7 @@ version = "0.1.0" dependencies = [ "cucumber", "getter-core", + "getter-downloader", "getter-storage", "rusqlite", "serde", @@ -546,6 +547,8 @@ name = "getter-downloader" version = "0.1.0" dependencies = [ "getter-core", + "getter-storage", + "thiserror 1.0.69", ] [[package]] diff --git a/crates/getter-cli/Cargo.toml b/crates/getter-cli/Cargo.toml index ef7d105..248cb01 100644 --- a/crates/getter-cli/Cargo.toml +++ b/crates/getter-cli/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core" } +getter-downloader = { path = "../getter-downloader" } getter-storage = { path = "../getter-storage" } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 0732d41..08d63d0 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -13,8 +13,15 @@ use getter_core::autogen::{ use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; use getter_core::repository::{RepositoryLayout, RepositoryMetadata, REPO_API_VERSION_V1}; +use getter_core::task::{ + DownloadTaskRequest, InstallHandoffStatus, TaskEventPage, DOWNLOAD_REQUEST_FORMAT, + DOWNLOAD_REQUEST_VERSION, +}; use getter_core::update::{run_offline_update_check, OfflineUpdateCheckFixture}; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; +use getter_downloader::{ + cancel_download_task, record_install_result, run_fake_download_task, submit_fake_download_task, +}; use getter_storage::legacy_room::{ map_legacy_app, read_legacy_room_database, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, LegacyRoomDbImport, LegacyRoomImportWarning, @@ -67,6 +74,24 @@ pub enum CliCommand { UpdateCheck { fixture: PathBuf, }, + TaskSubmit { + request: PathBuf, + }, + TaskRun { + task_id: String, + }, + TaskList, + TaskCancel { + task_id: String, + }, + TaskEvents { + after: u64, + limit: usize, + }, + TaskInstallResult { + handoff_id: String, + status: InstallHandoffStatus, + }, AutogenInstalledPreview { inventory: PathBuf, }, @@ -103,6 +128,7 @@ pub enum ExitCode { Usage = 2, Storage = 10, Migration = 20, + Download = 40, } impl ExitCode { @@ -123,6 +149,8 @@ pub enum CliError { PackageEval(String), #[error("update check error: {0}")] Update(String), + #[error("download task error: {0}")] + Download(String), #[error("autogen error: {0}")] Autogen(String), #[error("Legacy Room export bundle is invalid")] @@ -143,6 +171,7 @@ impl CliError { Self::Repository(_) | Self::PackageEval(_) | Self::Update(_) | Self::Autogen(_) => { ExitCode::GenericFailure } + Self::Download(_) => ExitCode::Download, Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } | Self::InvalidLegacyDb { .. } @@ -157,6 +186,7 @@ impl CliError { Self::Repository(_) => "repository.error", Self::PackageEval(_) => "package.eval_error", Self::Update(_) => "update.check_error", + Self::Download(_) => "download.task_error", Self::Autogen(_) => "autogen.error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", @@ -172,6 +202,7 @@ impl CliError { Self::Repository(_) => "Getter repository operation failed", Self::PackageEval(_) => "Getter package evaluation failed", Self::Update(_) => "Getter update check failed", + Self::Download(_) => "Getter download task operation failed", Self::Autogen(_) => "Getter autogen operation failed", Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", Self::UnsupportedLegacyBundle { .. } => { @@ -189,6 +220,7 @@ impl CliError { | Self::Repository(detail) | Self::PackageEval(detail) | Self::Update(detail) + | Self::Download(detail) | Self::Autogen(detail) => Some(detail.as_str()), Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } @@ -208,6 +240,7 @@ impl CliError { | Self::Repository(_) | Self::PackageEval(_) | Self::Update(_) + | Self::Download(_) | Self::Autogen(_) => None, } } @@ -343,6 +376,41 @@ where fixture: PathBuf::from(fixture), } } + [domain, command, flag, request] + if domain == "task" && command == "submit" && flag == "--request" => + { + CliCommand::TaskSubmit { + request: PathBuf::from(request), + } + } + [domain, command, task_id] if domain == "task" && command == "run" => CliCommand::TaskRun { + task_id: task_id.clone(), + }, + [domain, command] if domain == "task" && command == "list" => CliCommand::TaskList, + [domain, command, task_id] if domain == "task" && command == "cancel" => { + CliCommand::TaskCancel { + task_id: task_id.clone(), + } + } + [domain, command, after_flag, after, limit_flag, limit] + if domain == "task" + && command == "events" + && after_flag == "--after" + && limit_flag == "--limit" => + { + CliCommand::TaskEvents { + after: parse_u64(after, "--after")?, + limit: parse_positive_usize(limit, "--limit")?, + } + } + [domain, command, handoff_id, status_flag, status] + if domain == "task" && command == "install-result" && status_flag == "--status" => + { + CliCommand::TaskInstallResult { + handoff_id: handoff_id.clone(), + status: parse_install_handoff_status(status)?, + } + } [domain, subject, action, flag, inventory] if domain == "autogen" && subject == "installed" @@ -498,6 +566,46 @@ fn execute(invocation: CliInvocation) -> Result { CliError::Update(format!("failed to serialize update check: {source}")) }) } + CliCommand::TaskSubmit { request } => { + let db = open_main_db(&invocation.data_dir)?; + let request = read_download_task_request(&request)?; + serde_json::to_value(submit_fake_download_task(&db, request).map_err(|source| { + CliError::Download(format!("offline task submit failed: {source}")) + })?) + .map_err(|source| CliError::Download(format!("failed to serialize task: {source}"))) + } + CliCommand::TaskRun { task_id } => { + let db = open_main_db(&invocation.data_dir)?; + serde_json::to_value(run_fake_download_task(&db, &task_id).map_err(|source| { + CliError::Download(format!("offline task run failed: {source}")) + })?) + .map_err(|source| CliError::Download(format!("failed to serialize task: {source}"))) + } + CliCommand::TaskList => { + let db = open_main_db(&invocation.data_dir)?; + Ok(json!({ "tasks": db.download_tasks()? })) + } + CliCommand::TaskCancel { task_id } => { + let db = open_main_db(&invocation.data_dir)?; + serde_json::to_value(cancel_download_task(&db, &task_id).map_err(|source| { + CliError::Download(format!("offline task cancel failed: {source}")) + })?) + .map_err(|source| CliError::Download(format!("failed to serialize task: {source}"))) + } + CliCommand::TaskEvents { after, limit } => { + let db = open_main_db(&invocation.data_dir)?; + let events: TaskEventPage = db.task_events_after(after, limit)?; + serde_json::to_value(events).map_err(|source| { + CliError::Download(format!("failed to serialize task events: {source}")) + }) + } + CliCommand::TaskInstallResult { handoff_id, status } => { + let db = open_main_db(&invocation.data_dir)?; + serde_json::to_value(record_install_result(&db, &handoff_id, status).map_err( + |source| CliError::Download(format!("offline install result failed: {source}")), + )?) + .map_err(|source| CliError::Download(format!("failed to serialize handoff: {source}"))) + } CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; @@ -686,6 +794,38 @@ fn parse_package_id(value: &str) -> Result { .map_err(|source: getter_core::PackageIdError| CliError::Usage(source.to_string())) } +fn parse_u64(value: &str, flag: &str) -> Result { + value + .parse() + .map_err(|source| CliError::Usage(format!("invalid {flag} value '{value}': {source}"))) +} + +fn parse_usize(value: &str, flag: &str) -> Result { + value + .parse() + .map_err(|source| CliError::Usage(format!("invalid {flag} value '{value}': {source}"))) +} + +fn parse_positive_usize(value: &str, flag: &str) -> Result { + let parsed = parse_usize(value, flag)?; + if parsed == 0 { + return Err(CliError::Usage(format!("{flag} must be greater than zero"))); + } + Ok(parsed) +} + +fn parse_install_handoff_status(value: &str) -> Result { + match value { + "accepted" => Ok(InstallHandoffStatus::Accepted), + "succeeded" => Ok(InstallHandoffStatus::Succeeded), + "failed" => Ok(InstallHandoffStatus::Failed), + "canceled" => Ok(InstallHandoffStatus::Canceled), + _ => Err(CliError::Usage(format!( + "invalid install result status '{value}'; expected accepted, succeeded, failed, or canceled" + ))), + } +} + fn parse_priority(value: &str) -> Result { value .parse::() @@ -792,6 +932,27 @@ fn read_update_check_fixture(path: &Path) -> Result Result { + let bytes = fs::read(path) + .map_err(|source| CliError::Download(format!("failed to read task request: {source}")))?; + let request: DownloadTaskRequest = serde_json::from_slice(&bytes).map_err(|source| { + CliError::Download(format!("failed to parse task request JSON: {source}")) + })?; + if request.format != DOWNLOAD_REQUEST_FORMAT { + return Err(CliError::Download(format!( + "unsupported download request format '{}'", + request.format + ))); + } + if request.version != DOWNLOAD_REQUEST_VERSION { + return Err(CliError::Download(format!( + "unsupported download request version {}; expected {}", + request.version, DOWNLOAD_REQUEST_VERSION + ))); + } + Ok(request) +} + fn build_local_autogen_plan( db: &MainDb, inventory: &InstalledInventory, @@ -1722,7 +1883,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|update check --fixture |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|update check --fixture |task submit --request |task run |task list|task cancel |task events --after --limit |task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1776,6 +1937,12 @@ impl CliCommand { Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", Self::UpdateCheck { .. } => "update check", + Self::TaskSubmit { .. } => "task submit", + Self::TaskRun { .. } => "task run", + Self::TaskList => "task list", + Self::TaskCancel { .. } => "task cancel", + Self::TaskEvents { .. } => "task events", + Self::TaskInstallResult { .. } => "task install-result", Self::AutogenInstalledPreview { .. } => "autogen installed preview", Self::AutogenInstalledApply { .. } => "autogen installed apply", Self::AutogenCleanupPreview { .. } => "autogen cleanup preview", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index fdef3e5..d0230ea 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -15,6 +15,10 @@ struct CliWorld { inventory: Option, autogen_preview: Option, update_fixture: Option, + task_request: Option, + remembered_task_id: Option, + remembered_event_cursor: Option, + remembered_handoff_id: Option, fixture_repo_id: Option, fixture_repo_path: Option, fixture_package_id: Option, @@ -261,6 +265,44 @@ fn malformed_offline_update_fixture(world: &mut CliWorld) { world.update_fixture = Some(fixture); } +#[given(expr = "an offline download request for package {string}")] +fn offline_download_request_for_package(world: &mut CliWorld, package_id: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let request = temp.path().join("download-request.json"); + fs::write( + &request, + serde_json::to_vec_pretty(&serde_json::json!({ + "format": "getter-download-request", + "version": 1, + "package_id": package_id, + "executor": "fake", + "actions": [ + { + "type": "download", + "url": "https://example.invalid/app.apk", + "file_name": "app.apk" + }, + { + "type": "install", + "installer": "android_package", + "file": "app.apk" + } + ] + })) + .expect("request serializes"), + ) + .expect("write download request"); + world.task_request = Some(request); +} + +#[given("a malformed offline download request")] +fn malformed_offline_download_request(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let request = temp.path().join("malformed-download-request.json"); + fs::write(&request, "not-json").expect("write malformed request"); + world.task_request = Some(request); +} + #[given(expr = "a fixture Lua repository {string} with package {string}")] fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); @@ -500,6 +542,99 @@ fn run_getter_update_check(world: &mut CliWorld) { world.json = None; } +#[when("I run getter task submit for that request")] +fn run_getter_task_submit(world: &mut CliWorld) { + let request = world.task_request.as_ref().expect("task request exists"); + let output = run_getter( + world, + [ + "task".to_owned(), + "submit".to_owned(), + "--request".to_owned(), + request.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter task list")] +fn run_getter_task_list(world: &mut CliWorld) { + let output = run_getter(world, ["task".to_owned(), "list".to_owned()]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter task cancel for the remembered task")] +fn run_getter_task_cancel(world: &mut CliWorld) { + let task_id = world + .remembered_task_id + .as_ref() + .expect("remembered task id exists") + .clone(); + let output = run_getter(world, ["task".to_owned(), "cancel".to_owned(), task_id]); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter task run for the remembered task")] +fn run_getter_task_run(world: &mut CliWorld) { + let task_id = world + .remembered_task_id + .as_ref() + .expect("remembered task id exists") + .clone(); + let output = run_getter(world, ["task".to_owned(), "run".to_owned(), task_id]); + world.output = Some(output); + world.json = None; +} + +#[when(expr = "I run getter task events after {int} limit {int}")] +fn run_getter_task_events_after_limit(world: &mut CliWorld, after: u64, limit: u64) { + let output = run_getter( + world, + [ + "task".to_owned(), + "events".to_owned(), + "--after".to_owned(), + after.to_string(), + "--limit".to_owned(), + limit.to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when(expr = "I run getter task events after the remembered cursor limit {int}")] +fn run_getter_task_events_after_remembered_cursor(world: &mut CliWorld, limit: u64) { + let after = world + .remembered_event_cursor + .expect("remembered event cursor exists"); + run_getter_task_events_after_limit(world, after, limit); +} + +#[when(expr = "I run getter task install-result {string} for the remembered handoff")] +fn run_getter_task_install_result(world: &mut CliWorld, status: String) { + let handoff_id = world + .remembered_handoff_id + .as_ref() + .expect("remembered handoff id exists") + .clone(); + let output = run_getter( + world, + [ + "task".to_owned(), + "install-result".to_owned(), + handoff_id, + "--status".to_owned(), + status, + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen installed preview for that inventory")] fn run_getter_autogen_installed_preview(world: &mut CliWorld) { let inventory = world.inventory.as_ref().expect("inventory exists"); @@ -652,6 +787,26 @@ fn command_fails_with_update_check_error(world: &mut CliWorld) { world.json = Some(json); } +#[then("the command fails with a download task error")] +fn command_fails_with_download_task_error(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(40)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["error"]["code"], "download.task_error"); + world.json = Some(json); +} + +#[then("the command fails with a CLI usage error")] +fn command_fails_with_cli_usage_error(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(2)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["error"]["code"], "cli.usage"); + world.json = Some(json); +} + #[then(expr = "the command fails with direct DB migration error {string}")] fn command_fails_with_direct_db_migration_error(world: &mut CliWorld, code: String) { let output = world.output.as_ref().expect("command output exists"); @@ -714,6 +869,110 @@ fn output_contains_empty_repository_list(world: &mut CliWorld) { assert_eq!(json["data"]["repositories"], Value::Array(Vec::new())); } +#[then("I remember the submitted task id")] +fn remember_submitted_task_id(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["command"], "task submit"); + let task_id = json["data"]["task"]["id"] + .as_str() + .expect("task id should be a string") + .to_owned(); + world.remembered_task_id = Some(task_id); +} + +#[then(expr = "the task list contains the remembered task with status {string}")] +fn task_list_contains_remembered_task_with_status(world: &mut CliWorld, status: String) { + let task_id = world + .remembered_task_id + .as_ref() + .expect("remembered task id exists") + .clone(); + let json = current_json(world); + assert_eq!(json["command"], "task list"); + let tasks = json["data"]["tasks"].as_array().expect("tasks array"); + let task = tasks + .iter() + .find(|task| task["id"].as_str() == Some(task_id.as_str())) + .unwrap_or_else(|| panic!("task list should contain {task_id}: {tasks:?}")); + assert_eq!(task["status"], status); +} + +#[then(expr = "the task cancel result has status {string} and changed true")] +fn task_cancel_result_changed_true(world: &mut CliWorld, status: String) { + let json = current_json(world); + assert_eq!(json["command"], "task cancel"); + assert_eq!(json["data"]["status"], status); + assert_eq!(json["data"]["changed"], true); +} + +#[then(expr = "the task cancel result has status {string} and changed false")] +fn task_cancel_result_changed_false(world: &mut CliWorld, status: String) { + let json = current_json(world); + assert_eq!(json["command"], "task cancel"); + assert_eq!(json["data"]["status"], status); + assert_eq!(json["data"]["changed"], false); +} + +#[then(expr = "the task run result has status {string} and install handoff {string}")] +fn task_run_result_has_status_and_install_handoff( + world: &mut CliWorld, + status: String, + handoff_status: String, +) { + let json = current_json(world); + assert_eq!(json["command"], "task run"); + assert_eq!(json["data"]["task"]["status"], status); + assert_eq!(json["data"]["install_handoff"]["status"], handoff_status); +} + +#[then(expr = "the task events output contains {int} events and has more events")] +fn task_events_output_contains_events_and_has_more(world: &mut CliWorld, count: usize) { + let json = current_json(world); + assert_eq!(json["command"], "task events"); + assert_eq!(json["data"]["events"].as_array().unwrap().len(), count); + assert_eq!(json["data"]["has_more"], true); +} + +#[then("I remember the next event cursor")] +fn remember_next_event_cursor(world: &mut CliWorld) { + let json = current_json(world); + world.remembered_event_cursor = Some( + json["data"]["next_cursor"] + .as_u64() + .expect("next cursor should be a u64"), + ); +} + +#[then(expr = "the task events output contains event {string}")] +fn task_events_output_contains_event(world: &mut CliWorld, kind: String) { + let json = current_json(world); + let events = json["data"]["events"].as_array().expect("events array"); + assert!( + events + .iter() + .any(|event| event["kind"].as_str() == Some(kind.as_str())), + "events should contain {kind}: {events:?}" + ); +} + +#[then("I remember the install handoff id")] +fn remember_install_handoff_id(world: &mut CliWorld) { + let json = current_json(world); + world.remembered_handoff_id = Some( + json["data"]["install_handoff"]["id"] + .as_str() + .expect("handoff id should be a string") + .to_owned(), + ); +} + +#[then(expr = "the install result output has status {string}")] +fn install_result_output_has_status(world: &mut CliWorld, status: String) { + let json = current_json(world); + assert_eq!(json["command"], "task install-result"); + assert_eq!(json["data"]["handoff"]["status"], status); +} + #[then("the output reports valid storage")] fn output_reports_valid_storage(world: &mut CliWorld) { let json = current_json(world); diff --git a/crates/getter-cli/tests/features/cli/task_lifecycle.feature b/crates/getter-cli/tests/features/cli/task_lifecycle.feature new file mode 100644 index 0000000..6927548 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/task_lifecycle.feature @@ -0,0 +1,89 @@ +@getter-cli @task +Feature: Offline task lifecycle + Scenario: User submits and lists an offline fake download task + Given an initialized getter data directory + And an offline download request for package "android/org.fdroid.fdroid" + When I run getter task submit for that request + Then the command succeeds + And the output is valid JSON + And I remember the submitted task id + When I run getter task list + Then the command succeeds + And the task list contains the remembered task with status "queued" + + Scenario: User cancels a queued offline task + Given an initialized getter data directory + And an offline download request for package "android/org.fdroid.fdroid" + When I run getter task submit for that request + Then the command succeeds + And I remember the submitted task id + When I run getter task cancel for the remembered task + Then the command succeeds + And the task cancel result has status "canceled" and changed true + When I run getter task cancel for the remembered task + Then the command succeeds + And the task cancel result has status "canceled" and changed false + + Scenario: User cannot cancel a succeeded offline task + Given an initialized getter data directory + And an offline download request for package "android/org.fdroid.fdroid" + When I run getter task submit for that request + Then the command succeeds + And I remember the submitted task id + When I run getter task run for the remembered task + Then the command succeeds + And the task run result has status "succeeded" and install handoff "requested" + When I run getter task cancel for the remembered task + Then the command fails with a download task error + + Scenario: User polls offline task events with cursor and limit + Given an initialized getter data directory + And an offline download request for package "android/org.fdroid.fdroid" + When I run getter task submit for that request + Then the command succeeds + And I remember the submitted task id + When I run getter task run for the remembered task + Then the command succeeds + When I run getter task events after 0 limit 2 + Then the command succeeds + And the task events output contains 2 events and has more events + And I remember the next event cursor + When I run getter task events after the remembered cursor limit 10 + Then the command succeeds + And the task events output contains event "install_handoff_requested" + + Scenario: User records an offline install handoff result + Given an initialized getter data directory + And an offline download request for package "android/org.fdroid.fdroid" + When I run getter task submit for that request + Then the command succeeds + And I remember the submitted task id + When I run getter task run for the remembered task + Then the command succeeds + And I remember the install handoff id + When I run getter task install-result "succeeded" for the remembered handoff + Then the command succeeds + And the install result output has status "succeeded" + + Scenario: User cannot record getter-created requested state as an install result + Given an initialized getter data directory + And an offline download request for package "android/org.fdroid.fdroid" + When I run getter task submit for that request + Then the command succeeds + And I remember the submitted task id + When I run getter task run for the remembered task + Then the command succeeds + And I remember the install handoff id + When I run getter task install-result "requested" for the remembered handoff + Then the command fails with a CLI usage error + + Scenario: User cannot poll task events with a zero limit + Given an initialized getter data directory + When I run getter task events after 0 limit 0 + Then the command fails with a CLI usage error + + Scenario: User receives structured errors for malformed task requests + Given an initialized getter data directory + And a malformed offline download request + When I run getter task submit for that request + Then the command fails with a download task error diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 642e36f..aa3bb41 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -13,6 +13,7 @@ pub mod autogen; pub mod diagnostics; pub mod lua; pub mod repository; +pub mod task; pub mod update; /// Error returned when parsing or constructing a [`PackageId`]. diff --git a/crates/getter-core/src/task.rs b/crates/getter-core/src/task.rs new file mode 100644 index 0000000..d69f4f4 --- /dev/null +++ b/crates/getter-core/src/task.rs @@ -0,0 +1,266 @@ +//! Offline task lifecycle DTOs for getter-owned download/install workflows. +//! +//! These types are transport/domain shapes. They describe getter task state, +//! pollable events, and abstract platform install handoffs without performing +//! live network downloads or Android installation. + +use crate::{PackageId, UpdateAction}; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +pub const DOWNLOAD_REQUEST_FORMAT: &str = "getter-download-request"; +pub const DOWNLOAD_REQUEST_VERSION: u32 = 1; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DownloadTaskRequest { + pub format: String, + pub version: u32, + pub package_id: PackageId, + #[serde(default)] + pub actions: Vec, + pub executor: TaskExecutor, +} + +impl DownloadTaskRequest { + pub fn download_action(&self) -> Option<&UpdateAction> { + self.actions + .iter() + .find(|action| matches!(action, UpdateAction::Download { .. })) + } + + pub fn install_action(&self) -> Option<&UpdateAction> { + self.actions + .iter() + .find(|action| matches!(action, UpdateAction::Install { .. })) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TaskExecutor { + Fake, +} + +impl TaskExecutor { + pub const fn as_str(self) -> &'static str { + match self { + Self::Fake => "fake", + } + } +} + +impl FromStr for TaskExecutor { + type Err = TaskModelError; + + fn from_str(value: &str) -> Result { + match value { + "fake" => Ok(Self::Fake), + other => Err(TaskModelError::InvalidExecutor(other.to_owned())), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum DownloadTaskStatus { + Queued, + Running, + Succeeded, + Failed, + Canceled, +} + +impl DownloadTaskStatus { + pub const fn as_str(self) -> &'static str { + match self { + Self::Queued => "queued", + Self::Running => "running", + Self::Succeeded => "succeeded", + Self::Failed => "failed", + Self::Canceled => "canceled", + } + } + + pub const fn is_terminal(self) -> bool { + matches!(self, Self::Succeeded | Self::Failed | Self::Canceled) + } +} + +impl FromStr for DownloadTaskStatus { + type Err = TaskModelError; + + fn from_str(value: &str) -> Result { + match value { + "queued" => Ok(Self::Queued), + "running" => Ok(Self::Running), + "succeeded" => Ok(Self::Succeeded), + "failed" => Ok(Self::Failed), + "canceled" => Ok(Self::Canceled), + other => Err(TaskModelError::InvalidTaskStatus(other.to_owned())), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TaskEventKind { + TaskCreated, + TaskStarted, + TaskSucceeded, + TaskFailed, + TaskCanceled, + InstallHandoffRequested, + InstallResultRecorded, +} + +impl TaskEventKind { + pub const fn as_str(self) -> &'static str { + match self { + Self::TaskCreated => "task_created", + Self::TaskStarted => "task_started", + Self::TaskSucceeded => "task_succeeded", + Self::TaskFailed => "task_failed", + Self::TaskCanceled => "task_canceled", + Self::InstallHandoffRequested => "install_handoff_requested", + Self::InstallResultRecorded => "install_result_recorded", + } + } +} + +impl FromStr for TaskEventKind { + type Err = TaskModelError; + + fn from_str(value: &str) -> Result { + match value { + "task_created" => Ok(Self::TaskCreated), + "task_started" => Ok(Self::TaskStarted), + "task_succeeded" => Ok(Self::TaskSucceeded), + "task_failed" => Ok(Self::TaskFailed), + "task_canceled" => Ok(Self::TaskCanceled), + "install_handoff_requested" => Ok(Self::InstallHandoffRequested), + "install_result_recorded" => Ok(Self::InstallResultRecorded), + other => Err(TaskModelError::InvalidEventKind(other.to_owned())), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DownloadTaskSummary { + pub id: String, + pub package_id: PackageId, + pub status: DownloadTaskStatus, + pub executor: TaskExecutor, + pub actions: Vec, + pub download_file_name: String, + #[serde(default)] + pub downloaded_file: Option, + #[serde(default)] + pub failure_message: Option, + #[serde(default)] + pub install_handoff_id: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskEvent { + pub cursor: u64, + pub task_id: String, + pub kind: TaskEventKind, + #[serde(default)] + pub status: Option, + #[serde(default)] + pub message: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskEventPage { + pub events: Vec, + pub next_cursor: u64, + pub has_more: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskCancelResult { + pub task_id: String, + pub status: DownloadTaskStatus, + pub changed: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskRunResult { + pub task: DownloadTaskSummary, + #[serde(default)] + pub install_handoff: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskSubmitResult { + pub task: DownloadTaskSummary, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct InstallResultRecord { + pub handoff: InstallHandoffSummary, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum InstallHandoffStatus { + Requested, + Accepted, + Succeeded, + Failed, + Canceled, +} + +impl InstallHandoffStatus { + pub const fn as_str(self) -> &'static str { + match self { + Self::Requested => "requested", + Self::Accepted => "accepted", + Self::Succeeded => "succeeded", + Self::Failed => "failed", + Self::Canceled => "canceled", + } + } +} + +impl FromStr for InstallHandoffStatus { + type Err = TaskModelError; + + fn from_str(value: &str) -> Result { + match value { + "requested" => Ok(Self::Requested), + "accepted" => Ok(Self::Accepted), + "succeeded" => Ok(Self::Succeeded), + "failed" => Ok(Self::Failed), + "canceled" => Ok(Self::Canceled), + other => Err(TaskModelError::InvalidInstallHandoffStatus( + other.to_owned(), + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct InstallHandoffSummary { + pub id: String, + pub task_id: String, + pub package_id: PackageId, + pub installer: String, + pub file: String, + pub status: InstallHandoffStatus, + #[serde(default)] + pub message: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum TaskModelError { + #[error("invalid task executor '{0}'")] + InvalidExecutor(String), + #[error("invalid download task status '{0}'")] + InvalidTaskStatus(String), + #[error("invalid task event kind '{0}'")] + InvalidEventKind(String), + #[error("invalid install handoff status '{0}'")] + InvalidInstallHandoffStatus(String), +} diff --git a/crates/getter-downloader/Cargo.toml b/crates/getter-downloader/Cargo.toml index d8b2562..8cf48c4 100644 --- a/crates/getter-downloader/Cargo.toml +++ b/crates/getter-downloader/Cargo.toml @@ -5,3 +5,5 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core" } +getter-storage = { path = "../getter-storage" } +thiserror = "1" diff --git a/crates/getter-downloader/src/lib.rs b/crates/getter-downloader/src/lib.rs index 4a11c7d..52318c5 100644 --- a/crates/getter-downloader/src/lib.rs +++ b/crates/getter-downloader/src/lib.rs @@ -1,3 +1,167 @@ -//! getter-downloader rewrite crate skeleton. +//! Deterministic offline downloader lifecycle for the getter rewrite. +//! +//! This crate intentionally does not perform live network I/O. It proves the +//! getter-owned task state, cancellation, pollable events, and install-handoff +//! contract with an explicit fake executor. + +use getter_core::task::{ + DownloadTaskRequest, InstallHandoffStatus, InstallResultRecord, TaskCancelResult, + TaskRunResult, TaskSubmitResult, DOWNLOAD_REQUEST_FORMAT, DOWNLOAD_REQUEST_VERSION, +}; +use getter_storage::{MainDb, StorageError}; pub use getter_core as core; + +#[derive(Debug, thiserror::Error)] +pub enum DownloaderError { + #[error("unsupported download request format '{0}'")] + UnsupportedFormat(String), + #[error("unsupported download request version {found}; expected {expected}")] + UnsupportedVersion { found: u32, expected: u32 }, + #[error("download request must use the fake executor for this offline slice")] + UnsupportedExecutor, + #[error("download request must include a download action")] + MissingDownloadAction, + #[error("storage error: {0}")] + Storage(#[from] StorageError), +} + +pub fn submit_fake_download_task( + db: &MainDb, + request: DownloadTaskRequest, +) -> Result { + validate_fake_request(&request)?; + let task = db.create_download_task(&request)?; + Ok(TaskSubmitResult { task }) +} + +pub fn run_fake_download_task( + db: &MainDb, + task_id: &str, +) -> Result { + db.start_download_task(task_id)?; + let task = db.succeed_download_task(task_id)?; + let install_handoff = db.create_install_handoff_for_task(task_id)?; + Ok(TaskRunResult { + task, + install_handoff, + }) +} + +pub fn cancel_download_task( + db: &MainDb, + task_id: &str, +) -> Result { + Ok(db.cancel_download_task(task_id)?) +} + +pub fn record_install_result( + db: &MainDb, + handoff_id: &str, + status: InstallHandoffStatus, +) -> Result { + let handoff = db.record_install_result(handoff_id, status, None)?; + Ok(InstallResultRecord { handoff }) +} + +fn validate_fake_request(request: &DownloadTaskRequest) -> Result<(), DownloaderError> { + if request.format != DOWNLOAD_REQUEST_FORMAT { + return Err(DownloaderError::UnsupportedFormat(request.format.clone())); + } + if request.version != DOWNLOAD_REQUEST_VERSION { + return Err(DownloaderError::UnsupportedVersion { + found: request.version, + expected: DOWNLOAD_REQUEST_VERSION, + }); + } + // This match is intentionally explicit so adding a real executor later forces + // this offline-only validation point to be revisited. + match request.executor { + getter_core::task::TaskExecutor::Fake => {} + } + if request.download_action().is_none() { + return Err(DownloaderError::MissingDownloadAction); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::task::{DownloadTaskStatus, TaskEventKind, TaskExecutor}; + use getter_core::{PackageId, UpdateAction}; + + #[test] + fn fake_submit_run_records_install_handoff_and_events() { + let db = MainDb::open_in_memory().unwrap(); + let submitted = submit_fake_download_task(&db, request()).unwrap(); + assert_eq!(submitted.task.id, "task-1"); + assert_eq!(submitted.task.status, DownloadTaskStatus::Queued); + + let result = run_fake_download_task(&db, &submitted.task.id).unwrap(); + assert_eq!(result.task.status, DownloadTaskStatus::Succeeded); + assert_eq!( + result.install_handoff.as_ref().unwrap().installer, + "android_package" + ); + + let events = db.task_events_after(0, 10).unwrap().events; + assert_eq!(events[0].kind, TaskEventKind::TaskCreated); + assert_eq!(events[1].kind, TaskEventKind::TaskStarted); + assert_eq!(events[2].kind, TaskEventKind::TaskSucceeded); + assert_eq!(events[3].kind, TaskEventKind::InstallHandoffRequested); + } + + #[test] + fn fake_cancel_before_run_is_persisted() { + let db = MainDb::open_in_memory().unwrap(); + let submitted = submit_fake_download_task(&db, request()).unwrap(); + + let canceled = cancel_download_task(&db, &submitted.task.id).unwrap(); + assert!(canceled.changed); + assert_eq!(canceled.status, DownloadTaskStatus::Canceled); + assert!(run_fake_download_task(&db, &submitted.task.id).is_err()); + } + + #[test] + fn fake_submit_rejects_wrong_contract() { + let db = MainDb::open_in_memory().unwrap(); + let mut request = request(); + request.format = "wrong".to_owned(); + + let error = submit_fake_download_task(&db, request).unwrap_err(); + assert!(matches!(error, DownloaderError::UnsupportedFormat(_))); + } + + #[test] + fn install_result_can_be_recorded_after_handoff() { + let db = MainDb::open_in_memory().unwrap(); + let submitted = submit_fake_download_task(&db, request()).unwrap(); + let result = run_fake_download_task(&db, &submitted.task.id).unwrap(); + let handoff = result.install_handoff.unwrap(); + + let recorded = + record_install_result(&db, &handoff.id, InstallHandoffStatus::Succeeded).unwrap(); + assert_eq!(recorded.handoff.status, InstallHandoffStatus::Succeeded); + } + + fn request() -> DownloadTaskRequest { + DownloadTaskRequest { + format: DOWNLOAD_REQUEST_FORMAT.to_owned(), + version: DOWNLOAD_REQUEST_VERSION, + package_id: PackageId::new(getter_core::PackageKind::Android, "org.fdroid.fdroid") + .unwrap(), + executor: TaskExecutor::Fake, + actions: vec![ + UpdateAction::Download { + url: "https://example.invalid/app.apk".to_owned(), + file_name: "app.apk".to_owned(), + }, + UpdateAction::Install { + installer: "android_package".to_owned(), + file: "app.apk".to_owned(), + }, + ], + } + } +} diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index f1285c2..23b0e8a 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -3,8 +3,13 @@ pub mod legacy_room; use getter_core::repository::RepositoryMetadata; -use getter_core::{PackageId, RepositoryId, RepositoryPriority}; -use rusqlite::{params, Connection, Params, Transaction}; +use getter_core::task::{ + DownloadTaskRequest, DownloadTaskStatus, DownloadTaskSummary, InstallHandoffStatus, + InstallHandoffSummary, TaskCancelResult, TaskEvent, TaskEventKind, TaskEventPage, + TaskModelError, +}; +use getter_core::{PackageId, RepositoryId, RepositoryPriority, UpdateAction}; +use rusqlite::{params, Connection, OptionalExtension, Params, Transaction}; use std::path::Path; use std::str::FromStr; @@ -18,6 +23,20 @@ pub enum StorageError { RepositoryId(#[from] getter_core::RepositoryIdError), #[error("invalid package resolution in database: {0}")] PackageResolution(String), + #[error("invalid task state in database: {0}")] + TaskState(String), + #[error("download task not found: {0}")] + TaskNotFound(String), + #[error("install handoff not found: {0}")] + InstallHandoffNotFound(String), + #[error("invalid install handoff transition for {handoff_id}: {reason}")] + InvalidInstallHandoffTransition { handoff_id: String, reason: String }, + #[error("invalid task transition for {task_id}: {reason}")] + InvalidTaskTransition { task_id: String, reason: String }, + #[error("invalid task request: {0}")] + InvalidTaskRequest(String), + #[error("JSON error: {0}")] + Json(#[from] serde_json::Error), } pub struct MainDb { @@ -78,6 +97,43 @@ CREATE TABLE IF NOT EXISTS migration_records ( completed_at_unix INTEGER NOT NULL DEFAULT (unixepoch()), report_json TEXT NOT NULL DEFAULT '{}' ); + +CREATE TABLE IF NOT EXISTS download_tasks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_id TEXT UNIQUE, + package_id TEXT NOT NULL, + status TEXT NOT NULL, + executor TEXT NOT NULL, + actions_json TEXT NOT NULL, + download_file_name TEXT NOT NULL, + downloaded_file TEXT, + failure_message TEXT, + install_handoff_id TEXT, + created_at_unix INTEGER NOT NULL DEFAULT (unixepoch()), + updated_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); + +CREATE TABLE IF NOT EXISTS task_events ( + cursor INTEGER PRIMARY KEY AUTOINCREMENT, + task_id TEXT NOT NULL, + kind TEXT NOT NULL, + status TEXT, + message TEXT, + created_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); + +CREATE TABLE IF NOT EXISTS install_handoffs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + handoff_id TEXT UNIQUE, + task_id TEXT NOT NULL, + package_id TEXT NOT NULL, + installer TEXT NOT NULL, + file TEXT NOT NULL, + status TEXT NOT NULL, + message TEXT, + created_at_unix INTEGER NOT NULL DEFAULT (unixepoch()), + updated_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); "#, )?; self.ensure_column("tracked_packages", "ignored_version", "TEXT")?; @@ -90,6 +146,10 @@ CREATE TABLE IF NOT EXISTS migration_records ( "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('main-v1')", [], )?; + self.conn.execute( + "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('main-task-v1')", + [], + )?; Ok(()) } @@ -345,6 +405,375 @@ ON CONFLICT(id) DO UPDATE SET } Ok(records) } + + pub fn create_download_task( + &self, + request: &DownloadTaskRequest, + ) -> Result { + let download_file_name = match request.download_action() { + Some(UpdateAction::Download { file_name, .. }) => file_name.clone(), + _ => { + return Err(StorageError::InvalidTaskRequest( + "download task request must include a download action".to_owned(), + )) + } + }; + let actions_json = serde_json::to_string(&request.actions)?; + let tx = self.conn.unchecked_transaction()?; + tx.execute( + r#" +INSERT INTO download_tasks(package_id, status, executor, actions_json, download_file_name) +VALUES (?1, ?2, ?3, ?4, ?5) +"#, + params![ + request.package_id.to_string(), + DownloadTaskStatus::Queued.as_str(), + request.executor.as_str(), + actions_json, + download_file_name, + ], + )?; + let row_id = tx.last_insert_rowid(); + let task_id = format!("task-{row_id}"); + tx.execute( + "UPDATE download_tasks SET task_id = ?1 WHERE id = ?2", + params![task_id, row_id], + )?; + insert_task_event_with_executor( + &tx, + &task_id, + TaskEventKind::TaskCreated, + Some(DownloadTaskStatus::Queued), + Some("Task queued"), + )?; + tx.commit()?; + self.download_task(&task_id) + } + + pub fn download_task(&self, task_id: &str) -> Result { + let mut stmt = self.conn.prepare( + r#" +SELECT task_id, package_id, status, executor, actions_json, download_file_name, + downloaded_file, failure_message, install_handoff_id +FROM download_tasks +WHERE task_id = ?1 +"#, + )?; + let row = stmt + .query_row([task_id], |row| { + Ok(DownloadTaskRow { + task_id: row.get(0)?, + package_id: row.get(1)?, + status: row.get(2)?, + executor: row.get(3)?, + actions_json: row.get(4)?, + download_file_name: row.get(5)?, + downloaded_file: row.get(6)?, + failure_message: row.get(7)?, + install_handoff_id: row.get(8)?, + }) + }) + .optional()?; + row.map(download_task_from_row) + .transpose()? + .ok_or_else(|| StorageError::TaskNotFound(task_id.to_owned())) + } + + pub fn download_tasks(&self) -> Result, StorageError> { + let mut stmt = self.conn.prepare( + r#" +SELECT task_id, package_id, status, executor, actions_json, download_file_name, + downloaded_file, failure_message, install_handoff_id +FROM download_tasks +ORDER BY id ASC +"#, + )?; + let rows = stmt.query_map([], |row| { + Ok(DownloadTaskRow { + task_id: row.get(0)?, + package_id: row.get(1)?, + status: row.get(2)?, + executor: row.get(3)?, + actions_json: row.get(4)?, + download_file_name: row.get(5)?, + downloaded_file: row.get(6)?, + failure_message: row.get(7)?, + install_handoff_id: row.get(8)?, + }) + })?; + let mut tasks = Vec::new(); + for row in rows { + tasks.push(download_task_from_row(row?)?); + } + Ok(tasks) + } + + pub fn start_download_task(&self, task_id: &str) -> Result { + let task = self.download_task(task_id)?; + match task.status { + DownloadTaskStatus::Queued => { + self.update_download_task_status( + task_id, + DownloadTaskStatus::Running, + None, + None, + TaskEventKind::TaskStarted, + "Task started", + )?; + self.download_task(task_id) + } + DownloadTaskStatus::Running => Ok(task), + status => Err(StorageError::InvalidTaskTransition { + task_id: task_id.to_owned(), + reason: format!("cannot start task with status {}", status.as_str()), + }), + } + } + + pub fn succeed_download_task( + &self, + task_id: &str, + ) -> Result { + let task = self.download_task(task_id)?; + match task.status { + DownloadTaskStatus::Queued | DownloadTaskStatus::Running => { + self.update_download_task_status( + task_id, + DownloadTaskStatus::Succeeded, + Some(&task.download_file_name), + None, + TaskEventKind::TaskSucceeded, + "Task succeeded", + )?; + self.download_task(task_id) + } + status => Err(StorageError::InvalidTaskTransition { + task_id: task_id.to_owned(), + reason: format!("cannot succeed task with status {}", status.as_str()), + }), + } + } + + pub fn cancel_download_task(&self, task_id: &str) -> Result { + let task = self.download_task(task_id)?; + match task.status { + DownloadTaskStatus::Queued | DownloadTaskStatus::Running => { + self.update_download_task_status( + task_id, + DownloadTaskStatus::Canceled, + None, + None, + TaskEventKind::TaskCanceled, + "Task canceled", + )?; + Ok(TaskCancelResult { + task_id: task_id.to_owned(), + status: DownloadTaskStatus::Canceled, + changed: true, + }) + } + DownloadTaskStatus::Canceled => Ok(TaskCancelResult { + task_id: task_id.to_owned(), + status: DownloadTaskStatus::Canceled, + changed: false, + }), + status => Err(StorageError::InvalidTaskTransition { + task_id: task_id.to_owned(), + reason: format!("cannot cancel task with status {}", status.as_str()), + }), + } + } + + pub fn create_install_handoff_for_task( + &self, + task_id: &str, + ) -> Result, StorageError> { + let task = self.download_task(task_id)?; + if task.status != DownloadTaskStatus::Succeeded { + return Err(StorageError::InvalidTaskTransition { + task_id: task_id.to_owned(), + reason: format!( + "cannot request install handoff for task with status {}", + task.status.as_str() + ), + }); + } + if let Some(existing) = task.install_handoff_id.as_deref() { + return Ok(Some(self.install_handoff(existing)?)); + } + let Some(UpdateAction::Install { installer, file }) = task + .actions + .iter() + .find(|action| matches!(action, UpdateAction::Install { .. })) + else { + return Ok(None); + }; + let tx = self.conn.unchecked_transaction()?; + tx.execute( + r#" +INSERT INTO install_handoffs(task_id, package_id, installer, file, status) +VALUES (?1, ?2, ?3, ?4, ?5) +"#, + params![ + task_id, + task.package_id.to_string(), + installer, + file, + InstallHandoffStatus::Requested.as_str(), + ], + )?; + let row_id = tx.last_insert_rowid(); + let handoff_id = format!("handoff-{row_id}"); + tx.execute( + "UPDATE install_handoffs SET handoff_id = ?1 WHERE id = ?2", + params![handoff_id, row_id], + )?; + tx.execute( + "UPDATE download_tasks SET install_handoff_id = ?1, updated_at_unix = unixepoch() WHERE task_id = ?2", + params![handoff_id, task_id], + )?; + insert_task_event_with_executor( + &tx, + task_id, + TaskEventKind::InstallHandoffRequested, + Some(task.status), + Some("Install handoff requested"), + )?; + tx.commit()?; + Ok(Some(self.install_handoff(&handoff_id)?)) + } + + pub fn install_handoff(&self, handoff_id: &str) -> Result { + let mut stmt = self.conn.prepare( + r#" +SELECT handoff_id, task_id, package_id, installer, file, status, message +FROM install_handoffs +WHERE handoff_id = ?1 +"#, + )?; + let row = stmt + .query_row([handoff_id], |row| { + Ok(InstallHandoffRow { + handoff_id: row.get(0)?, + task_id: row.get(1)?, + package_id: row.get(2)?, + installer: row.get(3)?, + file: row.get(4)?, + status: row.get(5)?, + message: row.get(6)?, + }) + }) + .optional()?; + row.map(install_handoff_from_row) + .transpose()? + .ok_or_else(|| StorageError::InstallHandoffNotFound(handoff_id.to_owned())) + } + + pub fn record_install_result( + &self, + handoff_id: &str, + status: InstallHandoffStatus, + message: Option<&str>, + ) -> Result { + if status == InstallHandoffStatus::Requested { + return Err(StorageError::InvalidInstallHandoffTransition { + handoff_id: handoff_id.to_owned(), + reason: "requested is getter-created state, not a platform install result" + .to_owned(), + }); + } + let handoff = self.install_handoff(handoff_id)?; + let tx = self.conn.unchecked_transaction()?; + tx.execute( + r#" +UPDATE install_handoffs +SET status = ?1, message = ?2, updated_at_unix = unixepoch() +WHERE handoff_id = ?3 +"#, + params![status.as_str(), message, handoff_id], + )?; + insert_task_event_with_executor( + &tx, + &handoff.task_id, + TaskEventKind::InstallResultRecorded, + None, + Some("Install result recorded"), + )?; + tx.commit()?; + self.install_handoff(handoff_id) + } + + pub fn task_events_after( + &self, + after: u64, + limit: usize, + ) -> Result { + let fetch_limit = limit.saturating_add(1) as i64; + let mut stmt = self.conn.prepare( + r#" +SELECT cursor, task_id, kind, status, message +FROM task_events +WHERE cursor > ?1 +ORDER BY cursor ASC +LIMIT ?2 +"#, + )?; + let rows = stmt.query_map(params![after as i64, fetch_limit], |row| { + Ok(TaskEventRow { + cursor: row.get(0)?, + task_id: row.get(1)?, + kind: row.get(2)?, + status: row.get(3)?, + message: row.get(4)?, + }) + })?; + let mut events = Vec::new(); + for row in rows { + events.push(task_event_from_row(row?)?); + } + let has_more = events.len() > limit; + if has_more { + events.truncate(limit); + } + let next_cursor = events.last().map(|event| event.cursor).unwrap_or(after); + Ok(TaskEventPage { + events, + next_cursor, + has_more, + }) + } + + fn update_download_task_status( + &self, + task_id: &str, + status: DownloadTaskStatus, + downloaded_file: Option<&str>, + failure_message: Option<&str>, + event_kind: TaskEventKind, + event_message: &str, + ) -> Result<(), StorageError> { + let tx = self.conn.unchecked_transaction()?; + tx.execute( + r#" +UPDATE download_tasks +SET status = ?1, + downloaded_file = COALESCE(?2, downloaded_file), + failure_message = ?3, + updated_at_unix = unixepoch() +WHERE task_id = ?4 +"#, + params![status.as_str(), downloaded_file, failure_message, task_id], + )?; + insert_task_event_with_executor( + &tx, + task_id, + event_kind, + Some(status), + Some(event_message), + )?; + tx.commit()?; + Ok(()) + } } impl CacheDb { @@ -435,6 +864,36 @@ pub struct StoredMigrationRecord { pub report_json: String, } +struct DownloadTaskRow { + task_id: String, + package_id: String, + status: String, + executor: String, + actions_json: String, + download_file_name: String, + downloaded_file: Option, + failure_message: Option, + install_handoff_id: Option, +} + +struct TaskEventRow { + cursor: i64, + task_id: String, + kind: String, + status: Option, + message: Option, +} + +struct InstallHandoffRow { + handoff_id: String, + task_id: String, + package_id: String, + installer: String, + file: String, + status: String, + message: Option, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MigrationRecordUpsert<'a> { pub id: &'a str, @@ -529,10 +988,84 @@ fn bool_to_i64(value: bool) -> i64 { } } +fn download_task_from_row(row: DownloadTaskRow) -> Result { + Ok(DownloadTaskSummary { + id: row.task_id, + package_id: row.package_id.parse()?, + status: row + .status + .parse() + .map_err(|error: TaskModelError| StorageError::TaskState(error.to_string()))?, + executor: row + .executor + .parse() + .map_err(|error: TaskModelError| StorageError::TaskState(error.to_string()))?, + actions: serde_json::from_str(&row.actions_json)?, + download_file_name: row.download_file_name, + downloaded_file: row.downloaded_file, + failure_message: row.failure_message, + install_handoff_id: row.install_handoff_id, + }) +} + +fn task_event_from_row(row: TaskEventRow) -> Result { + Ok(TaskEvent { + cursor: row.cursor as u64, + task_id: row.task_id, + kind: row + .kind + .parse() + .map_err(|error: TaskModelError| StorageError::TaskState(error.to_string()))?, + status: row + .status + .map(|status| status.parse()) + .transpose() + .map_err(|error: TaskModelError| StorageError::TaskState(error.to_string()))?, + message: row.message, + }) +} + +fn install_handoff_from_row(row: InstallHandoffRow) -> Result { + Ok(InstallHandoffSummary { + id: row.handoff_id, + task_id: row.task_id, + package_id: row.package_id.parse()?, + installer: row.installer, + file: row.file, + status: row + .status + .parse() + .map_err(|error: TaskModelError| StorageError::TaskState(error.to_string()))?, + message: row.message, + }) +} + +fn insert_task_event_with_executor( + conn: &impl SqlExecutor, + task_id: &str, + kind: TaskEventKind, + status: Option, + message: Option<&str>, +) -> Result { + conn.execute_statement( + r#" +INSERT INTO task_events(task_id, kind, status, message) +VALUES (?1, ?2, ?3, ?4) +"#, + params![ + task_id, + kind.as_str(), + status.map(DownloadTaskStatus::as_str), + message, + ], + ) +} + #[cfg(test)] mod tests { use super::*; use getter_core::repository::REPO_API_VERSION_V1; + use getter_core::task::TaskExecutor; #[test] fn main_db_stores_repository_registry_ordered_by_priority() { @@ -720,6 +1253,143 @@ mod tests { assert!(db.migration_record_exists("legacy-room-v17").unwrap()); } + #[test] + fn task_lifecycle_persists_across_reopen() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("main.db"); + { + let db = MainDb::open(&db_path).unwrap(); + let task = db.create_download_task(&download_request()).unwrap(); + assert_eq!(task.id, "task-1"); + assert_eq!(task.status, DownloadTaskStatus::Queued); + } + + let db = MainDb::open(&db_path).unwrap(); + let tasks = db.download_tasks().unwrap(); + assert_eq!(tasks.len(), 1); + assert_eq!(tasks[0].id, "task-1"); + assert_eq!(tasks[0].package_id.to_string(), "android/org.fdroid.fdroid"); + assert_eq!(tasks[0].status, DownloadTaskStatus::Queued); + } + + #[test] + fn task_cancel_is_persistent_and_idempotent_before_terminal_state() { + let db = MainDb::open_in_memory().unwrap(); + let task = db.create_download_task(&download_request()).unwrap(); + + let first = db.cancel_download_task(&task.id).unwrap(); + assert!(first.changed); + assert_eq!(first.status, DownloadTaskStatus::Canceled); + let second = db.cancel_download_task(&task.id).unwrap(); + assert!(!second.changed); + assert_eq!( + db.download_task(&task.id).unwrap().status, + DownloadTaskStatus::Canceled + ); + } + + #[test] + fn task_cancel_after_success_is_rejected() { + let db = MainDb::open_in_memory().unwrap(); + let task = db.create_download_task(&download_request()).unwrap(); + db.start_download_task(&task.id).unwrap(); + db.succeed_download_task(&task.id).unwrap(); + + let error = db.cancel_download_task(&task.id).unwrap_err(); + assert!(matches!(error, StorageError::InvalidTaskTransition { .. })); + } + + #[test] + fn task_events_are_pollable_with_cursor_and_limit() { + let db = MainDb::open_in_memory().unwrap(); + let task = db.create_download_task(&download_request()).unwrap(); + db.start_download_task(&task.id).unwrap(); + db.succeed_download_task(&task.id).unwrap(); + + let first_page = db.task_events_after(0, 2).unwrap(); + assert_eq!(first_page.events.len(), 2); + assert!(first_page.has_more); + assert_eq!(first_page.events[0].kind, TaskEventKind::TaskCreated); + assert_eq!(first_page.events[1].kind, TaskEventKind::TaskStarted); + + let second_page = db.task_events_after(first_page.next_cursor, 2).unwrap(); + assert_eq!(second_page.events.len(), 1); + assert!(!second_page.has_more); + assert_eq!(second_page.events[0].kind, TaskEventKind::TaskSucceeded); + } + + #[test] + fn install_handoff_result_is_recorded() { + let db = MainDb::open_in_memory().unwrap(); + let task = db.create_download_task(&download_request()).unwrap(); + db.start_download_task(&task.id).unwrap(); + db.succeed_download_task(&task.id).unwrap(); + let handoff = db + .create_install_handoff_for_task(&task.id) + .unwrap() + .unwrap(); + assert_eq!(handoff.id, "handoff-1"); + assert_eq!(handoff.status, InstallHandoffStatus::Requested); + + let updated = db + .record_install_result(&handoff.id, InstallHandoffStatus::Succeeded, Some("ok")) + .unwrap(); + assert_eq!(updated.status, InstallHandoffStatus::Succeeded); + assert_eq!(updated.message.as_deref(), Some("ok")); + } + + #[test] + fn install_result_rejects_getter_created_requested_state() { + let db = MainDb::open_in_memory().unwrap(); + let task = db.create_download_task(&download_request()).unwrap(); + db.start_download_task(&task.id).unwrap(); + db.succeed_download_task(&task.id).unwrap(); + let handoff = db + .create_install_handoff_for_task(&task.id) + .unwrap() + .unwrap(); + + let error = db + .record_install_result(&handoff.id, InstallHandoffStatus::Requested, None) + .unwrap_err(); + assert!(matches!( + error, + StorageError::InvalidInstallHandoffTransition { .. } + )); + } + + #[test] + fn task_request_requires_download_action() { + let db = MainDb::open_in_memory().unwrap(); + let mut request = download_request(); + request.actions = vec![UpdateAction::Install { + installer: "android_package".to_owned(), + file: "app.apk".to_owned(), + }]; + + let error = db.create_download_task(&request).unwrap_err(); + assert!(matches!(error, StorageError::InvalidTaskRequest(_))); + } + + fn download_request() -> DownloadTaskRequest { + DownloadTaskRequest { + format: getter_core::task::DOWNLOAD_REQUEST_FORMAT.to_owned(), + version: getter_core::task::DOWNLOAD_REQUEST_VERSION, + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + executor: TaskExecutor::Fake, + actions: vec![ + UpdateAction::Download { + url: "https://example.invalid/app.apk".to_owned(), + file_name: "app.apk".to_owned(), + }, + UpdateAction::Install { + installer: "android_package".to_owned(), + file: "app.apk".to_owned(), + }, + ], + } + } + #[test] fn cache_db_migrates_schema() { let _db = CacheDb::open_in_memory().unwrap(); From cee3b007d5e03d9035f5319a582f452dfbc7c911 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 23 Jun 2026 23:57:34 +0800 Subject: [PATCH 17/71] feat: share installed autogen operations --- Cargo.lock | 12 + Cargo.toml | 7 +- crates/getter-cli/Cargo.toml | 1 + crates/getter-cli/src/lib.rs | 610 ++--------------------- crates/getter-core/Cargo.toml | 6 +- crates/getter-core/src/lib.rs | 2 + crates/getter-operations/Cargo.toml | 10 + crates/getter-operations/src/autogen.rs | 637 ++++++++++++++++++++++++ crates/getter-operations/src/lib.rs | 8 + crates/getter-storage/Cargo.toml | 2 +- src/lib.rs | 2 + 11 files changed, 714 insertions(+), 583 deletions(-) create mode 100644 crates/getter-operations/Cargo.toml create mode 100644 crates/getter-operations/src/autogen.rs create mode 100644 crates/getter-operations/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index a23b19e..1ff310d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -508,6 +508,7 @@ version = "0.1.0" dependencies = [ "getter-cli", "getter-core", + "getter-operations", "getter-storage", "rustls-platform-verifier", "thiserror 1.0.69", @@ -521,6 +522,7 @@ dependencies = [ "cucumber", "getter-core", "getter-downloader", + "getter-operations", "getter-storage", "rusqlite", "serde", @@ -558,6 +560,16 @@ dependencies = [ "getter-core", ] +[[package]] +name = "getter-operations" +version = "0.1.0" +dependencies = [ + "getter-core", + "getter-storage", + "serde_json", + "thiserror 1.0.69", +] + [[package]] name = "getter-plugin-api" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index e45af61..9347d01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "crates/getter-storage", "crates/getter-providers", "crates/getter-downloader", + "crates/getter-operations", "crates/getter-plugin-api", "crates/getter-rpc", "crates/getter-cli", @@ -22,7 +23,8 @@ edition = "2021" [features] default = ["cli", "domain"] -domain = ["dep:getter-core", "dep:getter-storage"] +domain = ["dep:getter-core", "dep:getter-storage", "dep:getter-operations"] +lua = ["getter-core/lua"] cli = ["dep:getter-cli"] rustls-platform-verifier = ["dep:rustls-platform-verifier"] rustls-platform-verifier-android = ["rustls-platform-verifier", "rustls-platform-verifier/jni"] @@ -30,8 +32,9 @@ webpki-roots = [] native-tokio = [] [dependencies] -getter-core = { path = "crates/getter-core", optional = true } +getter-core = { path = "crates/getter-core", default-features = false, optional = true } getter-storage = { path = "crates/getter-storage", optional = true } +getter-operations = { path = "crates/getter-operations", optional = true } getter-cli = { path = "crates/getter-cli", optional = true } thiserror = "1" tokio = { version = "1", features = ["net"] } diff --git a/crates/getter-cli/Cargo.toml b/crates/getter-cli/Cargo.toml index 248cb01..a5cec89 100644 --- a/crates/getter-cli/Cargo.toml +++ b/crates/getter-cli/Cargo.toml @@ -6,6 +6,7 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core" } getter-downloader = { path = "../getter-downloader" } +getter-operations = { path = "../getter-operations" } getter-storage = { path = "../getter-storage" } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 08d63d0..d9f4d47 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -4,15 +4,10 @@ //! while durable state is initialized through `getter-storage` so the command //! surface exercises the same Rust-owned SQLite direction used by embedders. -use getter_core::autogen::{ - content_hash, local_autogen_repo_toml, local_repo_toml, plan_local_autogen, AutogenManifest, - AutogenManifestEntry, AutogenPlan, AutogenSkipReason, InstalledInventory, - LOCAL_AUTOGEN_REPOSITORY_ID, LOCAL_AUTOGEN_REPOSITORY_NAME, LOCAL_REPOSITORY_ID, - LOCAL_REPOSITORY_NAME, -}; +use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; -use getter_core::repository::{RepositoryLayout, RepositoryMetadata, REPO_API_VERSION_V1}; +use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; use getter_core::task::{ DownloadTaskRequest, InstallHandoffStatus, TaskEventPage, DOWNLOAD_REQUEST_FORMAT, DOWNLOAD_REQUEST_VERSION, @@ -22,6 +17,7 @@ use getter_core::{PackageId, RepositoryId, RepositoryPriority}; use getter_downloader::{ cancel_download_task, record_install_result, run_fake_download_task, submit_fake_download_task, }; +use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; use getter_storage::legacy_room::{ map_legacy_app, read_legacy_room_database, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, LegacyRoomDbImport, LegacyRoomImportWarning, @@ -33,15 +29,13 @@ use getter_storage::{ }; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; -use std::collections::{BTreeSet, HashMap}; use std::fs; -use std::path::{Component, Path, PathBuf}; +use std::path::{Path, PathBuf}; const MAIN_DB_FILE: &str = "main.db"; const CACHE_DB_FILE: &str = "cache.db"; const MIGRATION_REPORTS_DIR: &str = "migration-reports"; const LEGACY_ROOM_MIGRATION_ID: &str = "legacy-room-v17"; -const AUTOGEN_MANIFEST_FILE: &str = "autogen-manifest.json"; #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliInvocation { @@ -115,12 +109,6 @@ pub enum CliCommand { LegacyReportList, } -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum AutogenAcceptance { - AcceptAll, - Accept(Vec), -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ExitCode { Success = 0, @@ -252,6 +240,16 @@ impl From for CliError { } } +impl From for CliError { + fn from(value: AutogenOperationError) -> Self { + match value { + AutogenOperationError::Storage(source) => Self::Storage(source.to_string()), + AutogenOperationError::Repository(detail) => Self::Repository(detail), + AutogenOperationError::Autogen(detail) => Self::Autogen(detail), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliOutput { pub exit_code: ExitCode, @@ -609,8 +607,8 @@ fn execute(invocation: CliInvocation) -> Result { CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; - let plan = build_local_autogen_plan(&db, &inventory)?; - Ok(autogen_installed_preview_json(&invocation.data_dir, &plan)) + let plan = autogen::build_local_autogen_plan(&db, &inventory)?; + Ok(autogen::installed_preview_json(&invocation.data_dir, &plan)) } CliCommand::AutogenInstalledApply { preview, @@ -618,12 +616,14 @@ fn execute(invocation: CliInvocation) -> Result { } => { let db = open_main_db(&invocation.data_dir)?; let preview = read_autogen_preview(&preview, "installed.preview")?; - apply_autogen_installed_preview(&invocation.data_dir, &db, &preview, &acceptance) + autogen::apply_installed_preview(&invocation.data_dir, &db, &preview, &acceptance) + .map_err(CliError::from) } CliCommand::AutogenCleanupPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; - cleanup_preview_json(&invocation.data_dir, &db, &inventory) + autogen::cleanup_preview_json(&invocation.data_dir, &db, &inventory) + .map_err(CliError::from) } CliCommand::AutogenCleanupApply { preview, @@ -631,7 +631,8 @@ fn execute(invocation: CliInvocation) -> Result { } => { let db = open_main_db(&invocation.data_dir)?; let preview = read_autogen_preview(&preview, "cleanup.preview")?; - apply_autogen_cleanup_preview(&invocation.data_dir, &db, &preview, &acceptance) + autogen::apply_cleanup_preview(&invocation.data_dir, &db, &preview, &acceptance) + .map_err(CliError::from) } CliCommand::LegacyImportRoomBundle { bundle } => { let db = open_main_db(&invocation.data_dir)?; @@ -921,6 +922,15 @@ fn read_installed_inventory(path: &Path) -> Result .map_err(|source| CliError::Autogen(format!("failed to parse inventory JSON: {source}"))) } +fn read_autogen_preview(path: &Path, expected_operation: &str) -> Result { + let bytes = fs::read(path) + .map_err(|source| CliError::Autogen(format!("failed to read autogen preview: {source}")))?; + let raw: Value = serde_json::from_slice(&bytes).map_err(|source| { + CliError::Autogen(format!("failed to parse autogen preview JSON: {source}")) + })?; + autogen::unwrap_preview_payload(raw, expected_operation).map_err(CliError::from) +} + fn read_update_check_fixture(path: &Path) -> Result { let bytes = fs::read(path).map_err(|source| { CliError::Update(format!("failed to read update check fixture: {source}")) @@ -953,564 +963,6 @@ fn read_download_task_request(path: &Path) -> Result Result { - let covered = higher_priority_package_coverage(db)?; - plan_local_autogen(inventory, &covered).map_err(|source| CliError::Autogen(source.to_string())) -} - -fn higher_priority_package_coverage( - db: &MainDb, -) -> Result, CliError> { - let mut covered = HashMap::new(); - for repo in db.repositories()? { - if repo.id.as_str() == LOCAL_AUTOGEN_REPOSITORY_ID { - continue; - } - if repo.priority <= RepositoryPriority::LOCAL_AUTOGEN { - continue; - } - let Some(path) = repo.path.as_ref() else { - continue; - }; - let layout = load_repository_layout(Path::new(path))?; - for package in layout.packages { - covered.entry(package.id).or_insert_with(|| repo.id.clone()); - } - } - Ok(covered) -} - -fn autogen_installed_preview_json(data_dir: &Path, plan: &AutogenPlan) -> Value { - let candidates: Vec = plan.candidates.iter().map(autogen_candidate_json).collect(); - let skipped: Vec = plan.skipped.iter().map(autogen_skip_json).collect(); - json!({ - "operation": "installed.preview", - "target_repo_id": plan.repository_id.as_str(), - "target_repo_path": local_autogen_repo_path(data_dir), - "summary": { - "candidate_count": candidates.len(), - "skipped_count": skipped.len(), - "write_count": candidates.len(), - "delete_count": 0, - }, - "candidates": candidates, - "skipped": skipped, - "diagnostics": [], - }) -} - -fn autogen_candidate_json(candidate: &getter_core::autogen::AutogenCandidate) -> Value { - json!({ - "package_id": candidate.package_id.to_string(), - "kind": candidate.package_id.kind().as_str(), - "display_name": candidate.name, - "installed_target": candidate.installed, - "action": "create", - "output_relative_path": candidate.relative_path, - "content_hash": candidate.content_hash, - "content": candidate.content, - }) -} - -fn autogen_skip_json(skip: &getter_core::autogen::AutogenSkip) -> Value { - json!({ - "package_id": skip.package_id.to_string(), - "reason": match skip.reason { - AutogenSkipReason::DuplicateInventoryItem => "duplicate_inventory_item", - AutogenSkipReason::CoveredByHigherPriorityRepository => "covered_by_higher_priority_repo", - }, - "covering_repo_id": skip.repository_id.as_ref().map(RepositoryId::as_str), - }) -} - -fn read_autogen_preview(path: &Path, expected_operation: &str) -> Result { - let bytes = fs::read(path) - .map_err(|source| CliError::Autogen(format!("failed to read autogen preview: {source}")))?; - let raw: Value = serde_json::from_slice(&bytes).map_err(|source| { - CliError::Autogen(format!("failed to parse autogen preview JSON: {source}")) - })?; - let payload = if raw.get("ok").is_some() && raw.get("data").is_some() { - raw.get("data").cloned().unwrap_or(Value::Null) - } else { - raw - }; - if payload.get("operation").and_then(Value::as_str) != Some(expected_operation) { - return Err(CliError::Autogen(format!( - "autogen preview operation must be '{expected_operation}'" - ))); - } - Ok(payload) -} - -fn apply_autogen_installed_preview( - data_dir: &Path, - db: &MainDb, - preview: &Value, - acceptance: &AutogenAcceptance, -) -> Result { - let repo_path = local_autogen_repo_path(data_dir); - ensure_local_autogen_repository(data_dir, db)?; - let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); - let mut applied = Vec::new(); - let mut preserved = Vec::new(); - - for candidate in accepted { - let package_id = preview_package_id(candidate)?; - let relative_path = preview_relative_path(candidate)?; - let content = candidate - .get("content") - .and_then(Value::as_str) - .ok_or_else(|| CliError::Autogen("preview candidate missing content".to_owned()))?; - let expected_hash = candidate - .get("content_hash") - .and_then(Value::as_str) - .ok_or_else(|| { - CliError::Autogen("preview candidate missing content_hash".to_owned()) - })?; - if content_hash(content) != expected_hash { - return Err(CliError::Autogen(format!( - "preview content hash mismatch for {package_id}" - ))); - } - let target = safe_join(&repo_path, &relative_path)?; - if target.exists() { - let current = fs::read_to_string(&target).map_err(|source| { - CliError::Autogen(format!( - "failed to read existing autogen file '{}': {source}", - target.display() - )) - })?; - let current_hash = content_hash(¤t); - let known_hash = manifest - .package(&package_id) - .map(|entry| entry.content_hash.as_str()); - if known_hash != Some(current_hash.as_str()) { - preserved.push(preserve_autogen_file_in_local( - data_dir, - db, - &package_id, - ¤t, - )?); - } - } - if let Some(parent) = target.parent() { - fs::create_dir_all(parent).map_err(|source| { - CliError::Autogen(format!( - "failed to create autogen package directory '{}': {source}", - parent.display() - )) - })?; - } - fs::write(&target, content).map_err(|source| { - CliError::Autogen(format!( - "failed to write autogen package '{}': {source}", - target.display() - )) - })?; - upsert_manifest_entry( - &mut manifest, - AutogenManifestEntry { - package_id: package_id.clone(), - relative_path: relative_path.clone(), - content_hash: expected_hash.to_owned(), - }, - ); - db.upsert_generated_tracked_package_preserving_user_state( - &package_id, - &RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), - )?; - applied.push(json!({ - "package_id": package_id.to_string(), - "output_relative_path": relative_path, - })); - } - - write_autogen_manifest(&repo_path, &manifest)?; - Ok(json!({ - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, - "target_repo_path": repo_path, - "applied_count": applied.len(), - "applied": applied, - "preserved_to_local": preserved, - })) -} - -fn cleanup_preview_json( - data_dir: &Path, - db: &MainDb, - inventory: &InstalledInventory, -) -> Result { - let repo_path = local_autogen_repo_path(data_dir); - let Some(manifest) = read_autogen_manifest(&repo_path)? else { - return Ok(json!({ - "operation": "cleanup.preview", - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, - "target_repo_path": repo_path, - "summary": { "candidate_count": 0, "skipped_count": 0, "write_count": 0, "delete_count": 0 }, - "candidates": [], - "skipped": [], - "diagnostics": [], - })); - }; - let plan = build_local_autogen_plan(db, inventory)?; - let installed_ids: BTreeSet = plan - .candidates - .iter() - .map(|candidate| candidate.package_id.to_string()) - .chain(plan.skipped.iter().map(|skip| skip.package_id.to_string())) - .collect(); - let mut candidates = Vec::new(); - for entry in manifest.packages { - if !installed_ids.contains(&entry.package_id.to_string()) { - candidates.push(json!({ - "package_id": entry.package_id.to_string(), - "action": "delete", - "output_relative_path": entry.relative_path, - "content_hash": entry.content_hash, - "reason": "not_in_installed_inventory", - })); - } - } - candidates.sort_by_key(|candidate| { - candidate - .get("package_id") - .and_then(Value::as_str) - .unwrap_or_default() - .to_owned() - }); - Ok(json!({ - "operation": "cleanup.preview", - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, - "target_repo_path": repo_path, - "summary": { - "candidate_count": candidates.len(), - "skipped_count": 0, - "write_count": 0, - "delete_count": candidates.len(), - }, - "candidates": candidates, - "skipped": [], - "diagnostics": [], - })) -} - -fn apply_autogen_cleanup_preview( - data_dir: &Path, - db: &MainDb, - preview: &Value, - acceptance: &AutogenAcceptance, -) -> Result { - if preview.get("target_repo_id").and_then(Value::as_str) != Some(LOCAL_AUTOGEN_REPOSITORY_ID) { - return Err(CliError::Autogen(format!( - "cleanup preview target_repo_id must be '{LOCAL_AUTOGEN_REPOSITORY_ID}'" - ))); - } - let repo_path = local_autogen_repo_path(data_dir); - let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); - let mut deleted = Vec::new(); - let mut preserved = Vec::new(); - let local_autogen_id = RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"); - for candidate in accepted { - let package_id = preview_package_id(candidate)?; - let relative_path = preview_relative_path(candidate)?; - let expected_hash = candidate - .get("content_hash") - .and_then(Value::as_str) - .ok_or_else(|| { - CliError::Autogen("cleanup preview candidate missing content_hash".to_owned()) - })?; - let manifest_entry = manifest.package(&package_id).ok_or_else(|| { - CliError::Autogen(format!( - "cleanup preview candidate {package_id} is not managed by local_autogen manifest" - )) - })?; - if manifest_entry.relative_path != relative_path - || manifest_entry.content_hash != expected_hash - { - return Err(CliError::Autogen(format!( - "cleanup preview candidate {package_id} does not match local_autogen manifest" - ))); - } - let target = safe_join(&repo_path, &relative_path)?; - if target.exists() { - let current = fs::read_to_string(&target).map_err(|source| { - CliError::Autogen(format!( - "failed to read existing autogen file '{}': {source}", - target.display() - )) - })?; - if content_hash(¤t) != expected_hash { - preserved.push(preserve_autogen_file_in_local( - data_dir, - db, - &package_id, - ¤t, - )?); - } - fs::remove_file(&target).map_err(|source| { - CliError::Autogen(format!( - "failed to delete autogen package '{}': {source}", - target.display() - )) - })?; - } - manifest - .packages - .retain(|entry| entry.package_id != package_id); - db.delete_generated_tracked_package(&package_id, &local_autogen_id)?; - deleted.push(json!({ - "package_id": package_id.to_string(), - "output_relative_path": relative_path, - })); - } - write_autogen_manifest(&repo_path, &manifest)?; - Ok(json!({ - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, - "target_repo_path": repo_path, - "deleted_count": deleted.len(), - "deleted": deleted, - "preserved_to_local": preserved, - })) -} - -fn accepted_preview_candidates<'a>( - preview: &'a Value, - acceptance: &AutogenAcceptance, -) -> Result, CliError> { - let candidates = preview - .get("candidates") - .and_then(Value::as_array) - .ok_or_else(|| CliError::Autogen("autogen preview missing candidates".to_owned()))?; - match acceptance { - AutogenAcceptance::AcceptAll => Ok(candidates.iter().collect()), - AutogenAcceptance::Accept(ids) => { - let accepted: BTreeSet = ids.iter().map(ToString::to_string).collect(); - Ok(candidates - .iter() - .filter(|candidate| { - candidate - .get("package_id") - .and_then(Value::as_str) - .is_some_and(|id| accepted.contains(id)) - }) - .collect()) - } - } -} - -fn preview_package_id(candidate: &Value) -> Result { - candidate - .get("package_id") - .and_then(Value::as_str) - .ok_or_else(|| CliError::Autogen("preview candidate missing package_id".to_owned()))? - .parse() - .map_err(|source: getter_core::PackageIdError| CliError::Autogen(source.to_string())) -} - -fn preview_relative_path(candidate: &Value) -> Result { - candidate - .get("output_relative_path") - .and_then(Value::as_str) - .map(PathBuf::from) - .ok_or_else(|| { - CliError::Autogen("preview candidate missing output_relative_path".to_owned()) - }) -} - -fn ensure_local_autogen_repository(data_dir: &Path, db: &MainDb) -> Result { - let repo_path = local_autogen_repo_path(data_dir); - ensure_repository_layout(&repo_path, &local_autogen_repo_toml())?; - db.upsert_repository( - &RepositoryMetadata { - id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), - name: LOCAL_AUTOGEN_REPOSITORY_NAME.to_owned(), - priority: RepositoryPriority::LOCAL_AUTOGEN, - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(&repo_path), - None, - )?; - Ok(repo_path) -} - -fn ensure_local_repository(data_dir: &Path, db: &MainDb) -> Result { - let local_id = RepositoryId::new(LOCAL_REPOSITORY_ID).expect("valid id"); - if let Ok(existing) = find_repository(db, &local_id) { - let repo_path = repo_path(&existing)?; - ensure_repository_layout(&repo_path, &local_repo_toml())?; - return Ok(repo_path); - } - - let repo_path = data_dir.join("repositories").join(LOCAL_REPOSITORY_ID); - ensure_repository_layout(&repo_path, &local_repo_toml())?; - db.upsert_repository( - &RepositoryMetadata { - id: local_id, - name: LOCAL_REPOSITORY_NAME.to_owned(), - priority: RepositoryPriority::LOCAL, - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(&repo_path), - None, - )?; - Ok(repo_path) -} - -fn ensure_repository_layout(repo_path: &Path, repo_toml: &str) -> Result<(), CliError> { - fs::create_dir_all(repo_path.join("packages")).map_err(|source| { - CliError::Autogen(format!( - "failed to create repository packages dir '{}': {source}", - repo_path.display() - )) - })?; - fs::create_dir_all(repo_path.join("lib")).map_err(|source| { - CliError::Autogen(format!( - "failed to create repository lib dir '{}': {source}", - repo_path.display() - )) - })?; - fs::create_dir_all(repo_path.join("templates")).map_err(|source| { - CliError::Autogen(format!( - "failed to create repository templates dir '{}': {source}", - repo_path.display() - )) - })?; - let repo_toml_path = repo_path.join("repo.toml"); - if !repo_toml_path.exists() { - fs::write(&repo_toml_path, repo_toml).map_err(|source| { - CliError::Autogen(format!( - "failed to write repo.toml '{}': {source}", - repo_toml_path.display() - )) - })?; - } - Ok(()) -} - -fn preserve_autogen_file_in_local( - data_dir: &Path, - db: &MainDb, - package_id: &PackageId, - content: &str, -) -> Result { - let local_repo = ensure_local_repository(data_dir, db)?; - let primary_relative = getter_core::autogen::package_relative_path(package_id); - let primary_target = safe_join(&local_repo, &primary_relative)?; - let relative_path = if primary_target.exists() { - let backup = PathBuf::from("autogen-preserved") - .join(package_id.kind().as_str()) - .join(format!( - "{}.{}.lua", - package_id.name(), - content_hash(content).replace(':', "-") - )); - safe_join(&local_repo, &backup)?; - backup - } else { - primary_relative - }; - let target = safe_join(&local_repo, &relative_path)?; - if let Some(parent) = target.parent() { - fs::create_dir_all(parent).map_err(|source| { - CliError::Autogen(format!( - "failed to create local preservation directory '{}': {source}", - parent.display() - )) - })?; - } - fs::write(&target, content).map_err(|source| { - CliError::Autogen(format!( - "failed to preserve modified autogen file '{}': {source}", - target.display() - )) - })?; - Ok(json!({ - "package_id": package_id.to_string(), - "repository_id": LOCAL_REPOSITORY_ID, - "relative_path": relative_path, - })) -} - -fn read_autogen_manifest(repo_path: &Path) -> Result, CliError> { - let path = repo_path.join(AUTOGEN_MANIFEST_FILE); - if !path.exists() { - return Ok(None); - } - let bytes = fs::read(&path).map_err(|source| { - CliError::Autogen(format!( - "failed to read autogen manifest '{}': {source}", - path.display() - )) - })?; - serde_json::from_slice(&bytes) - .map(Some) - .map_err(|source| CliError::Autogen(format!("failed to parse autogen manifest: {source}"))) -} - -fn write_autogen_manifest(repo_path: &Path, manifest: &AutogenManifest) -> Result<(), CliError> { - fs::create_dir_all(repo_path).map_err(|source| { - CliError::Autogen(format!( - "failed to create autogen repository '{}': {source}", - repo_path.display() - )) - })?; - let path = repo_path.join(AUTOGEN_MANIFEST_FILE); - let bytes = serde_json::to_vec_pretty(manifest) - .map_err(|source| CliError::Autogen(format!("failed to serialize manifest: {source}")))?; - fs::write(&path, bytes).map_err(|source| { - CliError::Autogen(format!( - "failed to write autogen manifest '{}': {source}", - path.display() - )) - }) -} - -fn empty_autogen_manifest() -> AutogenManifest { - AutogenManifest { - version: getter_core::autogen::AUTOGEN_MANIFEST_VERSION, - repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), - packages: Vec::new(), - } -} - -fn upsert_manifest_entry(manifest: &mut AutogenManifest, entry: AutogenManifestEntry) { - manifest - .packages - .retain(|existing| existing.package_id != entry.package_id); - manifest.packages.push(entry); - manifest - .packages - .sort_by_key(|existing| existing.package_id.to_string()); -} - -fn safe_join(root: &Path, relative: &Path) -> Result { - if relative.is_absolute() - || relative.components().any(|component| { - matches!( - component, - Component::ParentDir | Component::RootDir | Component::Prefix(_) - ) - }) - { - return Err(CliError::Autogen(format!( - "unsafe relative path '{}'", - relative.display() - ))); - } - Ok(root.join(relative)) -} - -fn local_autogen_repo_path(data_dir: &Path) -> PathBuf { - data_dir - .join("repositories") - .join(LOCAL_AUTOGEN_REPOSITORY_ID) -} - fn repo_path(repo: &StoredRepository) -> Result { repo.path .as_ref() diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml index 47326c5..93325e9 100644 --- a/crates/getter-core/Cargo.toml +++ b/crates/getter-core/Cargo.toml @@ -3,8 +3,12 @@ name = "getter-core" version.workspace = true edition.workspace = true +[features] +default = ["lua"] +lua = ["dep:mlua"] + [dependencies] -mlua = { version = "0.10", features = ["lua54", "vendored"] } +mlua = { version = "0.10", features = ["lua54", "vendored"], optional = true } serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "1" diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index aa3bb41..ce5c030 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -10,7 +10,9 @@ use std::fmt; use std::str::FromStr; pub mod autogen; +#[cfg(feature = "lua")] pub mod diagnostics; +#[cfg(feature = "lua")] pub mod lua; pub mod repository; pub mod task; diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml new file mode 100644 index 0000000..f24bcff --- /dev/null +++ b/crates/getter-operations/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "getter-operations" +version.workspace = true +edition.workspace = true + +[dependencies] +getter-core = { path = "../getter-core", default-features = false } +getter-storage = { path = "../getter-storage" } +serde_json = "1" +thiserror = "1" diff --git a/crates/getter-operations/src/autogen.rs b/crates/getter-operations/src/autogen.rs new file mode 100644 index 0000000..caab4aa --- /dev/null +++ b/crates/getter-operations/src/autogen.rs @@ -0,0 +1,637 @@ +//! Getter-owned installed-inventory autogen operations. +//! +//! The CLI and native bridge both call this module so there is one implementation +//! of `local_autogen` preview/apply semantics. Platform layers provide installed +//! inventory facts; this module decides generated package ids, repository +//! coverage, file writes, manifest updates, preservation behavior, and tracked +//! state updates. + +use getter_core::autogen::{ + content_hash, local_autogen_repo_toml, local_repo_toml, plan_local_autogen, AutogenManifest, + AutogenManifestEntry, AutogenPlan, AutogenSkipReason, InstalledInventory, + LOCAL_AUTOGEN_REPOSITORY_ID, LOCAL_AUTOGEN_REPOSITORY_NAME, LOCAL_REPOSITORY_ID, + LOCAL_REPOSITORY_NAME, +}; +use getter_core::repository::{RepositoryLayout, RepositoryMetadata, REPO_API_VERSION_V1}; +use getter_core::{PackageId, RepositoryId, RepositoryPriority}; +use getter_storage::{MainDb, StorageError, StoredRepository}; +use serde_json::{json, Value}; +use std::collections::{BTreeSet, HashMap}; +use std::fs; +use std::path::{Component, Path, PathBuf}; + +pub const AUTOGEN_MANIFEST_FILE: &str = "autogen-manifest.json"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AutogenAcceptance { + AcceptAll, + Accept(Vec), +} + +#[derive(Debug, thiserror::Error)] +pub enum AutogenOperationError { + #[error("storage error: {0}")] + Storage(#[from] StorageError), + #[error("repository error: {0}")] + Repository(String), + #[error("autogen error: {0}")] + Autogen(String), +} + +pub type AutogenOperationResult = Result; + +pub fn build_local_autogen_plan( + db: &MainDb, + inventory: &InstalledInventory, +) -> AutogenOperationResult { + let covered = higher_priority_package_coverage(db)?; + plan_local_autogen(inventory, &covered) + .map_err(|source| AutogenOperationError::Autogen(source.to_string())) +} + +pub fn installed_preview_json(data_dir: &Path, plan: &AutogenPlan) -> Value { + let candidates: Vec = plan.candidates.iter().map(autogen_candidate_json).collect(); + let skipped: Vec = plan.skipped.iter().map(autogen_skip_json).collect(); + json!({ + "operation": "installed.preview", + "target_repo_id": plan.repository_id.as_str(), + "target_repo_path": local_autogen_repo_path(data_dir), + "summary": { + "candidate_count": candidates.len(), + "skipped_count": skipped.len(), + "write_count": candidates.len(), + "delete_count": 0, + }, + "candidates": candidates, + "skipped": skipped, + "diagnostics": [], + }) +} + +pub fn cleanup_preview_json( + data_dir: &Path, + db: &MainDb, + inventory: &InstalledInventory, +) -> AutogenOperationResult { + let repo_path = local_autogen_repo_path(data_dir); + let Some(manifest) = read_autogen_manifest(&repo_path)? else { + return Ok(json!({ + "operation": "cleanup.preview", + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "summary": { "candidate_count": 0, "skipped_count": 0, "write_count": 0, "delete_count": 0 }, + "candidates": [], + "skipped": [], + "diagnostics": [], + })); + }; + let plan = build_local_autogen_plan(db, inventory)?; + let installed_ids: BTreeSet = plan + .candidates + .iter() + .map(|candidate| candidate.package_id.to_string()) + .chain(plan.skipped.iter().map(|skip| skip.package_id.to_string())) + .collect(); + let mut candidates = Vec::new(); + for entry in manifest.packages { + if !installed_ids.contains(&entry.package_id.to_string()) { + candidates.push(json!({ + "package_id": entry.package_id.to_string(), + "action": "delete", + "output_relative_path": entry.relative_path, + "content_hash": entry.content_hash, + "reason": "not_in_installed_inventory", + })); + } + } + candidates.sort_by_key(|candidate| { + candidate + .get("package_id") + .and_then(Value::as_str) + .unwrap_or_default() + .to_owned() + }); + Ok(json!({ + "operation": "cleanup.preview", + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "summary": { + "candidate_count": candidates.len(), + "skipped_count": 0, + "write_count": 0, + "delete_count": candidates.len(), + }, + "candidates": candidates, + "skipped": [], + "diagnostics": [], + })) +} + +pub fn apply_installed_preview( + data_dir: &Path, + db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, +) -> AutogenOperationResult { + let repo_path = local_autogen_repo_path(data_dir); + ensure_local_autogen_repository(data_dir, db)?; + let accepted = accepted_preview_candidates(preview, acceptance)?; + let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); + let mut applied = Vec::new(); + let mut preserved = Vec::new(); + + for candidate in accepted { + let package_id = preview_package_id(candidate)?; + let relative_path = preview_relative_path(candidate)?; + let content = candidate + .get("content") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen("preview candidate missing content".to_owned()) + })?; + let expected_hash = candidate + .get("content_hash") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen("preview candidate missing content_hash".to_owned()) + })?; + if content_hash(content) != expected_hash { + return Err(AutogenOperationError::Autogen(format!( + "preview content hash mismatch for {package_id}" + ))); + } + let target = safe_join(&repo_path, &relative_path)?; + if target.exists() { + let current = fs::read_to_string(&target).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to read existing autogen file '{}': {source}", + target.display() + )) + })?; + let current_hash = content_hash(¤t); + let known_hash = manifest + .package(&package_id) + .map(|entry| entry.content_hash.as_str()); + if known_hash != Some(current_hash.as_str()) { + preserved.push(preserve_autogen_file_in_local( + data_dir, + db, + &package_id, + ¤t, + )?); + } + } + if let Some(parent) = target.parent() { + fs::create_dir_all(parent).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create autogen package directory '{}': {source}", + parent.display() + )) + })?; + } + fs::write(&target, content).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to write autogen package '{}': {source}", + target.display() + )) + })?; + upsert_manifest_entry( + &mut manifest, + AutogenManifestEntry { + package_id: package_id.clone(), + relative_path: relative_path.clone(), + content_hash: expected_hash.to_owned(), + }, + ); + db.upsert_generated_tracked_package_preserving_user_state( + &package_id, + &RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + )?; + applied.push(json!({ + "package_id": package_id.to_string(), + "output_relative_path": relative_path, + })); + } + + write_autogen_manifest(&repo_path, &manifest)?; + Ok(json!({ + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "applied_count": applied.len(), + "applied": applied, + "preserved_to_local": preserved, + })) +} + +pub fn apply_cleanup_preview( + data_dir: &Path, + db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, +) -> AutogenOperationResult { + if preview.get("target_repo_id").and_then(Value::as_str) != Some(LOCAL_AUTOGEN_REPOSITORY_ID) { + return Err(AutogenOperationError::Autogen(format!( + "cleanup preview target_repo_id must be '{LOCAL_AUTOGEN_REPOSITORY_ID}'" + ))); + } + let repo_path = local_autogen_repo_path(data_dir); + let accepted = accepted_preview_candidates(preview, acceptance)?; + let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); + let mut deleted = Vec::new(); + let mut preserved = Vec::new(); + let local_autogen_id = RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"); + for candidate in accepted { + let package_id = preview_package_id(candidate)?; + let relative_path = preview_relative_path(candidate)?; + let expected_hash = candidate + .get("content_hash") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen( + "cleanup preview candidate missing content_hash".to_owned(), + ) + })?; + let manifest_entry = manifest.package(&package_id).ok_or_else(|| { + AutogenOperationError::Autogen(format!( + "cleanup preview candidate {package_id} is not managed by local_autogen manifest" + )) + })?; + if manifest_entry.relative_path != relative_path + || manifest_entry.content_hash != expected_hash + { + return Err(AutogenOperationError::Autogen(format!( + "cleanup preview candidate {package_id} does not match local_autogen manifest" + ))); + } + let target = safe_join(&repo_path, &relative_path)?; + if target.exists() { + let current = fs::read_to_string(&target).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to read existing autogen file '{}': {source}", + target.display() + )) + })?; + if content_hash(¤t) != expected_hash { + preserved.push(preserve_autogen_file_in_local( + data_dir, + db, + &package_id, + ¤t, + )?); + } + fs::remove_file(&target).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to delete autogen package '{}': {source}", + target.display() + )) + })?; + } + manifest + .packages + .retain(|entry| entry.package_id != package_id); + db.delete_generated_tracked_package(&package_id, &local_autogen_id)?; + deleted.push(json!({ + "package_id": package_id.to_string(), + "output_relative_path": relative_path, + })); + } + write_autogen_manifest(&repo_path, &manifest)?; + Ok(json!({ + "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_path": repo_path, + "deleted_count": deleted.len(), + "deleted": deleted, + "preserved_to_local": preserved, + })) +} + +pub fn unwrap_preview_payload( + raw: Value, + expected_operation: &str, +) -> AutogenOperationResult { + let payload = if raw.get("ok").is_some() && raw.get("data").is_some() { + raw.get("data").cloned().unwrap_or(Value::Null) + } else { + raw + }; + if payload.get("operation").and_then(Value::as_str) != Some(expected_operation) { + return Err(AutogenOperationError::Autogen(format!( + "autogen preview operation must be '{expected_operation}'" + ))); + } + Ok(payload) +} + +pub fn local_autogen_repo_path(data_dir: &Path) -> PathBuf { + data_dir + .join("repositories") + .join(LOCAL_AUTOGEN_REPOSITORY_ID) +} + +fn higher_priority_package_coverage( + db: &MainDb, +) -> AutogenOperationResult> { + let mut covered = HashMap::new(); + for repo in db.repositories()? { + if repo.id.as_str() == LOCAL_AUTOGEN_REPOSITORY_ID { + continue; + } + if repo.priority <= RepositoryPriority::LOCAL_AUTOGEN { + continue; + } + let Some(path) = repo.path.as_ref() else { + continue; + }; + let layout = load_repository_layout(Path::new(path))?; + for package in layout.packages { + covered.entry(package.id).or_insert_with(|| repo.id.clone()); + } + } + Ok(covered) +} + +fn load_repository_layout(path: &Path) -> AutogenOperationResult { + RepositoryLayout::load(path) + .map_err(|source| AutogenOperationError::Repository(source.to_string())) +} + +fn autogen_candidate_json(candidate: &getter_core::autogen::AutogenCandidate) -> Value { + json!({ + "package_id": candidate.package_id.to_string(), + "kind": candidate.package_id.kind().as_str(), + "display_name": candidate.name, + "installed_target": candidate.installed, + "action": "create", + "output_relative_path": candidate.relative_path, + "content_hash": candidate.content_hash, + "content": candidate.content, + }) +} + +fn autogen_skip_json(skip: &getter_core::autogen::AutogenSkip) -> Value { + json!({ + "package_id": skip.package_id.to_string(), + "reason": match skip.reason { + AutogenSkipReason::DuplicateInventoryItem => "duplicate_inventory_item", + AutogenSkipReason::CoveredByHigherPriorityRepository => "covered_by_higher_priority_repo", + }, + "covering_repo_id": skip.repository_id.as_ref().map(RepositoryId::as_str), + }) +} + +fn accepted_preview_candidates<'a>( + preview: &'a Value, + acceptance: &AutogenAcceptance, +) -> AutogenOperationResult> { + let candidates = preview + .get("candidates") + .and_then(Value::as_array) + .ok_or_else(|| { + AutogenOperationError::Autogen("autogen preview missing candidates".to_owned()) + })?; + match acceptance { + AutogenAcceptance::AcceptAll => Ok(candidates.iter().collect()), + AutogenAcceptance::Accept(ids) => { + let accepted: BTreeSet = ids.iter().map(ToString::to_string).collect(); + Ok(candidates + .iter() + .filter(|candidate| { + candidate + .get("package_id") + .and_then(Value::as_str) + .is_some_and(|id| accepted.contains(id)) + }) + .collect()) + } + } +} + +fn preview_package_id(candidate: &Value) -> AutogenOperationResult { + candidate + .get("package_id") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen("preview candidate missing package_id".to_owned()) + })? + .parse() + .map_err(|source: getter_core::PackageIdError| { + AutogenOperationError::Autogen(source.to_string()) + }) +} + +fn preview_relative_path(candidate: &Value) -> AutogenOperationResult { + candidate + .get("output_relative_path") + .and_then(Value::as_str) + .map(PathBuf::from) + .ok_or_else(|| { + AutogenOperationError::Autogen( + "preview candidate missing output_relative_path".to_owned(), + ) + }) +} + +fn ensure_local_autogen_repository( + data_dir: &Path, + db: &MainDb, +) -> AutogenOperationResult { + let repo_path = local_autogen_repo_path(data_dir); + ensure_repository_layout(&repo_path, &local_autogen_repo_toml())?; + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + name: LOCAL_AUTOGEN_REPOSITORY_NAME.to_owned(), + priority: RepositoryPriority::LOCAL_AUTOGEN, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + )?; + Ok(repo_path) +} + +fn ensure_local_repository(data_dir: &Path, db: &MainDb) -> AutogenOperationResult { + let local_id = RepositoryId::new(LOCAL_REPOSITORY_ID).expect("valid id"); + if let Ok(existing) = find_repository(db, &local_id) { + let repo_path = repo_path(&existing)?; + ensure_repository_layout(&repo_path, &local_repo_toml())?; + return Ok(repo_path); + } + + let repo_path = data_dir.join("repositories").join(LOCAL_REPOSITORY_ID); + ensure_repository_layout(&repo_path, &local_repo_toml())?; + db.upsert_repository( + &RepositoryMetadata { + id: local_id, + name: LOCAL_REPOSITORY_NAME.to_owned(), + priority: RepositoryPriority::LOCAL, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + )?; + Ok(repo_path) +} + +fn find_repository(db: &MainDb, id: &RepositoryId) -> AutogenOperationResult { + db.repositories()? + .into_iter() + .find(|repo| &repo.id == id) + .ok_or_else(|| { + AutogenOperationError::Repository(format!("repository '{id}' is not registered")) + }) +} + +fn repo_path(repo: &StoredRepository) -> AutogenOperationResult { + repo.path.as_ref().map(PathBuf::from).ok_or_else(|| { + AutogenOperationError::Repository(format!("repository '{}' has no path", repo.id)) + }) +} + +fn ensure_repository_layout(repo_path: &Path, repo_toml: &str) -> AutogenOperationResult<()> { + fs::create_dir_all(repo_path.join("packages")).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create repository packages dir '{}': {source}", + repo_path.display() + )) + })?; + fs::create_dir_all(repo_path.join("lib")).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create repository lib dir '{}': {source}", + repo_path.display() + )) + })?; + fs::create_dir_all(repo_path.join("templates")).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create repository templates dir '{}': {source}", + repo_path.display() + )) + })?; + let repo_toml_path = repo_path.join("repo.toml"); + if !repo_toml_path.exists() { + fs::write(&repo_toml_path, repo_toml).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to write repo.toml '{}': {source}", + repo_toml_path.display() + )) + })?; + } + Ok(()) +} + +fn preserve_autogen_file_in_local( + data_dir: &Path, + db: &MainDb, + package_id: &PackageId, + content: &str, +) -> AutogenOperationResult { + let local_repo = ensure_local_repository(data_dir, db)?; + let primary_relative = getter_core::autogen::package_relative_path(package_id); + let primary_target = safe_join(&local_repo, &primary_relative)?; + let relative_path = if primary_target.exists() { + let backup = PathBuf::from("autogen-preserved") + .join(package_id.kind().as_str()) + .join(format!( + "{}.{}.lua", + package_id.name(), + content_hash(content).replace(':', "-") + )); + safe_join(&local_repo, &backup)?; + backup + } else { + primary_relative + }; + let target = safe_join(&local_repo, &relative_path)?; + if let Some(parent) = target.parent() { + fs::create_dir_all(parent).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create local preservation directory '{}': {source}", + parent.display() + )) + })?; + } + fs::write(&target, content).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to preserve modified autogen file '{}': {source}", + target.display() + )) + })?; + Ok(json!({ + "package_id": package_id.to_string(), + "repository_id": LOCAL_REPOSITORY_ID, + "relative_path": relative_path, + })) +} + +fn read_autogen_manifest(repo_path: &Path) -> AutogenOperationResult> { + let path = repo_path.join(AUTOGEN_MANIFEST_FILE); + if !path.exists() { + return Ok(None); + } + let bytes = fs::read(&path).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to read autogen manifest '{}': {source}", + path.display() + )) + })?; + serde_json::from_slice(&bytes).map(Some).map_err(|source| { + AutogenOperationError::Autogen(format!("failed to parse autogen manifest: {source}")) + }) +} + +fn write_autogen_manifest( + repo_path: &Path, + manifest: &AutogenManifest, +) -> AutogenOperationResult<()> { + fs::create_dir_all(repo_path).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create autogen repository '{}': {source}", + repo_path.display() + )) + })?; + let path = repo_path.join(AUTOGEN_MANIFEST_FILE); + let bytes = serde_json::to_vec_pretty(manifest).map_err(|source| { + AutogenOperationError::Autogen(format!("failed to serialize manifest: {source}")) + })?; + fs::write(&path, bytes).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to write autogen manifest '{}': {source}", + path.display() + )) + }) +} + +fn empty_autogen_manifest() -> AutogenManifest { + AutogenManifest { + version: getter_core::autogen::AUTOGEN_MANIFEST_VERSION, + repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + packages: Vec::new(), + } +} + +fn upsert_manifest_entry(manifest: &mut AutogenManifest, entry: AutogenManifestEntry) { + manifest + .packages + .retain(|existing| existing.package_id != entry.package_id); + manifest.packages.push(entry); + manifest + .packages + .sort_by_key(|existing| existing.package_id.to_string()); +} + +fn safe_join(root: &Path, relative: &Path) -> AutogenOperationResult { + if relative.is_absolute() + || relative.components().any(|component| { + matches!( + component, + Component::ParentDir | Component::RootDir | Component::Prefix(_) + ) + }) + { + return Err(AutogenOperationError::Autogen(format!( + "unsafe relative path '{}'", + relative.display() + ))); + } + Ok(root.join(relative)) +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs new file mode 100644 index 0000000..f4432e2 --- /dev/null +++ b/crates/getter-operations/src/lib.rs @@ -0,0 +1,8 @@ +//! Reusable getter-owned operations shared by CLI and native embedders. +//! +//! This crate intentionally contains product/domain orchestration that must not +//! be duplicated in Flutter or Android/Kotlin bridge glue. Platform adapters may +//! provide raw facts to callers, but the rules for repository coverage, +//! generated package files, manifests, and tracked state live here. + +pub mod autogen; diff --git a/crates/getter-storage/Cargo.toml b/crates/getter-storage/Cargo.toml index bc0964b..da9172d 100644 --- a/crates/getter-storage/Cargo.toml +++ b/crates/getter-storage/Cargo.toml @@ -4,7 +4,7 @@ version.workspace = true edition.workspace = true [dependencies] -getter-core = { path = "../getter-core" } +getter-core = { path = "../getter-core", default-features = false } rusqlite = { version = "0.32", features = ["bundled"] } serde_json = "1" thiserror = "1" diff --git a/src/lib.rs b/src/lib.rs index 607df46..d29d0ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,8 @@ #[cfg(feature = "domain")] pub use getter_core as core; #[cfg(feature = "domain")] +pub use getter_operations as operations; +#[cfg(feature = "domain")] pub use getter_storage as storage; #[cfg(feature = "rustls-platform-verifier")] From 21dc994f5d452238a0b5fda3ecf9cd09e541155a Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Wed, 24 Jun 2026 10:17:21 +0800 Subject: [PATCH 18/71] feat: share legacy room operations --- Cargo.lock | 1 + crates/getter-cli/src/lib.rs | 208 +--------- crates/getter-operations/Cargo.toml | 1 + crates/getter-operations/src/legacy_room.rs | 426 ++++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + 5 files changed, 448 insertions(+), 189 deletions(-) create mode 100644 crates/getter-operations/src/legacy_room.rs diff --git a/Cargo.lock b/Cargo.lock index 1ff310d..fce5fc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -566,6 +566,7 @@ version = "0.1.0" dependencies = [ "getter-core", "getter-storage", + "serde", "serde_json", "thiserror 1.0.69", ] diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index d9f4d47..3562bf1 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -18,10 +18,9 @@ use getter_downloader::{ cancel_download_task, record_install_result, run_fake_download_task, submit_fake_download_task, }; use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; +use getter_operations::legacy_room::{self, LegacyRoomOperationError}; use getter_storage::legacy_room::{ - map_legacy_app, read_legacy_room_database, LegacyAppKind, LegacyAppRecord, - LegacyExtraAppRecord, LegacyPackageResolution, LegacyRoomDbImport, LegacyRoomImportWarning, - LegacyRoomReadError, + map_legacy_app, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, }; use getter_storage::{ CacheDb, MainDb, MigrationRecordUpsert, StorageError, StoredPackageResolution, @@ -250,6 +249,20 @@ impl From for CliError { } } +impl From for CliError { + fn from(value: LegacyRoomOperationError) -> Self { + match value { + LegacyRoomOperationError::Storage(detail) => Self::Storage(detail), + LegacyRoomOperationError::UnsupportedDb { report_path } => { + Self::UnsupportedLegacyDb { report_path } + } + LegacyRoomOperationError::InvalidDb { report_path } => { + Self::InvalidLegacyDb { report_path } + } + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliOutput { pub exit_code: ExitCode, @@ -705,64 +718,11 @@ fn execute(invocation: CliInvocation) -> Result { })) } CliCommand::LegacyImportRoomDb { db: legacy_db } => { - let db = open_main_db(&invocation.data_dir)?; - if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { - let records = db.tracked_packages()?; - return Ok(json!({ - "already_imported": true, - "imported_records": 0, - "source_counts": { - "app_rows": 0, - "extra_app_rows": 0, - "hub_rows": 0, - "extra_hub_rows": 0, - }, - "warnings": [], - "apps": tracked_packages_json(records), - })); - } - let import = read_legacy_room_database(&legacy_db).map_err(|source| { - legacy_db_import_error(&invocation.data_dir, &legacy_db, source) - })?; - let imported_records = import.apps.len(); - let source_counts = source_counts_json(&import); - let warnings = import_warnings_json(&import.warnings); - if import.source_counts.app_rows > 0 && imported_records == 0 { - let report_path = create_migration_report_with_source_counts( - &invocation.data_dir, - &legacy_db, - "migration.invalid_db", - "Legacy Room database has app rows but no importable app rows", - 0, - 0, - &warnings, - Some(&source_counts), - )?; - return Err(CliError::InvalidLegacyDb { report_path }); - } - import_legacy_room_db(&db, &import)?; - let report_path = create_migration_report_with_source_counts( - &invocation.data_dir, - &legacy_db, - "migration.imported", - "Legacy Room database imported", - imported_records as u64, - imported_records as u64, - &warnings, - Some(&source_counts), - )?; - let records = db.tracked_packages()?; - Ok(json!({ - "report_path": report_path, - "imported_records": imported_records, - "source_counts": source_counts, - "warnings": warnings, - "apps": tracked_packages_json(records), - })) + legacy_room::import_room_db_json(&invocation.data_dir, &legacy_db) + .map_err(CliError::from) } CliCommand::LegacyReportList => { - open_initialized_storage(&invocation.data_dir)?; - Ok(json!({ "reports": list_migration_reports(&invocation.data_dir)? })) + legacy_room::report_list_json(&invocation.data_dir).map_err(CliError::from) } } } @@ -1053,34 +1013,6 @@ fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<( Ok(()) } -fn import_legacy_room_db(db: &MainDb, import: &LegacyRoomDbImport) -> Result<(), CliError> { - let mut packages = Vec::new(); - for app in &import.apps { - let mapping = map_legacy_app(&app.app, Some(&app.user_state)) - .map_err(|source| CliError::Storage(source.to_string()))?; - packages.push(tracked_package_upsert(mapping)); - } - - let report_json = json!({ - "ok": true, - "source": "legacy-room-db", - "version": import.version, - "imported_records": import.apps.len(), - "source_counts": source_counts_json(import), - "warnings": import_warnings_json(&import.warnings), - }) - .to_string(); - db.import_tracked_packages_with_migration_record( - &packages, - &MigrationRecordUpsert { - id: LEGACY_ROOM_MIGRATION_ID, - source: "legacy-room-db", - report_json: &report_json, - }, - )?; - Ok(()) -} - fn tracked_package_upsert( mapping: getter_storage::legacy_room::LegacyAppMapping, ) -> TrackedPackageUpsert { @@ -1108,54 +1040,6 @@ fn stored_resolution(resolution: LegacyPackageResolution) -> StoredPackageResolu } } -fn legacy_db_import_error( - data_dir: &Path, - legacy_db: &Path, - source: LegacyRoomReadError, -) -> CliError { - let (code, unsupported) = match source { - LegacyRoomReadError::UnsupportedVersion { .. } => ("migration.unsupported_db", true), - LegacyRoomReadError::Sqlite(_) | LegacyRoomReadError::MissingRequiredTable(_) => { - ("migration.invalid_db", false) - } - }; - match create_migration_report_with_source_counts( - data_dir, - legacy_db, - code, - &source.to_string(), - 0, - 0, - &[], - None, - ) { - Ok(report_path) if unsupported => CliError::UnsupportedLegacyDb { report_path }, - Ok(report_path) => CliError::InvalidLegacyDb { report_path }, - Err(error) => error, - } -} - -fn source_counts_json(import: &LegacyRoomDbImport) -> Value { - json!({ - "app_rows": import.source_counts.app_rows, - "extra_app_rows": import.source_counts.extra_app_rows, - "hub_rows": import.source_counts.hub_rows, - "extra_hub_rows": import.source_counts.extra_hub_rows, - }) -} - -fn import_warnings_json(warnings: &[LegacyRoomImportWarning]) -> Vec { - warnings - .iter() - .map(|warning| { - json!({ - "code": warning.code(), - "message": warning.message(), - }) - }) - .collect() -} - fn main_db_path(data_dir: &Path) -> PathBuf { data_dir.join(MAIN_DB_FILE) } @@ -1232,60 +1116,6 @@ fn report_file_name(code: &str) -> String { format!("{}.json", code.replace('.', "-")) } -fn list_migration_reports(data_dir: &Path) -> Result, CliError> { - let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); - if !reports_dir.exists() { - return Ok(Vec::new()); - } - - let mut report_paths = fs::read_dir(&reports_dir) - .map_err(|source| CliError::Storage(format!("failed to read migration reports: {source}")))? - .map(|entry| entry.map(|entry| entry.path())) - .collect::, _>>() - .map_err(|source| { - CliError::Storage(format!("failed to read migration report entry: {source}")) - })?; - report_paths.retain(|path| { - path.extension() - .is_some_and(|extension| extension == "json") - }); - report_paths.sort(); - - report_paths - .into_iter() - .map(|path| { - let bytes = fs::read(&path).map_err(|source| { - CliError::Storage(format!( - "failed to read migration report '{}': {source}", - path.display() - )) - })?; - let report: Value = serde_json::from_slice(&bytes).map_err(|source| { - CliError::Storage(format!( - "failed to parse migration report '{}': {source}", - path.display() - )) - })?; - Ok(json!({ - "ok": report.get("ok").and_then(Value::as_bool).unwrap_or(false), - "code": report.get("code").and_then(Value::as_str).unwrap_or("migration.unknown"), - "message": report.get("message").and_then(Value::as_str).unwrap_or("Legacy migration report"), - "source_file_name": report - .get("source_file_name") - .or_else(|| report.get("bundle_file_name")) - .and_then(Value::as_str), - "imported_records": report.get("imported_records").and_then(Value::as_u64).unwrap_or(0), - "tracked_records": report.get("tracked_records").and_then(Value::as_u64).unwrap_or(0), - "warnings": report - .get("warnings") - .cloned() - .unwrap_or_else(|| Value::Array(Vec::new())), - "source_counts": report.get("source_counts").cloned().unwrap_or(Value::Null), - })) - }) - .collect() -} - #[derive(Debug, Serialize)] struct MigrationReport<'a> { ok: bool, diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index f24bcff..a44e0b6 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -6,5 +6,6 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core", default-features = false } getter-storage = { path = "../getter-storage" } +serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "1" diff --git a/crates/getter-operations/src/legacy_room.rs b/crates/getter-operations/src/legacy_room.rs new file mode 100644 index 0000000..40c7c34 --- /dev/null +++ b/crates/getter-operations/src/legacy_room.rs @@ -0,0 +1,426 @@ +//! Getter-owned legacy Room migration operations. +//! +//! Platform layers may prepare/copy/checkpoint a legacy SQLite database and pass +//! its path to getter, but this module owns reading Room rows, mapping legacy app +//! records, writing tracked package state, recording migration completion, and +//! producing sanitized migration reports. + +use getter_storage::legacy_room::{ + map_legacy_app, read_legacy_room_database, LegacyPackageResolution, LegacyRoomDbImport, + LegacyRoomImportWarning, LegacyRoomReadError, +}; +use getter_storage::{ + CacheDb, MainDb, MigrationRecordUpsert, StorageError, StoredPackageResolution, + StoredTrackedPackage, TrackedPackageUpsert, +}; +use serde::Serialize; +use serde_json::{json, Value}; +use std::fs; +use std::path::{Path, PathBuf}; + +const MAIN_DB_FILE: &str = "main.db"; +const CACHE_DB_FILE: &str = "cache.db"; +pub const MIGRATION_REPORTS_DIR: &str = "migration-reports"; +pub const LEGACY_ROOM_MIGRATION_ID: &str = "legacy-room-v17"; + +#[derive(Debug, thiserror::Error)] +pub enum LegacyRoomOperationError { + #[error("storage error: {0}")] + Storage(String), + #[error("unsupported legacy Room database")] + UnsupportedDb { report_path: PathBuf }, + #[error("invalid legacy Room database")] + InvalidDb { report_path: PathBuf }, +} + +impl LegacyRoomOperationError { + pub fn code(&self) -> &'static str { + match self { + Self::Storage(_) => "storage.error", + Self::UnsupportedDb { .. } => "migration.unsupported_db", + Self::InvalidDb { .. } => "migration.invalid_db", + } + } + + pub fn message(&self) -> &'static str { + match self { + Self::Storage(_) => "Getter storage operation failed", + Self::UnsupportedDb { .. } => "Legacy Room database version is not supported", + Self::InvalidDb { .. } => "Legacy Room database is invalid", + } + } + + pub fn detail(&self) -> Option { + match self { + Self::Storage(detail) => Some(detail.clone()), + Self::UnsupportedDb { .. } | Self::InvalidDb { .. } => None, + } + } + + pub fn report_path(&self) -> Option<&Path> { + match self { + Self::UnsupportedDb { report_path } | Self::InvalidDb { report_path } => { + Some(report_path.as_path()) + } + Self::Storage(_) => None, + } + } +} + +impl From for LegacyRoomOperationError { + fn from(source: StorageError) -> Self { + Self::Storage(source.to_string()) + } +} + +pub type LegacyRoomOperationResult = Result; + +pub fn import_room_db_json(data_dir: &Path, legacy_db: &Path) -> LegacyRoomOperationResult { + let db = open_main_db(data_dir)?; + if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { + let records = db.tracked_packages()?; + return Ok(json!({ + "already_imported": true, + "imported_records": 0, + "source_counts": { + "app_rows": 0, + "extra_app_rows": 0, + "hub_rows": 0, + "extra_hub_rows": 0, + }, + "warnings": [], + "apps": tracked_packages_json(records), + })); + } + + let import = read_legacy_room_database(legacy_db) + .map_err(|source| legacy_db_import_error(data_dir, legacy_db, source))?; + let imported_records = import.apps.len(); + let source_counts = source_counts_json(&import); + let warnings = import_warnings_json(&import.warnings); + if import.source_counts.app_rows > 0 && imported_records == 0 { + let report_path = create_migration_report_with_source_counts( + data_dir, + legacy_db, + "migration.invalid_db", + "Legacy Room database has app rows but no importable app rows", + 0, + 0, + &warnings, + Some(&source_counts), + )?; + return Err(LegacyRoomOperationError::InvalidDb { report_path }); + } + + import_legacy_room_db(&db, &import)?; + let report_path = create_migration_report_with_source_counts( + data_dir, + legacy_db, + "migration.imported", + "Legacy Room database imported", + imported_records as u64, + imported_records as u64, + &warnings, + Some(&source_counts), + )?; + let records = db.tracked_packages()?; + Ok(json!({ + "report_path": report_path, + "imported_records": imported_records, + "source_counts": source_counts, + "warnings": warnings, + "apps": tracked_packages_json(records), + })) +} + +pub fn report_list_json(data_dir: &Path) -> LegacyRoomOperationResult { + open_initialized_storage(data_dir)?; + Ok(json!({ "reports": list_migration_reports(data_dir)? })) +} + +fn initialize_storage(data_dir: &Path) -> LegacyRoomOperationResult<()> { + fs::create_dir_all(data_dir).map_err(|source| { + LegacyRoomOperationError::Storage(format!("failed to create data directory: {source}")) + })?; + MainDb::open(data_dir.join(MAIN_DB_FILE))?; + CacheDb::open(data_dir.join(CACHE_DB_FILE))?; + Ok(()) +} + +fn open_initialized_storage(data_dir: &Path) -> LegacyRoomOperationResult<()> { + initialize_storage(data_dir) +} + +fn open_main_db(data_dir: &Path) -> LegacyRoomOperationResult { + initialize_storage(data_dir)?; + Ok(MainDb::open(data_dir.join(MAIN_DB_FILE))?) +} + +fn import_legacy_room_db( + db: &MainDb, + import: &LegacyRoomDbImport, +) -> LegacyRoomOperationResult<()> { + let mut packages = Vec::new(); + for app in &import.apps { + let mapping = map_legacy_app(&app.app, Some(&app.user_state)) + .map_err(|source| LegacyRoomOperationError::Storage(source.to_string()))?; + packages.push(tracked_package_upsert(mapping)); + } + + let report_json = json!({ + "ok": true, + "source": "legacy-room-db", + "version": import.version, + "imported_records": import.apps.len(), + "source_counts": source_counts_json(import), + "warnings": import_warnings_json(&import.warnings), + }) + .to_string(); + db.import_tracked_packages_with_migration_record( + &packages, + &MigrationRecordUpsert { + id: LEGACY_ROOM_MIGRATION_ID, + source: "legacy-room-db", + report_json: &report_json, + }, + )?; + Ok(()) +} + +fn tracked_package_upsert( + mapping: getter_storage::legacy_room::LegacyAppMapping, +) -> TrackedPackageUpsert { + TrackedPackageUpsert { + package_id: mapping.package_id, + enabled: true, + favorite: mapping.user_state.favorite, + ignored_version: mapping.user_state.ignored_version, + repository_id: None, + package_resolution: stored_resolution(mapping.package_resolution), + } +} + +fn stored_resolution(resolution: LegacyPackageResolution) -> StoredPackageResolution { + match resolution { + LegacyPackageResolution::OfficialRepositoryPackage => { + StoredPackageResolution::OfficialRepositoryPackage + } + LegacyPackageResolution::GenerateLocalPackage => { + StoredPackageResolution::GenerateLocalPackage + } + LegacyPackageResolution::MissingPackageDefinition => { + StoredPackageResolution::MissingPackageDefinition + } + } +} + +fn legacy_db_import_error( + data_dir: &Path, + legacy_db: &Path, + source: LegacyRoomReadError, +) -> LegacyRoomOperationError { + let (code, unsupported) = match source { + LegacyRoomReadError::UnsupportedVersion { .. } => ("migration.unsupported_db", true), + LegacyRoomReadError::Sqlite(_) | LegacyRoomReadError::MissingRequiredTable(_) => { + ("migration.invalid_db", false) + } + }; + match create_migration_report_with_source_counts( + data_dir, + legacy_db, + code, + &source.to_string(), + 0, + 0, + &[], + None, + ) { + Ok(report_path) if unsupported => LegacyRoomOperationError::UnsupportedDb { report_path }, + Ok(report_path) => LegacyRoomOperationError::InvalidDb { report_path }, + Err(error) => error, + } +} + +fn tracked_packages_json(packages: Vec) -> Vec { + packages + .into_iter() + .map(|package| { + json!({ + "id": package.package_id.to_string(), + "enabled": package.enabled, + "favorite": package.favorite, + "ignored_version": package.ignored_version, + "repository_id": package.repository_id.map(|id| id.to_string()), + "package_resolution": package.package_resolution.as_str(), + }) + }) + .collect() +} + +fn source_counts_json(import: &LegacyRoomDbImport) -> Value { + json!({ + "app_rows": import.source_counts.app_rows, + "extra_app_rows": import.source_counts.extra_app_rows, + "hub_rows": import.source_counts.hub_rows, + "extra_hub_rows": import.source_counts.extra_hub_rows, + }) +} + +fn import_warnings_json(warnings: &[LegacyRoomImportWarning]) -> Vec { + warnings + .iter() + .map(|warning| { + json!({ + "code": warning.code(), + "message": warning.message(), + }) + }) + .collect() +} + +#[allow(clippy::too_many_arguments)] +fn create_migration_report_with_source_counts( + data_dir: &Path, + source_file: &Path, + code: &str, + detail: &str, + imported_records: u64, + tracked_records: u64, + warnings: &[Value], + source_counts: Option<&Value>, +) -> LegacyRoomOperationResult { + let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); + fs::create_dir_all(&reports_dir).map_err(|source| { + LegacyRoomOperationError::Storage(format!( + "failed to create migration report directory: {source}" + )) + })?; + let report_path = reports_dir.join(report_file_name(code)); + let report = MigrationReport { + ok: code == "migration.imported", + code, + message: match code { + "migration.invalid_db" => "Legacy Room database is invalid", + "migration.unsupported_db" => "Legacy Room database version is not supported", + "migration.imported" => "Legacy Room data imported", + _ => "Legacy migration failed", + }, + source_file_name: source_file.file_name().and_then(|name| name.to_str()), + detail, + imported_records, + tracked_records, + warnings, + source_counts, + }; + let bytes = serde_json::to_vec_pretty(&report).map_err(|source| { + LegacyRoomOperationError::Storage(format!("failed to serialize report: {source}")) + })?; + fs::write(&report_path, bytes).map_err(|source| { + LegacyRoomOperationError::Storage(format!("failed to write report: {source}")) + })?; + Ok(report_path) +} + +fn report_file_name(code: &str) -> String { + format!("{}.json", code.replace('.', "-")) +} + +fn list_migration_reports(data_dir: &Path) -> LegacyRoomOperationResult> { + let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); + if !reports_dir.exists() { + return Ok(Vec::new()); + } + + let mut report_paths = fs::read_dir(&reports_dir) + .map_err(|source| { + LegacyRoomOperationError::Storage(format!("failed to read migration reports: {source}")) + })? + .map(|entry| entry.map(|entry| entry.path())) + .collect::, _>>() + .map_err(|source| { + LegacyRoomOperationError::Storage(format!( + "failed to read migration report entry: {source}" + )) + })?; + report_paths.retain(|path| { + path.extension() + .is_some_and(|extension| extension == "json") + }); + report_paths.sort(); + + report_paths + .into_iter() + .map(|path| { + let bytes = fs::read(&path).map_err(|source| { + LegacyRoomOperationError::Storage(format!( + "failed to read migration report '{}': {source}", + path.display() + )) + })?; + let report: Value = serde_json::from_slice(&bytes).map_err(|source| { + LegacyRoomOperationError::Storage(format!( + "failed to parse migration report '{}': {source}", + path.display() + )) + })?; + Ok(json!({ + "ok": report.get("ok").and_then(Value::as_bool).unwrap_or(false), + "code": report.get("code").and_then(Value::as_str).unwrap_or("migration.unknown"), + "message": report.get("message").and_then(Value::as_str).unwrap_or("Legacy migration report"), + "source_file_name": report + .get("source_file_name") + .or_else(|| report.get("bundle_file_name")) + .and_then(Value::as_str), + "imported_records": report.get("imported_records").and_then(Value::as_u64).unwrap_or(0), + "tracked_records": report.get("tracked_records").and_then(Value::as_u64).unwrap_or(0), + "warnings": report + .get("warnings") + .cloned() + .unwrap_or_else(|| Value::Array(Vec::new())), + "source_counts": report.get("source_counts").cloned().unwrap_or(Value::Null), + })) + }) + .collect() +} + +#[derive(Debug, Serialize)] +struct MigrationReport<'a> { + ok: bool, + code: &'a str, + message: &'a str, + source_file_name: Option<&'a str>, + detail: &'a str, + imported_records: u64, + tracked_records: u64, + warnings: &'a [Value], + #[serde(skip_serializing_if = "Option::is_none")] + source_counts: Option<&'a Value>, +} + +#[cfg(test)] +mod tests { + use super::*; + use std::time::{SystemTime, UNIX_EPOCH}; + + #[test] + fn import_room_db_rejects_malformed_db_with_report() { + let temp = temp_dir("malformed"); + fs::create_dir_all(&temp).unwrap(); + let db_path = temp.join("legacy.db"); + fs::write(&db_path, b"not sqlite").unwrap(); + + let error = import_room_db_json(&temp.join("getter"), &db_path).unwrap_err(); + + assert!(matches!(error, LegacyRoomOperationError::InvalidDb { .. })); + assert_eq!(error.code(), "migration.invalid_db"); + assert!(error.report_path().is_some_and(Path::exists)); + } + + fn temp_dir(name: &str) -> PathBuf { + let nanos = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + std::env::temp_dir().join(format!("upgradeall-legacy-room-op-{name}-{nanos}")) + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index f4432e2..71a0ca1 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -6,3 +6,4 @@ //! generated package files, manifests, and tracked state live here. pub mod autogen; +pub mod legacy_room; From 8e3e3c12803f1f8c2659361bfeba78c666917772 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 10:20:45 +0800 Subject: [PATCH 19/71] feat(core): add in-memory task runtime --- crates/getter-core/src/autogen.rs | 4 + crates/getter-core/src/lib.rs | 1 + crates/getter-core/src/runtime.rs | 936 ++++++++++++++++++++++++++++++ 3 files changed, 941 insertions(+) create mode 100644 crates/getter-core/src/runtime.rs diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs index 265c6be..2001ac5 100644 --- a/crates/getter-core/src/autogen.rs +++ b/crates/getter-core/src/autogen.rs @@ -369,8 +369,11 @@ fn fnv1a64(bytes: &[u8]) -> u64 { #[cfg(test)] mod tests { use super::*; + #[cfg(feature = "lua")] use crate::lua::evaluate_package_file; + #[cfg(feature = "lua")] use crate::repository::RepositoryLayout; + #[cfg(feature = "lua")] use std::fs; #[test] @@ -492,6 +495,7 @@ mod tests { ); } + #[cfg(feature = "lua")] #[test] fn generated_lua_evaluates_through_package_boundary() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index ce5c030..96cdb95 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -15,6 +15,7 @@ pub mod diagnostics; #[cfg(feature = "lua")] pub mod lua; pub mod repository; +pub mod runtime; pub mod task; pub mod update; diff --git a/crates/getter-core/src/runtime.rs b/crates/getter-core/src/runtime.rs new file mode 100644 index 0000000..3dd4b7c --- /dev/null +++ b/crates/getter-core/src/runtime.rs @@ -0,0 +1,936 @@ +//! In-memory Phase D runtime for getter-owned update/download/install tasks. +//! +//! This module implements the ADR-0011 task/action/runtime-notification shape. +//! It deliberately keeps task state in the current process only: no SQLite task +//! persistence, no daemon, no recovery, and no replayable event log. Product +//! embedders can hold this runtime as a process-lifetime singleton and expose +//! [`RuntimeNotification`] through a push stream. + +use crate::{PackageId, UpdateAction}; +use serde::{Deserialize, Serialize}; +use std::collections::{BTreeMap, HashMap}; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PackageVersionLuaObject { + pub object_id: String, + pub dependency_digest: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SealedActionPlan { + pub package_id: PackageId, + #[serde(default)] + pub actions: Vec, + pub lua_object: PackageVersionLuaObject, +} + +impl SealedActionPlan { + fn has_install_action(&self) -> bool { + self.actions + .iter() + .any(|action| matches!(action, UpdateAction::Install { .. })) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct IssuedAction { + pub action_id: String, + pub package_id: PackageId, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskSnapshot { + pub task_id: String, + pub package_id: PackageId, + pub status: RuntimeTaskStatus, + pub phase: TaskPhase, + #[serde(default)] + pub progress: Option, + pub capabilities: TaskCapabilities, + #[serde(default)] + pub current_diagnostic: Option, + pub updated_at: u64, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum RuntimeTaskStatus { + Queued, + Running, + Paused, + Failed, + Completed, + Canceled, +} + +impl RuntimeTaskStatus { + pub const fn is_active(self) -> bool { + matches!(self, Self::Queued | Self::Running | Self::Paused) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskPhase { + pub category: TaskPhaseCategory, + #[serde(default)] + pub reason: Option, +} + +impl TaskPhase { + pub const fn new(category: TaskPhaseCategory) -> Self { + Self { + category, + reason: None, + } + } + + pub const fn with_reason(category: TaskPhaseCategory, reason: TaskPhaseReason) -> Self { + Self { + category, + reason: Some(reason), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TaskPhaseCategory { + Queued, + Download, + WaitingUser, + Install, + Completed, + Failed, + Canceled, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TaskPhaseReason { + InstallHandoff, + PackageLocked, + UserRejected, + DownloadFailed, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskProgress { + pub unit: TaskProgressUnit, + pub current: u64, + #[serde(default)] + pub total: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TaskProgressUnit { + Percent, + Bit, +} + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskCapabilities { + pub cancel: bool, + pub pause: bool, + pub resume: bool, + pub retry: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TaskDiagnostic { + pub code: String, + pub message: String, + pub severity: DiagnosticSeverity, +} + +impl TaskDiagnostic { + pub fn error(code: impl Into, message: impl Into) -> Self { + Self { + code: code.into(), + message: message.into(), + severity: DiagnosticSeverity::Error, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum DiagnosticSeverity { + Info, + Warning, + Error, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum RuntimeNotification { + TaskChanged { task: TaskSnapshot }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum UserResult { + Accepted, + Rejected, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TaskCleanMode { + /// Remove completed and canceled tasks only. + Default, + /// Remove failed tasks only. + Failed, + /// Remove completed, canceled, and failed tasks. + AllInactive, +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum RuntimeError { + #[error("update action is no longer available: {0}")] + ActionNotFound(String), + #[error("task not found: {0}")] + TaskNotFound(String), + #[error("task is not waiting for a user result: {0}")] + TaskNotWaitingForUser(String), + #[error("task cannot be paused in its current state: {0}")] + PauseNotSupported(String), + #[error("task cannot be resumed in its current state: {0}")] + ResumeNotSupported(String), + #[error("task cannot be retried in its current state: {0}")] + RetryNotSupported(String), + #[error("task cannot be canceled in its current state: {0}")] + CancelNotSupported(String), + #[error("task is active and must be canceled before removal: {0}")] + TaskActive(String), +} + +impl RuntimeError { + pub const fn code(&self) -> &'static str { + match self { + Self::ActionNotFound(_) => "action.not_found", + Self::TaskNotFound(_) => "task.not_found", + Self::TaskNotWaitingForUser(_) => "task.not_waiting_for_user", + Self::PauseNotSupported(_) => "task.pause_not_supported", + Self::ResumeNotSupported(_) => "task.resume_not_supported", + Self::RetryNotSupported(_) => "task.retry_not_supported", + Self::CancelNotSupported(_) => "task.cancel_not_supported", + Self::TaskActive(_) => "task.active", + } + } +} + +type RuntimeNotificationSink = Box; + +#[derive(Default)] +pub struct GetterRuntime { + next_action_id: u64, + next_task_id: u64, + logical_clock: u64, + actions: HashMap, + tasks: BTreeMap, + package_locks: HashMap, + notification_sink: Option, +} + +impl GetterRuntime { + pub fn new() -> Self { + Self::default() + } + + pub fn set_notification_sink( + &mut self, + sink: impl FnMut(RuntimeNotification) + Send + 'static, + ) { + self.notification_sink = Some(Box::new(sink)); + } + + pub fn issue_action(&mut self, plan: SealedActionPlan) -> IssuedAction { + self.next_action_id += 1; + let action_id = format!("action-{}", self.next_action_id); + let package_id = plan.package_id.clone(); + self.actions.insert(action_id.clone(), plan); + IssuedAction { + action_id, + package_id, + } + } + + pub fn submit_action(&mut self, action_id: &str) -> Result { + let plan = self + .actions + .remove(action_id) + .ok_or_else(|| RuntimeError::ActionNotFound(action_id.to_owned()))?; + self.next_task_id += 1; + let task_id = format!("task-{}", self.next_task_id); + let task = RuntimeTask::new(task_id.clone(), plan, self.tick()); + let snapshot = task.snapshot(); + self.tasks.insert(task_id, task); + self.notify_task_changed(snapshot.clone()); + Ok(snapshot) + } + + pub fn task(&self, task_id: &str) -> Result { + Ok(self.task_ref(task_id)?.snapshot()) + } + + pub fn tasks(&self) -> Vec { + self.tasks.values().map(RuntimeTask::snapshot).collect() + } + + pub fn active_tasks(&self) -> Vec { + self.tasks + .values() + .filter(|task| task.status.is_active()) + .map(RuntimeTask::snapshot) + .collect() + } + + pub fn tasks_for_package(&self, package_id: &PackageId) -> Vec { + self.tasks + .values() + .filter(|task| &task.plan.package_id == package_id) + .map(RuntimeTask::snapshot) + .collect() + } + + pub fn start_task(&mut self, task_id: &str) -> Result { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + match task.status { + RuntimeTaskStatus::Queued => task.start_download(clock), + RuntimeTaskStatus::Running => {} + _ => return Err(RuntimeError::PauseNotSupported(task_id.to_owned())), + } + } + self.snapshot_and_notify(task_id) + } + + pub fn set_download_progress( + &mut self, + task_id: &str, + current_bits: u64, + total_bits: Option, + ) -> Result { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + if !task.is_running_download() { + return Err(RuntimeError::PauseNotSupported(task_id.to_owned())); + } + task.progress = Some(TaskProgress { + unit: TaskProgressUnit::Bit, + current: current_bits, + total: total_bits, + }); + task.updated_at = clock; + } + self.snapshot_and_notify(task_id) + } + + pub fn complete_download(&mut self, task_id: &str) -> Result { + let needs_install = { + let task = self.task_ref(task_id)?; + task.plan.has_install_action() + }; + + if needs_install { + self.enter_install_handoff(task_id) + } else { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + if !matches!( + task.status, + RuntimeTaskStatus::Queued | RuntimeTaskStatus::Running + ) { + return Err(RuntimeError::RetryNotSupported(task_id.to_owned())); + } + task.complete(clock); + } + self.snapshot_and_notify(task_id) + } + } + + pub fn pause_task(&mut self, task_id: &str) -> Result { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + if !task.is_running_download() { + return Err(RuntimeError::PauseNotSupported(task_id.to_owned())); + } + task.status = RuntimeTaskStatus::Paused; + task.updated_at = clock; + } + self.snapshot_and_notify(task_id) + } + + pub fn resume_task(&mut self, task_id: &str) -> Result { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + if task.status != RuntimeTaskStatus::Paused { + return Err(RuntimeError::ResumeNotSupported(task_id.to_owned())); + } + task.status = RuntimeTaskStatus::Running; + task.phase = TaskPhase::new(TaskPhaseCategory::Download); + task.updated_at = clock; + } + self.snapshot_and_notify(task_id) + } + + pub fn user_result( + &mut self, + task_id: &str, + result: UserResult, + reason: Option<&str>, + ) -> Result { + { + let task = self.task_ref(task_id)?; + if !task.is_waiting_user() { + return Err(RuntimeError::TaskNotWaitingForUser(task_id.to_owned())); + } + } + self.unlock_package_for_task(task_id); + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + match result { + UserResult::Accepted => task.complete(clock), + UserResult::Rejected => task.fail( + clock, + TaskPhase::with_reason( + TaskPhaseCategory::Failed, + TaskPhaseReason::UserRejected, + ), + TaskDiagnostic::error( + "user.rejected", + reason.unwrap_or("User rejected the pending step"), + ), + RetryResume::InstallHandoff, + ), + } + } + self.snapshot_and_notify(task_id) + } + + pub fn cancel_task(&mut self, task_id: &str) -> Result { + { + let task = self.task_ref(task_id)?; + if !matches!( + task.status, + RuntimeTaskStatus::Queued | RuntimeTaskStatus::Running | RuntimeTaskStatus::Paused + ) { + return Err(RuntimeError::CancelNotSupported(task_id.to_owned())); + } + } + self.unlock_package_for_task(task_id); + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + task.cancel(clock); + } + self.snapshot_and_notify(task_id) + } + + pub fn retry_task(&mut self, task_id: &str) -> Result { + let retry_resume = { + let task = self.task_ref(task_id)?; + if task.status != RuntimeTaskStatus::Failed { + return Err(RuntimeError::RetryNotSupported(task_id.to_owned())); + } + task.retry_resume + }; + match retry_resume { + RetryResume::Download => { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + task.start_download(clock); + } + self.snapshot_and_notify(task_id) + } + RetryResume::InstallHandoff => self.enter_install_handoff(task_id), + } + } + + pub fn remove_task(&mut self, task_id: &str) -> Result { + let task = self.task_ref(task_id)?; + if task.status.is_active() { + return Err(RuntimeError::TaskActive(task_id.to_owned())); + } + let removed = self + .tasks + .remove(task_id) + .expect("task existence checked before remove"); + Ok(removed.snapshot()) + } + + pub fn clean_tasks(&mut self, mode: TaskCleanMode) -> Vec { + let task_ids: Vec = self + .tasks + .iter() + .filter_map(|(task_id, task)| { + let remove = match mode { + TaskCleanMode::Default => matches!( + task.status, + RuntimeTaskStatus::Completed | RuntimeTaskStatus::Canceled + ), + TaskCleanMode::Failed => task.status == RuntimeTaskStatus::Failed, + TaskCleanMode::AllInactive => !task.status.is_active(), + }; + remove.then(|| task_id.clone()) + }) + .collect(); + + task_ids + .into_iter() + .filter_map(|task_id| { + let removed = self.tasks.remove(&task_id)?; + Some(removed.snapshot()) + }) + .collect() + } + + fn enter_install_handoff(&mut self, task_id: &str) -> Result { + let package_id = self.task_ref(task_id)?.plan.package_id.clone(); + if self.package_locks.contains_key(&package_id) { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + task.fail( + clock, + TaskPhase::with_reason( + TaskPhaseCategory::Install, + TaskPhaseReason::PackageLocked, + ), + TaskDiagnostic::error( + "package.locked", + format!( + "Package {} is already being modified by another task", + package_id + ), + ), + RetryResume::InstallHandoff, + ); + } + return self.snapshot_and_notify(task_id); + } + + self.package_locks.insert(package_id, task_id.to_owned()); + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + task.enter_waiting_install(clock); + } + self.snapshot_and_notify(task_id) + } + + fn unlock_package_for_task(&mut self, task_id: &str) { + self.package_locks + .retain(|_, locked_by_task| locked_by_task != task_id); + } + + fn snapshot_and_notify(&mut self, task_id: &str) -> Result { + let snapshot = self.task(task_id)?; + self.notify_task_changed(snapshot.clone()); + Ok(snapshot) + } + + fn notify_task_changed(&mut self, task: TaskSnapshot) { + if let Some(sink) = self.notification_sink.as_mut() { + sink(RuntimeNotification::TaskChanged { task }); + } + } + + fn task_ref(&self, task_id: &str) -> Result<&RuntimeTask, RuntimeError> { + self.tasks + .get(task_id) + .ok_or_else(|| RuntimeError::TaskNotFound(task_id.to_owned())) + } + + fn task_mut(&mut self, task_id: &str) -> Result<&mut RuntimeTask, RuntimeError> { + self.tasks + .get_mut(task_id) + .ok_or_else(|| RuntimeError::TaskNotFound(task_id.to_owned())) + } + + fn tick(&mut self) -> u64 { + self.logical_clock += 1; + self.logical_clock + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum RetryResume { + Download, + InstallHandoff, +} + +#[derive(Debug, Clone)] +struct RuntimeTask { + id: String, + plan: SealedActionPlan, + status: RuntimeTaskStatus, + phase: TaskPhase, + progress: Option, + current_diagnostic: Option, + retry_resume: RetryResume, + updated_at: u64, +} + +impl RuntimeTask { + fn new(id: String, plan: SealedActionPlan, updated_at: u64) -> Self { + Self { + id, + plan, + status: RuntimeTaskStatus::Queued, + phase: TaskPhase::new(TaskPhaseCategory::Queued), + progress: None, + current_diagnostic: None, + retry_resume: RetryResume::Download, + updated_at, + } + } + + fn snapshot(&self) -> TaskSnapshot { + TaskSnapshot { + task_id: self.id.clone(), + package_id: self.plan.package_id.clone(), + status: self.status, + phase: self.phase.clone(), + progress: self.progress.clone(), + capabilities: self.capabilities(), + current_diagnostic: self.current_diagnostic.clone(), + updated_at: self.updated_at, + } + } + + fn capabilities(&self) -> TaskCapabilities { + TaskCapabilities { + cancel: matches!( + self.status, + RuntimeTaskStatus::Queued | RuntimeTaskStatus::Running | RuntimeTaskStatus::Paused + ), + pause: self.is_running_download(), + resume: self.status == RuntimeTaskStatus::Paused, + retry: self.status == RuntimeTaskStatus::Failed, + } + } + + fn is_running_download(&self) -> bool { + self.status == RuntimeTaskStatus::Running + && self.phase.category == TaskPhaseCategory::Download + } + + fn is_waiting_user(&self) -> bool { + self.status == RuntimeTaskStatus::Running + && self.phase + == TaskPhase::with_reason( + TaskPhaseCategory::WaitingUser, + TaskPhaseReason::InstallHandoff, + ) + } + + fn start_download(&mut self, updated_at: u64) { + self.status = RuntimeTaskStatus::Running; + self.phase = TaskPhase::new(TaskPhaseCategory::Download); + self.progress = Some(TaskProgress { + unit: TaskProgressUnit::Bit, + current: 0, + total: None, + }); + self.current_diagnostic = None; + self.updated_at = updated_at; + } + + fn enter_waiting_install(&mut self, updated_at: u64) { + self.status = RuntimeTaskStatus::Running; + self.phase = TaskPhase::with_reason( + TaskPhaseCategory::WaitingUser, + TaskPhaseReason::InstallHandoff, + ); + self.progress = None; + self.current_diagnostic = None; + self.updated_at = updated_at; + } + + fn complete(&mut self, updated_at: u64) { + self.status = RuntimeTaskStatus::Completed; + self.phase = TaskPhase::new(TaskPhaseCategory::Completed); + self.progress = None; + self.current_diagnostic = None; + self.updated_at = updated_at; + } + + fn cancel(&mut self, updated_at: u64) { + self.status = RuntimeTaskStatus::Canceled; + self.phase = TaskPhase::new(TaskPhaseCategory::Canceled); + self.progress = None; + self.current_diagnostic = None; + self.updated_at = updated_at; + } + + fn fail( + &mut self, + updated_at: u64, + phase: TaskPhase, + diagnostic: TaskDiagnostic, + retry_resume: RetryResume, + ) { + self.status = RuntimeTaskStatus::Failed; + self.phase = phase; + self.progress = None; + self.current_diagnostic = Some(diagnostic); + self.retry_resume = retry_resume; + self.updated_at = updated_at; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{PackageKind, UpdateAction}; + use std::sync::{Arc, Mutex}; + + #[test] + fn submit_consumes_action_id_and_pushes_snapshot() { + let notifications = Arc::new(Mutex::new(Vec::new())); + let sink_notifications = notifications.clone(); + let mut runtime = GetterRuntime::new(); + runtime.set_notification_sink(move |notification| { + sink_notifications.lock().unwrap().push(notification); + }); + let action = runtime.issue_action(plan("android/org.fdroid.fdroid")); + + let task = runtime.submit_action(&action.action_id).unwrap(); + + assert_eq!(task.task_id, "task-1"); + assert_eq!(task.status, RuntimeTaskStatus::Queued); + assert_eq!(task.capabilities.cancel, true); + let error = runtime.submit_action(&action.action_id).unwrap_err(); + assert_eq!(error.code(), "action.not_found"); + assert_eq!(notifications.lock().unwrap().len(), 1); + } + + #[test] + fn fake_install_waits_for_generic_user_result() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + + runtime.start_task(&task_id).unwrap(); + let waiting = runtime.complete_download(&task_id).unwrap(); + + assert_eq!(waiting.status, RuntimeTaskStatus::Running); + assert_eq!( + waiting.phase, + TaskPhase::with_reason( + TaskPhaseCategory::WaitingUser, + TaskPhaseReason::InstallHandoff + ) + ); + assert_eq!(waiting.capabilities.pause, false); + assert_eq!(waiting.capabilities.resume, false); + assert_eq!(waiting.capabilities.cancel, true); + + let completed = runtime + .user_result(&task_id, UserResult::Accepted, None) + .unwrap(); + assert_eq!(completed.status, RuntimeTaskStatus::Completed); + assert_eq!( + completed.phase, + TaskPhase::new(TaskPhaseCategory::Completed) + ); + } + + #[test] + fn rejected_user_result_fails_and_retry_reuses_same_task() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + runtime.start_task(&task_id).unwrap(); + runtime.complete_download(&task_id).unwrap(); + + let failed = runtime + .user_result(&task_id, UserResult::Rejected, Some("not now")) + .unwrap(); + + assert_eq!(failed.status, RuntimeTaskStatus::Failed); + assert_eq!(failed.capabilities.retry, true); + assert_eq!( + failed.current_diagnostic.as_ref().unwrap().code, + "user.rejected" + ); + + let retried = runtime.retry_task(&task_id).unwrap(); + assert_eq!(retried.task_id, task_id); + assert_eq!(retried.status, RuntimeTaskStatus::Running); + assert_eq!( + retried.phase, + TaskPhase::with_reason( + TaskPhaseCategory::WaitingUser, + TaskPhaseReason::InstallHandoff + ) + ); + } + + #[test] + fn user_result_outside_waiting_user_is_an_error_without_mutation() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + let before = runtime.task(&task_id).unwrap(); + + let error = runtime + .user_result(&task_id, UserResult::Accepted, None) + .unwrap_err(); + + assert_eq!(error.code(), "task.not_waiting_for_user"); + assert_eq!(runtime.task(&task_id).unwrap(), before); + } + + #[test] + fn pause_resume_are_download_phase_controls_only() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + runtime.start_task(&task_id).unwrap(); + runtime + .set_download_progress(&task_id, 42, Some(100)) + .unwrap(); + + let paused = runtime.pause_task(&task_id).unwrap(); + assert_eq!(paused.status, RuntimeTaskStatus::Paused); + assert_eq!(paused.capabilities.resume, true); + assert_eq!(paused.capabilities.cancel, true); + + let resumed = runtime.resume_task(&task_id).unwrap(); + assert_eq!(resumed.status, RuntimeTaskStatus::Running); + assert_eq!(resumed.phase, TaskPhase::new(TaskPhaseCategory::Download)); + + runtime.complete_download(&task_id).unwrap(); + let error = runtime.pause_task(&task_id).unwrap_err(); + assert_eq!(error.code(), "task.pause_not_supported"); + } + + #[test] + fn same_package_install_lock_fails_later_task_without_waiting() { + let mut runtime = GetterRuntime::new(); + let first_task = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + let second_task = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + + runtime.start_task(&first_task).unwrap(); + runtime.complete_download(&first_task).unwrap(); + runtime.start_task(&second_task).unwrap(); + let failed = runtime.complete_download(&second_task).unwrap(); + + assert_eq!(failed.status, RuntimeTaskStatus::Failed); + assert_eq!( + failed.phase, + TaskPhase::with_reason(TaskPhaseCategory::Install, TaskPhaseReason::PackageLocked) + ); + assert_eq!( + failed.current_diagnostic.as_ref().unwrap().code, + "package.locked" + ); + + runtime + .user_result(&first_task, UserResult::Accepted, None) + .unwrap(); + let retried = runtime.retry_task(&second_task).unwrap(); + assert_eq!(retried.status, RuntimeTaskStatus::Running); + assert_eq!( + retried.phase, + TaskPhase::with_reason( + TaskPhaseCategory::WaitingUser, + TaskPhaseReason::InstallHandoff + ) + ); + } + + #[test] + fn cancel_remove_and_clean_follow_in_memory_task_lifecycle() { + let mut runtime = GetterRuntime::new(); + let canceled = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + let failed = submit_plan(&mut runtime, plan("android/com.termux")); + let completed = submit_plan(&mut runtime, plan_without_install("generic/example")); + + runtime.cancel_task(&canceled).unwrap(); + runtime.start_task(&failed).unwrap(); + runtime.complete_download(&failed).unwrap(); + runtime + .user_result(&failed, UserResult::Rejected, None) + .unwrap(); + runtime.start_task(&completed).unwrap(); + runtime.complete_download(&completed).unwrap(); + + let removed_by_default = runtime.clean_tasks(TaskCleanMode::Default); + let removed_ids: Vec<_> = removed_by_default + .into_iter() + .map(|task| task.task_id) + .collect(); + assert!(removed_ids.contains(&canceled)); + assert!(removed_ids.contains(&completed)); + assert!(!removed_ids.contains(&failed)); + assert_eq!( + runtime.task(&failed).unwrap().status, + RuntimeTaskStatus::Failed + ); + + let removed_failed = runtime.remove_task(&failed).unwrap(); + assert_eq!(removed_failed.status, RuntimeTaskStatus::Failed); + assert_eq!(runtime.task(&failed).unwrap_err().code(), "task.not_found"); + } + + #[test] + fn active_tasks_cannot_be_removed_and_can_be_canceled_while_paused() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + runtime.start_task(&task_id).unwrap(); + runtime.pause_task(&task_id).unwrap(); + + let error = runtime.remove_task(&task_id).unwrap_err(); + assert_eq!(error.code(), "task.active"); + + let canceled = runtime.cancel_task(&task_id).unwrap(); + assert_eq!(canceled.status, RuntimeTaskStatus::Canceled); + runtime.remove_task(&task_id).unwrap(); + } + + fn submit_plan(runtime: &mut GetterRuntime, plan: SealedActionPlan) -> String { + let action = runtime.issue_action(plan); + runtime.submit_action(&action.action_id).unwrap().task_id + } + + fn plan(package_id: &str) -> SealedActionPlan { + SealedActionPlan { + package_id: package_id.parse().unwrap(), + lua_object: PackageVersionLuaObject { + object_id: format!("lua:{package_id}:1.0"), + dependency_digest: "sha256-test".to_owned(), + }, + actions: vec![ + UpdateAction::Download { + url: "https://example.invalid/app.apk".to_owned(), + file_name: "app.apk".to_owned(), + }, + UpdateAction::Install { + installer: "android_package".to_owned(), + file: "app.apk".to_owned(), + }, + ], + } + } + + fn plan_without_install(package_id: &str) -> SealedActionPlan { + SealedActionPlan { + package_id: PackageId::new(PackageKind::Generic, package_id.split('/').nth(1).unwrap()) + .unwrap(), + lua_object: PackageVersionLuaObject { + object_id: format!("lua:{package_id}:1.0"), + dependency_digest: "sha256-test".to_owned(), + }, + actions: vec![UpdateAction::Download { + url: "https://example.invalid/file.bin".to_owned(), + file_name: "file.bin".to_owned(), + }], + } + } +} From aa0b75eed55f5be2614ec40623c13aa1982d4559 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 10:37:13 +0800 Subject: [PATCH 20/71] feat(operations): expose runtime task controls --- crates/getter-operations/src/lib.rs | 1 + crates/getter-operations/src/runtime.rs | 373 ++++++++++++++++++++++++ 2 files changed, 374 insertions(+) create mode 100644 crates/getter-operations/src/runtime.rs diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 71a0ca1..6d75c88 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -7,3 +7,4 @@ pub mod autogen; pub mod legacy_room; +pub mod runtime; diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs new file mode 100644 index 0000000..0d6e11d --- /dev/null +++ b/crates/getter-operations/src/runtime.rs @@ -0,0 +1,373 @@ +//! Shared JSON operation helpers for the in-memory Phase D getter runtime. +//! +//! These functions are intentionally thin over [`getter_core::runtime`]. They +//! give embedders (native bridge, future single-process debug CLI, tests) one +//! place to map JSON requests into runtime controls without reintroducing +//! persisted task state or duplicating task/control semantics in Flutter or +//! Android glue. + +use getter_core::{ + runtime::{ + GetterRuntime, IssuedAction, RuntimeError, SealedActionPlan, TaskCleanMode, TaskSnapshot, + UserResult, + }, + PackageId, +}; +use serde::Deserialize; +use serde_json::{json, Value}; + +#[derive(Debug, thiserror::Error)] +pub enum RuntimeOperationError { + #[error("invalid runtime request: {0}")] + InvalidRequest(String), + #[error("runtime error: {0}")] + Runtime(#[from] RuntimeError), + #[error("runtime response serialization failed: {0}")] + Serialize(String), +} + +impl RuntimeOperationError { + pub fn code(&self) -> &'static str { + match self { + Self::InvalidRequest(_) => "runtime.invalid_request", + Self::Runtime(error) => error.code(), + Self::Serialize(_) => "runtime.serialize_error", + } + } + + pub fn message(&self) -> &'static str { + match self { + Self::InvalidRequest(_) => "Getter runtime request is invalid", + Self::Runtime(_) => "Getter runtime operation failed", + Self::Serialize(_) => "Getter runtime response serialization failed", + } + } + + pub fn detail(&self) -> Option { + match self { + Self::InvalidRequest(detail) | Self::Serialize(detail) => Some(detail.clone()), + Self::Runtime(error) => Some(error.to_string()), + } + } +} + +pub fn issue_action(runtime: &mut GetterRuntime, plan: SealedActionPlan) -> Value { + issued_action_json(runtime.issue_action(plan)) +} + +pub fn submit_action_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: SubmitActionRequest = parse_request(request_json)?; + task_json(runtime.submit_action(&request.action_id)?) +} + +pub fn task_get_json( + runtime: &GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.task(&request.task_id)?) +} + +pub fn task_list_json( + runtime: &GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskListRequest = parse_request(request_json)?; + let tasks = if let Some(package_id) = request.package_id.as_ref() { + runtime.tasks_for_package(package_id) + } else if request.active { + runtime.active_tasks() + } else { + runtime.tasks() + }; + tasks_json(tasks) +} + +pub fn task_start_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.start_task(&request.task_id)?) +} + +pub fn task_download_progress_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: DownloadProgressRequest = parse_request(request_json)?; + task_json(runtime.set_download_progress( + &request.task_id, + request.current_bits, + request.total_bits, + )?) +} + +pub fn task_complete_download_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.complete_download(&request.task_id)?) +} + +pub fn task_pause_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.pause_task(&request.task_id)?) +} + +pub fn task_resume_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.resume_task(&request.task_id)?) +} + +pub fn task_user_result_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: UserResultRequest = parse_request(request_json)?; + task_json(runtime.user_result(&request.task_id, request.result, request.reason.as_deref())?) +} + +pub fn task_cancel_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.cancel_task(&request.task_id)?) +} + +pub fn task_retry_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.retry_task(&request.task_id)?) +} + +pub fn task_remove_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: TaskIdRequest = parse_request(request_json)?; + task_json(runtime.remove_task(&request.task_id)?) +} + +pub fn task_clean_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: CleanTasksRequest = parse_request(request_json)?; + tasks_json(runtime.clean_tasks(request.mode()?)) +} + +fn parse_request(request_json: &str) -> Result +where + T: for<'de> Deserialize<'de>, +{ + serde_json::from_str(request_json) + .map_err(|source| RuntimeOperationError::InvalidRequest(source.to_string())) +} + +fn issued_action_json(action: IssuedAction) -> Value { + json!({ + "action_id": action.action_id, + "package_id": action.package_id, + }) +} + +fn task_json(task: TaskSnapshot) -> Result { + serde_json::to_value(task) + .map_err(|source| RuntimeOperationError::Serialize(source.to_string())) +} + +fn tasks_json(tasks: Vec) -> Result { + Ok(json!({ "tasks": tasks })) +} + +#[derive(Debug, Deserialize)] +struct SubmitActionRequest { + action_id: String, +} + +#[derive(Debug, Deserialize)] +struct TaskIdRequest { + task_id: String, +} + +#[derive(Debug, Default, Deserialize)] +struct TaskListRequest { + #[serde(default)] + active: bool, + #[serde(default)] + package_id: Option, +} + +#[derive(Debug, Deserialize)] +struct DownloadProgressRequest { + task_id: String, + current_bits: u64, + #[serde(default)] + total_bits: Option, +} + +#[derive(Debug, Deserialize)] +struct UserResultRequest { + task_id: String, + result: UserResult, + #[serde(default)] + reason: Option, +} + +#[derive(Debug, Default, Deserialize)] +struct CleanTasksRequest { + #[serde(default)] + mode: Option, +} + +impl CleanTasksRequest { + fn mode(self) -> Result { + match self.mode.as_deref().unwrap_or("default") { + "default" => Ok(TaskCleanMode::Default), + "failed" => Ok(TaskCleanMode::Failed), + "all_inactive" => Ok(TaskCleanMode::AllInactive), + other => Err(RuntimeOperationError::InvalidRequest(format!( + "unsupported task clean mode '{other}'" + ))), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::{runtime::PackageVersionLuaObject, runtime::RuntimeTaskStatus, UpdateAction}; + + #[test] + fn json_submit_and_user_result_round_trip_consumes_action() { + let mut runtime = GetterRuntime::new(); + let issued = issue_action(&mut runtime, plan("android/org.fdroid.fdroid")); + let action_id = issued["action_id"].as_str().unwrap(); + + let submitted = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap(); + + assert_eq!(submitted["status"], "queued"); + let error = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap_err(); + assert_eq!(error.code(), "action.not_found"); + + let task_id = submitted["task_id"].as_str().unwrap(); + task_start_json(&mut runtime, &json!({ "task_id": task_id }).to_string()).unwrap(); + task_complete_download_json(&mut runtime, &json!({ "task_id": task_id }).to_string()) + .unwrap(); + let completed = task_user_result_json( + &mut runtime, + &json!({ "task_id": task_id, "result": "accepted" }).to_string(), + ) + .unwrap(); + + assert_eq!(completed["status"], "completed"); + assert_eq!( + runtime.task(task_id).unwrap().status, + RuntimeTaskStatus::Completed + ); + } + + #[test] + fn json_list_filters_and_clean_modes_follow_runtime_semantics() { + let mut runtime = GetterRuntime::new(); + let fdroid = submit_plan(&mut runtime, plan("android/org.fdroid.fdroid")); + let termux = submit_plan(&mut runtime, plan("android/com.termux")); + runtime.cancel_task(&fdroid).unwrap(); + runtime.start_task(&termux).unwrap(); + + let active = task_list_json(&runtime, &json!({ "active": true }).to_string()).unwrap(); + assert_eq!(active["tasks"].as_array().unwrap().len(), 1); + assert_eq!(active["tasks"][0]["task_id"], termux); + + let package = task_list_json( + &runtime, + &json!({ "package_id": "android/org.fdroid.fdroid" }).to_string(), + ) + .unwrap(); + assert_eq!(package["tasks"].as_array().unwrap().len(), 1); + assert_eq!(package["tasks"][0]["task_id"], fdroid); + + let removed = task_clean_json(&mut runtime, "{}").unwrap(); + assert_eq!(removed["tasks"].as_array().unwrap().len(), 1); + assert_eq!(runtime.task(&fdroid).unwrap_err().code(), "task.not_found"); + assert_eq!( + runtime.task(&termux).unwrap().status, + RuntimeTaskStatus::Running + ); + } + + #[test] + fn json_clean_rejects_unknown_mode_without_mutating_tasks() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan_without_install("generic/example")); + runtime.start_task(&task_id).unwrap(); + runtime.complete_download(&task_id).unwrap(); + + let error = + task_clean_json(&mut runtime, &json!({ "mode": "all" }).to_string()).unwrap_err(); + + assert_eq!(error.code(), "runtime.invalid_request"); + assert_eq!( + runtime.task(&task_id).unwrap().status, + RuntimeTaskStatus::Completed + ); + } + + fn submit_plan(runtime: &mut GetterRuntime, plan: SealedActionPlan) -> String { + let action = runtime.issue_action(plan); + runtime.submit_action(&action.action_id).unwrap().task_id + } + + fn plan(package_id: &str) -> SealedActionPlan { + SealedActionPlan { + package_id: package_id.parse().unwrap(), + actions: vec![ + UpdateAction::Download { + url: "https://example.invalid/app.apk".to_owned(), + file_name: "app.apk".to_owned(), + }, + UpdateAction::Install { + installer: "android_package".to_owned(), + file: "app.apk".to_owned(), + }, + ], + lua_object: lua_object(package_id), + } + } + + fn plan_without_install(package_id: &str) -> SealedActionPlan { + SealedActionPlan { + package_id: package_id.parse().unwrap(), + actions: vec![UpdateAction::Download { + url: "https://example.invalid/archive.zip".to_owned(), + file_name: "archive.zip".to_owned(), + }], + lua_object: lua_object(package_id), + } + } + + fn lua_object(package_id: &str) -> PackageVersionLuaObject { + PackageVersionLuaObject { + object_id: format!("lua:{package_id}"), + dependency_digest: "sha256:test".to_owned(), + } + } +} From c4f2c281611d64ba4af41ceafd76142246ae847c Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 11:09:30 +0800 Subject: [PATCH 21/71] feat(operations): issue actions from update checks --- crates/getter-operations/src/runtime.rs | 118 +++++++++++++++++++++++- 1 file changed, 115 insertions(+), 3 deletions(-) diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 0d6e11d..5c44bff 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -8,9 +8,10 @@ use getter_core::{ runtime::{ - GetterRuntime, IssuedAction, RuntimeError, SealedActionPlan, TaskCleanMode, TaskSnapshot, - UserResult, + GetterRuntime, IssuedAction, PackageVersionLuaObject, RuntimeError, SealedActionPlan, + TaskCleanMode, TaskSnapshot, UserResult, }, + update::{run_offline_update_check, OfflineUpdateCheckError, OfflineUpdateCheckFixture}, PackageId, }; use serde::Deserialize; @@ -22,6 +23,8 @@ pub enum RuntimeOperationError { InvalidRequest(String), #[error("runtime error: {0}")] Runtime(#[from] RuntimeError), + #[error("update check failed: {0}")] + UpdateCheck(#[from] OfflineUpdateCheckError), #[error("runtime response serialization failed: {0}")] Serialize(String), } @@ -31,6 +34,7 @@ impl RuntimeOperationError { match self { Self::InvalidRequest(_) => "runtime.invalid_request", Self::Runtime(error) => error.code(), + Self::UpdateCheck(_) => "update.check_error", Self::Serialize(_) => "runtime.serialize_error", } } @@ -39,6 +43,7 @@ impl RuntimeOperationError { match self { Self::InvalidRequest(_) => "Getter runtime request is invalid", Self::Runtime(_) => "Getter runtime operation failed", + Self::UpdateCheck(_) => "Getter update check failed", Self::Serialize(_) => "Getter runtime response serialization failed", } } @@ -47,6 +52,7 @@ impl RuntimeOperationError { match self { Self::InvalidRequest(detail) | Self::Serialize(detail) => Some(detail.clone()), Self::Runtime(error) => Some(error.to_string()), + Self::UpdateCheck(error) => Some(error.to_string()), } } } @@ -55,6 +61,34 @@ pub fn issue_action(runtime: &mut GetterRuntime, plan: SealedActionPlan) -> Valu issued_action_json(runtime.issue_action(plan)) } +pub fn issue_action_from_offline_update_check_json( + runtime: &mut GetterRuntime, + request_json: &str, +) -> Result { + let request: OfflineUpdateActionRequest = parse_request(request_json)?; + let update = run_offline_update_check(request.fixture)?; + let action = if update.actions.is_empty() { + None + } else { + Some( + runtime.issue_action(SealedActionPlan { + package_id: update.package_id.clone(), + actions: update.actions.clone(), + lua_object: PackageVersionLuaObject { + object_id: format!("offline-update:{}", update.package_id), + dependency_digest: request + .dependency_digest + .unwrap_or_else(|| format!("offline-update:{}", update.package_id)), + }, + }), + ) + }; + Ok(json!({ + "update": update, + "action": action.map(issued_action_json), + })) +} + pub fn submit_action_json( runtime: &mut GetterRuntime, request_json: &str, @@ -194,6 +228,13 @@ fn tasks_json(tasks: Vec) -> Result Ok(json!({ "tasks": tasks })) } +#[derive(Debug, Deserialize)] +struct OfflineUpdateActionRequest { + fixture: OfflineUpdateCheckFixture, + #[serde(default)] + dependency_digest: Option, +} + #[derive(Debug, Deserialize)] struct SubmitActionRequest { action_id: String, @@ -250,7 +291,51 @@ impl CleanTasksRequest { #[cfg(test)] mod tests { use super::*; - use getter_core::{runtime::PackageVersionLuaObject, runtime::RuntimeTaskStatus, UpdateAction}; + use getter_core::{ + runtime::RuntimeTaskStatus, update::OFFLINE_UPDATE_CHECK_FORMAT, + update::OFFLINE_UPDATE_CHECK_VERSION, UpdateAction, UpdateArtifact, UpdateCandidate, + }; + + #[test] + fn offline_update_check_issues_getter_owned_action_id() { + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_offline_update_check_json( + &mut runtime, + &json!({ + "fixture": update_fixture("android/org.fdroid.fdroid", Some("1.0.0"), vec!["1.2.0"]), + "dependency_digest": "sha256:fixture" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["update"]["status"], "update_available"); + let action_id = issued["action"]["action_id"].as_str().unwrap(); + let submitted = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap(); + assert_eq!(submitted["package_id"], "android/org.fdroid.fdroid"); + assert_eq!(submitted["status"], "queued"); + } + + #[test] + fn offline_update_check_without_update_does_not_issue_action() { + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_offline_update_check_json( + &mut runtime, + &json!({ + "fixture": update_fixture("android/org.fdroid.fdroid", Some("1.2.0"), vec!["1.2.0"]), + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["update"]["status"], "up_to_date"); + assert!(issued["action"].is_null()); + assert_eq!(runtime.tasks().len(), 0); + } #[test] fn json_submit_and_user_result_round_trip_consumes_action() { @@ -336,6 +421,33 @@ mod tests { runtime.submit_action(&action.action_id).unwrap().task_id } + fn update_fixture( + package_id: &str, + installed_version: Option<&str>, + versions: Vec<&str>, + ) -> OfflineUpdateCheckFixture { + OfflineUpdateCheckFixture { + format: OFFLINE_UPDATE_CHECK_FORMAT.to_owned(), + version: OFFLINE_UPDATE_CHECK_VERSION, + package_id: package_id.parse().unwrap(), + installed_version: installed_version.map(str::to_owned), + ignored_version: None, + candidates: versions + .into_iter() + .map(|version| UpdateCandidate { + version: version.to_owned(), + channel: None, + source: None, + artifacts: vec![UpdateArtifact { + name: "app.apk".to_owned(), + url: "https://example.invalid/app.apk".to_owned(), + file_name: Some("app.apk".to_owned()), + }], + }) + .collect(), + } + } + fn plan(package_id: &str) -> SealedActionPlan { SealedActionPlan { package_id: package_id.parse().unwrap(), From 604a5f6a49e26ca895f26dceae655ffb404e5033 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 11:20:00 +0800 Subject: [PATCH 22/71] feat(core): accept static update candidates --- crates/getter-core/src/lib.rs | 7 +++ crates/getter-core/src/lua.rs | 92 ++++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 96cdb95..b1d759e 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -251,6 +251,13 @@ pub struct ResolvedPackage { pub permissions: PackagePermissions, #[serde(default)] pub source_priority: Vec, + /// Static/offline update candidates declared by package Lua. + /// + /// This is a first mock-provider bridge toward ADR-0011 action issuance: + /// getter still owns selection and action planning, while live provider + /// execution remains a later runtime slice. + #[serde(default)] + pub updates: Vec, } /// Installed target matched by a package, such as an Android package name. diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index a31107d..79bfc61 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -1,7 +1,10 @@ //! Minimal Lua package-file evaluation and Rust validation boundary. use crate::repository::RepositoryLayout; -use crate::{InstalledTarget, PackageId, PackagePermissions, ResolvedPackage}; +use crate::{ + InstalledTarget, PackageId, PackagePermissions, ResolvedPackage, UpdateArtifact, + UpdateCandidate, +}; use mlua::{Lua, Table, Value}; use serde_json::{Map, Number, Value as JsonValue}; use std::fs; @@ -331,6 +334,7 @@ fn validate_package_json( let permissions = parse_permissions(path, object.get("permissions"))?; let source_priority = parse_string_array(path, "source_priority", object.get("source_priority"))?; + let updates = parse_update_candidates(path, object.get("updates"))?; Ok(ResolvedPackage { id, @@ -339,6 +343,7 @@ fn validate_package_json( installed, permissions, source_priority, + updates, }) } @@ -394,6 +399,69 @@ fn parse_installed_targets( .collect() } +fn parse_update_candidates( + path: &Path, + value: Option<&JsonValue>, +) -> Result, LuaPackageError> { + let Some(value) = value else { + return Ok(Vec::new()); + }; + let array = value.as_array().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "field 'updates' must be an array".to_owned(), + })?; + array + .iter() + .map(|item| { + let object = item.as_object().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "updates entries must be objects".to_owned(), + })?; + let artifacts = parse_update_artifacts(path, object.get("artifacts"))?; + Ok(UpdateCandidate { + version: required_string(path, object, "version")?.to_owned(), + channel: optional_string(object, "channel"), + source: optional_string(object, "source"), + artifacts, + }) + }) + .collect() +} + +fn parse_update_artifacts( + path: &Path, + value: Option<&JsonValue>, +) -> Result, LuaPackageError> { + let Some(value) = value else { + return Ok(Vec::new()); + }; + let array = value.as_array().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "field 'artifacts' must be an array".to_owned(), + })?; + array + .iter() + .map(|item| { + let object = item.as_object().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: "artifact entries must be objects".to_owned(), + })?; + Ok(UpdateArtifact { + name: required_string(path, object, "name")?.to_owned(), + url: required_string(path, object, "url")?.to_owned(), + file_name: optional_string(object, "file_name"), + }) + }) + .collect() +} + +fn optional_string(object: &Map, field: &str) -> Option { + object + .get(field) + .and_then(JsonValue::as_str) + .map(str::to_owned) +} + fn parse_permissions( path: &Path, value: Option<&JsonValue>, @@ -480,6 +548,20 @@ return package_def { }, permissions = { free_network = true }, source_priority = { "github", "fdroid" }, + updates = { + { + version = "1.2.0", + channel = "stable", + source = "fixture", + artifacts = { + { + name = "app.apk", + url = "https://example.invalid/app.apk", + file_name = "fdroid.apk", + }, + }, + }, + }, } "#, ) @@ -497,6 +579,14 @@ return package_def { ); assert!(package.permissions.free_network); assert_eq!(package.source_priority, vec!["github", "fdroid"]); + assert_eq!(package.updates.len(), 1); + assert_eq!(package.updates[0].version, "1.2.0"); + assert_eq!(package.updates[0].channel.as_deref(), Some("stable")); + assert_eq!(package.updates[0].source.as_deref(), Some("fixture")); + assert_eq!( + package.updates[0].artifacts[0].file_name.as_deref(), + Some("fdroid.apk") + ); } #[test] From 1738c03f58450095bc075fb7adf75e4a57262854 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 11:47:24 +0800 Subject: [PATCH 23/71] feat(operations): issue actions from registered packages --- Cargo.lock | 1 + Cargo.toml | 2 +- crates/getter-core/Cargo.toml | 2 +- crates/getter-core/src/lua.rs | 11 +- crates/getter-operations/Cargo.toml | 7 + crates/getter-operations/src/runtime.rs | 258 ++++++++++++++++++++++++ 6 files changed, 277 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fce5fc5..e033e93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -568,6 +568,7 @@ dependencies = [ "getter-storage", "serde", "serde_json", + "tempfile", "thiserror 1.0.69", ] diff --git a/Cargo.toml b/Cargo.toml index 9347d01..ba37ca3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ edition = "2021" [features] default = ["cli", "domain"] domain = ["dep:getter-core", "dep:getter-storage", "dep:getter-operations"] -lua = ["getter-core/lua"] +lua = ["getter-core/lua", "getter-operations/lua"] cli = ["dep:getter-cli"] rustls-platform-verifier = ["dep:rustls-platform-verifier"] rustls-platform-verifier-android = ["rustls-platform-verifier", "rustls-platform-verifier/jni"] diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml index 93325e9..187fcf6 100644 --- a/crates/getter-core/Cargo.toml +++ b/crates/getter-core/Cargo.toml @@ -8,7 +8,7 @@ default = ["lua"] lua = ["dep:mlua"] [dependencies] -mlua = { version = "0.10", features = ["lua54", "vendored"], optional = true } +mlua = { version = "0.10", features = ["luajit", "vendored"], optional = true } serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "1" diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 79bfc61..d461539 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -106,7 +106,7 @@ fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Res } fn disable_native_module_searchers(package: &Table) -> mlua::Result<()> { - let searchers: Table = package.get("searchers")?; + let searchers = package_searchers(package)?; let len = searchers.raw_len(); for index in 4..=len { searchers.raw_set(index, Value::Nil)?; @@ -128,7 +128,7 @@ fn remove_unsafe_globals(lua: &Lua) -> mlua::Result<()> { } fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> mlua::Result<()> { - let searchers: Table = package.get("searchers")?; + let searchers = package_searchers(package)?; let searcher = lua.create_function(move |lua, module: String| { let Some(module) = module.strip_prefix("lib.") else { return lua @@ -171,6 +171,13 @@ fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> searchers.raw_set(2, searcher) } +fn package_searchers(package: &Table) -> mlua::Result { + match package.get::("searchers")? { + Value::Table(searchers) => Ok(searchers), + _ => package.get("loaders"), + } +} + fn module_to_relative_path(module: &str) -> Option { let mut path = PathBuf::new(); for part in module.split('.') { diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index a44e0b6..a36ba74 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -3,9 +3,16 @@ name = "getter-operations" version.workspace = true edition.workspace = true +[features] +default = [] +lua = ["getter-core/lua"] + [dependencies] getter-core = { path = "../getter-core", default-features = false } getter-storage = { path = "../getter-storage" } serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "1" + +[dev-dependencies] +tempfile = "3" diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 5c44bff..9887f15 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -6,6 +6,11 @@ //! persisted task state or duplicating task/control semantics in Flutter or //! Android glue. +#[cfg(feature = "lua")] +use getter_core::{ + lua::evaluate_package_file, + repository::{package_cache_key, RepositoryLayout, RepositoryLoadError}, +}; use getter_core::{ runtime::{ GetterRuntime, IssuedAction, PackageVersionLuaObject, RuntimeError, SealedActionPlan, @@ -14,8 +19,14 @@ use getter_core::{ update::{run_offline_update_check, OfflineUpdateCheckError, OfflineUpdateCheckFixture}, PackageId, }; +#[cfg(feature = "lua")] +use getter_core::{update::check_updates_offline, update::UpdateSelectionPolicy, RepositoryId}; +#[cfg(feature = "lua")] +use getter_storage::{MainDb, StorageError}; use serde::Deserialize; use serde_json::{json, Value}; +#[cfg(feature = "lua")] +use std::path::PathBuf; #[derive(Debug, thiserror::Error)] pub enum RuntimeOperationError { @@ -25,6 +36,15 @@ pub enum RuntimeOperationError { Runtime(#[from] RuntimeError), #[error("update check failed: {0}")] UpdateCheck(#[from] OfflineUpdateCheckError), + #[cfg(feature = "lua")] + #[error("storage operation failed: {0}")] + Storage(#[from] StorageError), + #[cfg(feature = "lua")] + #[error("repository operation failed: {0}")] + Repository(#[from] RepositoryLoadError), + #[cfg(feature = "lua")] + #[error("package evaluation failed: {0}")] + PackageEval(String), #[error("runtime response serialization failed: {0}")] Serialize(String), } @@ -35,6 +55,12 @@ impl RuntimeOperationError { Self::InvalidRequest(_) => "runtime.invalid_request", Self::Runtime(error) => error.code(), Self::UpdateCheck(_) => "update.check_error", + #[cfg(feature = "lua")] + Self::Storage(_) => "storage.error", + #[cfg(feature = "lua")] + Self::Repository(_) => "repository.error", + #[cfg(feature = "lua")] + Self::PackageEval(_) => "package.eval_error", Self::Serialize(_) => "runtime.serialize_error", } } @@ -44,6 +70,12 @@ impl RuntimeOperationError { Self::InvalidRequest(_) => "Getter runtime request is invalid", Self::Runtime(_) => "Getter runtime operation failed", Self::UpdateCheck(_) => "Getter update check failed", + #[cfg(feature = "lua")] + Self::Storage(_) => "Getter storage operation failed", + #[cfg(feature = "lua")] + Self::Repository(_) => "Getter repository operation failed", + #[cfg(feature = "lua")] + Self::PackageEval(_) => "Getter package evaluation failed", Self::Serialize(_) => "Getter runtime response serialization failed", } } @@ -53,6 +85,12 @@ impl RuntimeOperationError { Self::InvalidRequest(detail) | Self::Serialize(detail) => Some(detail.clone()), Self::Runtime(error) => Some(error.to_string()), Self::UpdateCheck(error) => Some(error.to_string()), + #[cfg(feature = "lua")] + Self::Storage(error) => Some(error.to_string()), + #[cfg(feature = "lua")] + Self::Repository(error) => Some(error.to_string()), + #[cfg(feature = "lua")] + Self::PackageEval(detail) => Some(detail.clone()), } } } @@ -89,6 +127,41 @@ pub fn issue_action_from_offline_update_check_json( })) } +#[cfg(feature = "lua")] +pub fn issue_action_from_registered_package_json( + runtime: &mut GetterRuntime, + db: &MainDb, + request_json: &str, +) -> Result { + let request: RegisteredPackageUpdateActionRequest = parse_request(request_json)?; + let (package, dependency_digest) = evaluate_registered_package(db, &request)?; + let update = check_updates_offline( + package.id.clone(), + request.installed_version, + package.updates.clone(), + UpdateSelectionPolicy { + ignored_version: request.ignored_version, + }, + )?; + let action = if update.actions.is_empty() { + None + } else { + Some(runtime.issue_action(SealedActionPlan { + package_id: update.package_id.clone(), + actions: update.actions.clone(), + lua_object: PackageVersionLuaObject { + object_id: format!("package-update:{}", update.package_id), + dependency_digest, + }, + })) + }; + Ok(json!({ + "package": package, + "update": update, + "action": action.map(issued_action_json), + })) +} + pub fn submit_action_json( runtime: &mut GetterRuntime, request_json: &str, @@ -235,6 +308,18 @@ struct OfflineUpdateActionRequest { dependency_digest: Option, } +#[cfg(feature = "lua")] +#[derive(Debug, Deserialize)] +struct RegisteredPackageUpdateActionRequest { + package_id: PackageId, + #[serde(default)] + repository_id: Option, + #[serde(default)] + installed_version: Option, + #[serde(default)] + ignored_version: Option, +} + #[derive(Debug, Deserialize)] struct SubmitActionRequest { action_id: String, @@ -275,6 +360,60 @@ struct CleanTasksRequest { mode: Option, } +#[cfg(feature = "lua")] +fn evaluate_registered_package( + db: &MainDb, + request: &RegisteredPackageUpdateActionRequest, +) -> Result<(getter_core::ResolvedPackage, String), RuntimeOperationError> { + let repositories = db.repositories()?; + let mut missing_path = None; + for repository in repositories { + if request + .repository_id + .as_ref() + .is_some_and(|requested| requested != &repository.id) + { + continue; + } + let Some(root) = repository.path.as_ref() else { + missing_path = Some(repository.id.to_string()); + continue; + }; + let root = PathBuf::from(root); + let layout = RepositoryLayout::load(&root)?; + let Some(package_file) = layout.package_file(&request.package_id) else { + continue; + }; + let package = evaluate_package_file(&layout, &package_file.path) + .map_err(|source| RuntimeOperationError::PackageEval(source.to_string()))?; + let cache_key = package_cache_key(&layout, package_file)?; + let dependency_digest = format!( + "repo:{}:package:{}:hash:{}", + cache_key.repository_id, cache_key.package_id, cache_key.package_file_hash + ); + return Ok((package, dependency_digest)); + } + let detail = if let Some(repository_id) = request.repository_id.as_ref() { + format!( + "package '{}' was not found in registered repository '{}'{}", + request.package_id, + repository_id, + missing_path + .map(|id| format!("; repository '{id}' has no path")) + .unwrap_or_default() + ) + } else { + format!( + "package '{}' was not found in any registered repository{}", + request.package_id, + missing_path + .map(|id| format!("; repository '{id}' has no path")) + .unwrap_or_default() + ) + }; + Err(RuntimeOperationError::PackageEval(detail)) +} + impl CleanTasksRequest { fn mode(self) -> Result { match self.mode.as_deref().unwrap_or("default") { @@ -291,10 +430,91 @@ impl CleanTasksRequest { #[cfg(test)] mod tests { use super::*; + #[cfg(feature = "lua")] + use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; + #[cfg(feature = "lua")] + use getter_core::RepositoryPriority; use getter_core::{ runtime::RuntimeTaskStatus, update::OFFLINE_UPDATE_CHECK_FORMAT, update::OFFLINE_UPDATE_CHECK_VERSION, UpdateAction, UpdateArtifact, UpdateCandidate, }; + #[cfg(feature = "lua")] + use std::fs; + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_issues_action_from_lua_static_updates() { + let temp = tempfile::tempdir().unwrap(); + let repo_root = temp.path().join("repo"); + write_static_update_repo(&repo_root); + let db = MainDb::open_in_memory().unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::new(0), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_root), + None, + ) + .unwrap(); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + &db, + &json!({ + "package_id": "android/org.fdroid.fdroid", + "installed_version": "1.0.0" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["package"]["repository"], "official"); + assert_eq!(issued["update"]["status"], "update_available"); + let action_id = issued["action"]["action_id"].as_str().unwrap(); + let submitted = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap(); + assert_eq!(submitted["package_id"], "android/org.fdroid.fdroid"); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_without_update_does_not_issue_action() { + let temp = tempfile::tempdir().unwrap(); + let repo_root = temp.path().join("repo"); + write_static_update_repo(&repo_root); + let db = MainDb::open_in_memory().unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::new(0), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_root), + None, + ) + .unwrap(); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + &db, + &json!({ + "package_id": "android/org.fdroid.fdroid", + "installed_version": "1.2.0" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["update"]["status"], "up_to_date"); + assert!(issued["action"].is_null()); + } #[test] fn offline_update_check_issues_getter_owned_action_id() { @@ -421,6 +641,44 @@ mod tests { runtime.submit_action(&action.action_id).unwrap().task_id } + #[cfg(feature = "lua")] + fn write_static_update_repo(root: &std::path::Path) { + fs::create_dir_all(root.join("packages/android")).unwrap(); + fs::create_dir(root.join("lib")).unwrap(); + fs::create_dir(root.join("templates")).unwrap(); + fs::write( + root.join("repo.toml"), + r#"id = "official" +name = "Official" +priority = 0 +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + fs::write( + root.join("packages/android/org.fdroid.fdroid.lua"), + r#" +return package_def { + id = "android/org.fdroid.fdroid", + name = "F-Droid", + updates = { + { + version = "1.2.0", + artifacts = { + { + name = "app.apk", + url = "https://example.invalid/app.apk", + file_name = "app.apk", + }, + }, + }, + }, +} +"#, + ) + .unwrap(); + } + fn update_fixture( package_id: &str, installed_version: Option<&str>, From 9ae7e5680188b2ef87bc052d2e55d5375116c0ce Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 12:43:02 +0800 Subject: [PATCH 24/71] feat(storage): rename ignored version to pin version --- crates/getter-cli/src/lib.rs | 155 ++++++++++++++++-- crates/getter-cli/tests/bdd_cli.rs | 61 +++++-- .../tests/features/cli/update_check.feature | 12 +- .../tests/features/cli/version_pin.feature | 12 ++ crates/getter-core/src/update.rs | 110 +++++-------- crates/getter-operations/src/legacy_room.rs | 27 ++- crates/getter-operations/src/runtime.rs | 8 +- crates/getter-storage/src/legacy_room.rs | 7 +- crates/getter-storage/src/lib.rs | 142 +++++++++++++--- 9 files changed, 398 insertions(+), 136 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/version_pin.feature diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 3562bf1..ed72425 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -64,6 +64,13 @@ pub enum CliCommand { repo_id: Option, }, StorageValidate, + VersionPin { + package_id: PackageId, + version: String, + }, + VersionUnpin { + package_id: PackageId, + }, UpdateCheck { fixture: PathBuf, }, @@ -380,6 +387,17 @@ where [domain, command] if domain == "storage" && command == "validate" => { CliCommand::StorageValidate } + [domain, command, package_id, version] if domain == "version" && command == "pin" => { + CliCommand::VersionPin { + package_id: parse_package_id(package_id)?, + version: version.clone(), + } + } + [domain, command, package_id] if domain == "version" && command == "unpin" => { + CliCommand::VersionUnpin { + package_id: parse_package_id(package_id)?, + } + } [domain, command, flag, fixture] if domain == "update" && command == "check" && flag == "--fixture" => { @@ -567,6 +585,19 @@ fn execute(invocation: CliInvocation) -> Result { "cache_db": cache_db_path(&invocation.data_dir), })) } + CliCommand::VersionPin { + package_id, + version, + } => { + let db = open_main_db(&invocation.data_dir)?; + let package = db.set_tracked_package_pin_version(&package_id, Some(&version))?; + Ok(json!({ "package": tracked_package_json(package) })) + } + CliCommand::VersionUnpin { package_id } => { + let db = open_main_db(&invocation.data_dir)?; + let package = db.set_tracked_package_pin_version(&package_id, None)?; + Ok(json!({ "package": tracked_package_json(package) })) + } CliCommand::UpdateCheck { fixture } => { open_initialized_storage(&invocation.data_dir)?; let fixture = read_update_check_fixture(&fixture)?; @@ -962,19 +993,18 @@ fn package_json(package: getter_core::ResolvedPackage) -> Result) -> Vec { - packages - .into_iter() - .map(|package| { - json!({ - "id": package.package_id.to_string(), - "enabled": package.enabled, - "favorite": package.favorite, - "ignored_version": package.ignored_version, - "repository_id": package.repository_id.map(|id| id.to_string()), - "package_resolution": package.package_resolution.as_str(), - }) - }) - .collect() + packages.into_iter().map(tracked_package_json).collect() +} + +fn tracked_package_json(package: StoredTrackedPackage) -> Value { + json!({ + "id": package.package_id.to_string(), + "enabled": package.enabled, + "favorite": package.favorite, + "pin_version": package.pin_version, + "repository_id": package.repository_id.map(|id| id.to_string()), + "package_resolution": package.package_resolution.as_str(), + }) } fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<(), CliError> { @@ -988,7 +1018,7 @@ fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<( common_conversion_available: app.common_conversion_available, }, Some(&LegacyExtraAppRecord { - ignored_version: app.ignored_version.clone(), + ignored_version: app.pin_version.clone(), favorite: app.favorite, }), ) @@ -1000,6 +1030,7 @@ fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<( "ok": true, "source": "legacy-room-bundle", "imported_records": bundle.apps.len(), + "notices": [migration_pin_version_notice()], }) .to_string(); db.import_tracked_packages_with_migration_record( @@ -1013,6 +1044,13 @@ fn import_legacy_room_bundle(db: &MainDb, bundle: &LegacyRoomBundle) -> Result<( Ok(()) } +fn migration_pin_version_notice() -> Value { + json!({ + "code": "migration.renamed_ignored_version_to_pin_version", + "message": "Legacy ignored version state was preserved as pin_version", + }) +} + fn tracked_package_upsert( mapping: getter_storage::legacy_room::LegacyAppMapping, ) -> TrackedPackageUpsert { @@ -1020,7 +1058,7 @@ fn tracked_package_upsert( package_id: mapping.package_id, enabled: true, favorite: mapping.user_state.favorite, - ignored_version: mapping.user_state.ignored_version, + pin_version: mapping.user_state.pin_version, repository_id: None, package_resolution: stored_resolution(mapping.package_resolution), } @@ -1103,6 +1141,7 @@ fn create_migration_report_with_source_counts( imported_records, tracked_records, warnings, + notices: migration_report_notices(code), source_counts, }; let bytes = serde_json::to_vec_pretty(&report) @@ -1116,6 +1155,14 @@ fn report_file_name(code: &str) -> String { format!("{}.json", code.replace('.', "-")) } +fn migration_report_notices(code: &str) -> Vec { + if code == "migration.imported" { + vec![migration_pin_version_notice()] + } else { + Vec::new() + } +} + #[derive(Debug, Serialize)] struct MigrationReport<'a> { ok: bool, @@ -1126,6 +1173,8 @@ struct MigrationReport<'a> { imported_records: u64, tracked_records: u64, warnings: &'a [Value], + #[serde(skip_serializing_if = "Vec::is_empty")] + notices: Vec, #[serde(skip_serializing_if = "Option::is_none")] source_counts: Option<&'a Value>, } @@ -1165,7 +1214,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|hub list|update check --fixture |task submit --request |task run |task list|task cancel |task events --after --limit |task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |task submit --request |task run |task list|task cancel |task events --after --limit |task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1184,8 +1233,8 @@ struct LegacyBundleApp { official_package_available: bool, #[serde(default)] common_conversion_available: bool, - #[serde(default)] - ignored_version: Option, + #[serde(default, alias = "ignored_version")] + pin_version: Option, #[serde(default)] favorite: bool, } @@ -1218,6 +1267,8 @@ impl CliCommand { Self::RepoValidate { .. } => "repo validate", Self::PackageEval { .. } => "package eval", Self::StorageValidate => "storage validate", + Self::VersionPin { .. } => "version pin", + Self::VersionUnpin { .. } => "version unpin", Self::UpdateCheck { .. } => "update check", Self::TaskSubmit { .. } => "task submit", Self::TaskRun { .. } => "task run", @@ -1249,6 +1300,43 @@ mod tests { assert_eq!(invocation.command, CliCommand::AppList); } + #[test] + fn parses_version_pin_and_unpin_commands() { + let pin = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "version", + "pin", + "android/org.fdroid.fdroid", + "1.2.3", + ]) + .unwrap(); + assert_eq!( + pin.command, + CliCommand::VersionPin { + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + version: "1.2.3".to_owned(), + } + ); + + let unpin = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "version", + "unpin", + "android/org.fdroid.fdroid", + ]) + .unwrap(); + assert_eq!( + unpin.command, + CliCommand::VersionUnpin { + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + } + ); + } + #[test] fn run_init_creates_sqlite_database_files_and_json_envelope() { let temp = tempfile::tempdir().unwrap(); @@ -1269,6 +1357,37 @@ mod tests { assert_eq!(json["command"], "init"); } + #[test] + fn run_version_pin_and_unpin_mutates_tracked_package_pin_version() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path().join("getter-data"); + + let pin = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "version".to_owned(), + "pin".to_owned(), + "android/org.fdroid.fdroid".to_owned(), + "1.2.3".to_owned(), + ]); + assert_eq!(pin.exit_code, ExitCode::Success); + let pin_json: Value = serde_json::from_str(&pin.stdout).unwrap(); + assert_eq!(pin_json["data"]["package"]["pin_version"], "1.2.3"); + + let unpin = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "version".to_owned(), + "unpin".to_owned(), + "android/org.fdroid.fdroid".to_owned(), + ]); + assert_eq!(unpin.exit_code, ExitCode::Success); + let unpin_json: Value = serde_json::from_str(&unpin.stdout).unwrap(); + assert!(unpin_json["data"]["package"]["pin_version"].is_null()); + } + #[test] fn malformed_bundle_report_does_not_create_imported_state() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index d0230ea..733827f 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -205,20 +205,20 @@ fn offline_update_fixture_with_installed_version( } #[given( - expr = "an offline update fixture for package {string} installed version {string} ignored version {string} with candidate versions {string}" + expr = "an offline update fixture for package {string} installed version {string} pin version {string} with candidate versions {string}" )] -fn offline_update_fixture_with_ignored_version( +fn offline_update_fixture_with_pin_version( world: &mut CliWorld, package_id: String, installed_version: String, - ignored_version: String, + pin_version: String, versions: String, ) { write_offline_update_fixture( world, package_id, Some(installed_version), - Some(ignored_version), + Some(pin_version), versions, ); } @@ -542,6 +542,26 @@ fn run_getter_update_check(world: &mut CliWorld) { world.json = None; } +#[when(expr = "I run getter version pin for package {string} version {string}")] +fn run_getter_version_pin(world: &mut CliWorld, package_id: String, version: String) { + let output = run_getter( + world, + ["version".to_owned(), "pin".to_owned(), package_id, version], + ); + world.output = Some(output); + world.json = None; +} + +#[when(expr = "I run getter version unpin for package {string}")] +fn run_getter_version_unpin(world: &mut CliWorld, package_id: String) { + let output = run_getter( + world, + ["version".to_owned(), "unpin".to_owned(), package_id], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter task submit for that request")] fn run_getter_task_submit(world: &mut CliWorld) { let request = world.task_request.as_ref().expect("task request exists"); @@ -1074,6 +1094,18 @@ fn output_contains_named_package(world: &mut CliWorld, package_id: String, packa assert_eq!(json["data"]["package"]["name"], package_name); } +#[then(expr = "the pinned package version is {string}")] +fn pinned_package_version_is(world: &mut CliWorld, version: String) { + let json = current_json(world); + assert_eq!(json["data"]["package"]["pin_version"], version); +} + +#[then("the package is unpinned")] +fn package_is_unpinned(world: &mut CliWorld) { + let json = current_json(world); + assert!(json["data"]["package"]["pin_version"].is_null()); +} + #[then(expr = "the update check status is {string}")] fn update_check_status_is(world: &mut CliWorld, status: String) { let json = current_json(world); @@ -1343,6 +1375,7 @@ fn direct_migration_report_stays_sanitized(world: &mut CliWorld) { assert_eq!(report_json["source_counts"]["hub_rows"], 1); assert_eq!(report_json["source_counts"]["extra_hub_rows"], 1); assert_report_has_drop_warnings(&report_json); + assert_report_has_pin_version_notice(&report_json); } #[then("the output reports the legacy Room migration was already completed")] @@ -1398,6 +1431,7 @@ fn direct_migration_report_list_stays_sanitized(world: &mut CliWorld) { assert_eq!(imported["source_counts"]["hub_rows"], 1); assert_eq!(imported["source_counts"]["extra_hub_rows"], 1); assert_report_has_drop_warnings(imported); + assert_report_has_pin_version_notice(imported); } #[then(expr = "the app list contains imported package {string}")] @@ -1411,7 +1445,7 @@ fn app_list_contains_imported_package(world: &mut CliWorld, package_id: String) .find(|app| app["id"].as_str() == Some(package_id.as_str())) .unwrap_or_else(|| panic!("app list should contain {package_id}: {apps:?}")); assert_eq!(app["favorite"], true); - assert_eq!(app["ignored_version"], "1.20.0"); + assert_eq!(app["pin_version"], "1.20.0"); assert_eq!(app["package_resolution"], "official_repository_package"); world.output = Some(output); world.json = Some(json); @@ -1428,7 +1462,7 @@ fn app_list_contains_directly_imported_package(world: &mut CliWorld, package_id: .find(|app| app["id"].as_str() == Some(package_id.as_str())) .unwrap_or_else(|| panic!("app list should contain {package_id}: {apps:?}")); assert_eq!(app["favorite"], true); - assert_eq!(app["ignored_version"], "1.20.0"); + assert_eq!(app["pin_version"], "1.20.0"); assert_eq!(app["package_resolution"], "missing_package_definition"); world.output = Some(output); world.json = Some(json); @@ -1504,7 +1538,7 @@ fn write_offline_update_fixture( world: &mut CliWorld, package_id: String, installed_version: Option, - ignored_version: Option, + pin_version: Option, versions: String, ) { let candidates: Vec = versions @@ -1530,7 +1564,7 @@ fn write_offline_update_fixture( world, package_id, installed_version, - ignored_version, + pin_version, candidates, ); } @@ -1539,7 +1573,7 @@ fn write_offline_update_fixture_with_candidates( world: &mut CliWorld, package_id: String, installed_version: Option, - ignored_version: Option, + pin_version: Option, candidates: Vec, ) { let temp = world.temp.as_ref().expect("tempdir exists"); @@ -1551,7 +1585,7 @@ fn write_offline_update_fixture_with_candidates( "version": 1, "package_id": package_id, "installed_version": installed_version, - "ignored_version": ignored_version, + "pin_version": pin_version, "candidates": candidates, })) .expect("fixture serializes"), @@ -1635,6 +1669,13 @@ fn assert_report_has_drop_warnings(report: &Value) { .any(|warning| warning["code"].as_str() == Some("legacy.dropped_extra_hub_rows"))); } +fn assert_report_has_pin_version_notice(report: &Value) { + let notices = report["notices"].as_array().expect("notices array"); + assert!(notices.iter().any(|notice| { + notice["code"].as_str() == Some("migration.renamed_ignored_version_to_pin_version") + })); +} + fn assert_sanitized_direct_room_report_text(text: &str) { for forbidden in [ "UA_DIRECT_DB_SENTINEL_SECRET", diff --git a/crates/getter-cli/tests/features/cli/update_check.feature b/crates/getter-cli/tests/features/cli/update_check.feature index e5d4828..9ad2f95 100644 --- a/crates/getter-cli/tests/features/cli/update_check.feature +++ b/crates/getter-cli/tests/features/cli/update_check.feature @@ -19,22 +19,22 @@ Feature: Offline update check And the update check status is "up_to_date" And the update check has no selected update - Scenario: User checks an offline fixture where the latest update is ignored + Scenario: User checks an offline fixture where pin_version overrides the local baseline Given an initialized getter data directory - And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" ignored version "1.2.0" with candidate versions "1.1.0,1.2.0" + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" pin version "1.2.0" with candidate versions "1.1.0,1.2.0,1.3.0" When I run getter update check for that fixture Then the command succeeds And the output is valid JSON And the update check status is "update_available" - And the selected update version is "1.1.0" + And the selected update version is "1.3.0" - Scenario: User checks an offline fixture where the only update is ignored + Scenario: User checks an offline fixture where pin_version makes the package up to date Given an initialized getter data directory - And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" ignored version "1.2.0" with candidate versions "1.2.0" + And an offline update fixture for package "android/org.fdroid.fdroid" installed version "1.0.0" pin version "1.2.0" with candidate versions "1.2.0" When I run getter update check for that fixture Then the command succeeds And the output is valid JSON - And the update check status is "ignored" + And the update check status is "up_to_date" And the update check has no selected update Scenario: User checks an offline fixture without an installed version diff --git a/crates/getter-cli/tests/features/cli/version_pin.feature b/crates/getter-cli/tests/features/cli/version_pin.feature new file mode 100644 index 0000000..fb18337 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/version_pin.feature @@ -0,0 +1,12 @@ +@getter-cli @version +Feature: Version pin state + Scenario: User pins and unpins a package version baseline + Given an initialized getter data directory + When I run getter version pin for package "android/org.fdroid.fdroid" version "1.2.3" + Then the command succeeds + And the output is valid JSON + And the pinned package version is "1.2.3" + When I run getter version unpin for package "android/org.fdroid.fdroid" + Then the command succeeds + And the output is valid JSON + And the package is unpinned diff --git a/crates/getter-core/src/update.rs b/crates/getter-core/src/update.rs index 72acfa6..09952ef 100644 --- a/crates/getter-core/src/update.rs +++ b/crates/getter-core/src/update.rs @@ -12,9 +12,9 @@ pub const OFFLINE_UPDATE_CHECK_VERSION: u32 = 1; /// User state that affects update selection. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct UpdateSelectionPolicy { - /// Candidate version the user chose to ignore/mark as skipped. - #[serde(default)] - pub ignored_version: Option, + /// User-selected local version override used as the comparison baseline. + #[serde(default, alias = "ignored_version")] + pub pin_version: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -24,8 +24,8 @@ pub struct OfflineUpdateCheckFixture { pub package_id: PackageId, #[serde(default)] pub installed_version: Option, - #[serde(default)] - pub ignored_version: Option, + #[serde(default, alias = "ignored_version")] + pub pin_version: Option, #[serde(default)] pub candidates: Vec, } @@ -36,6 +36,8 @@ pub struct OfflineUpdateCheckResult { pub package_id: PackageId, #[serde(default)] pub installed_version: Option, + #[serde(default)] + pub effective_local_version: Option, pub policy: UpdateSelectionPolicy, pub status: UpdateCheckStatus, #[serde(default)] @@ -49,7 +51,6 @@ pub enum UpdateCheckStatus { UpdateAvailable, UpToDate, NoCandidates, - Ignored, } #[derive(Debug, thiserror::Error)] @@ -76,7 +77,7 @@ pub fn run_offline_update_check( } let policy = UpdateSelectionPolicy { - ignored_version: fixture.ignored_version, + pin_version: fixture.pin_version, }; check_updates_offline( fixture.package_id, @@ -92,18 +93,17 @@ pub fn check_updates_offline( candidates: Vec, policy: UpdateSelectionPolicy, ) -> Result { + let effective_local_version = policy + .pin_version + .clone() + .or_else(|| installed_version.clone()); let selected = select_update( package_id.clone(), - installed_version.as_deref(), - &candidates, - &policy, - ); - let status = update_check_status( - selected.as_ref(), - installed_version.as_deref(), + effective_local_version.as_deref(), &candidates, &policy, ); + let status = update_check_status(selected.as_ref(), &candidates); let actions = match selected.as_ref() { Some(selected) => { if selected.artifact.is_none() { @@ -120,6 +120,7 @@ pub fn check_updates_offline( network_required: false, package_id, installed_version, + effective_local_version, policy, status, selected, @@ -163,38 +164,17 @@ fn installer_for_package_kind(kind: PackageKind) -> &'static str { fn update_check_status( selected: Option<&SelectedUpdate>, - installed_version: Option<&str>, candidates: &[UpdateCandidate], - policy: &UpdateSelectionPolicy, ) -> UpdateCheckStatus { if candidates.is_empty() { UpdateCheckStatus::NoCandidates } else if selected.is_some() { UpdateCheckStatus::UpdateAvailable - } else if ignored_candidate_would_have_been_update(installed_version, candidates, policy) { - UpdateCheckStatus::Ignored } else { UpdateCheckStatus::UpToDate } } -fn ignored_candidate_would_have_been_update( - installed_version: Option<&str>, - candidates: &[UpdateCandidate], - policy: &UpdateSelectionPolicy, -) -> bool { - let Some(ignored) = policy.ignored_version.as_deref() else { - return false; - }; - - candidates.iter().any(|candidate| { - compare_versions(&candidate.version, ignored) == Ordering::Equal - && installed_version.is_none_or(|installed| { - compare_versions(&candidate.version, installed) == Ordering::Greater - }) - }) -} - /// Compare human-facing version strings using a deterministic token ordering. /// /// This intentionally starts small: digit runs compare numerically, ASCII text @@ -236,8 +216,10 @@ pub fn compare_versions(left: &str, right: &str) -> Ordering { /// Select the best update candidate for a package. /// /// The selected candidate is the highest version that is newer than the -/// installed version and not equal to the user's ignored version. If no -/// installed version is known, the highest non-ignored candidate is selected. +/// effective local baseline. The effective baseline is normally the observed +/// installed version; callers pass `pin_version` instead when the user has set +/// a baseline override. If no baseline is known, the highest candidate is +/// selected. pub fn select_update( package_id: PackageId, installed_version: Option<&str>, @@ -261,13 +243,7 @@ fn is_selectable( installed_version: Option<&str>, policy: &UpdateSelectionPolicy, ) -> bool { - if policy - .ignored_version - .as_deref() - .is_some_and(|ignored| compare_versions(&candidate.version, ignored) == Ordering::Equal) - { - return false; - } + let _ = policy; match installed_version { Some(installed) => compare_versions(&candidate.version, installed) == Ordering::Greater, @@ -400,18 +376,21 @@ mod tests { } #[test] - fn respects_ignored_version() { - let selected = select_update( + fn pin_version_overrides_comparison_baseline() { + let result = check_updates_offline( "android/org.fdroid.fdroid".parse().unwrap(), - Some("1.0.0"), - &[candidate("1.1.0"), candidate("1.2.0")], - &UpdateSelectionPolicy { - ignored_version: Some("1.2.0".to_owned()), + Some("1.0.0".to_owned()), + vec![candidate("1.1.0"), candidate("1.2.0")], + UpdateSelectionPolicy { + pin_version: Some("1.2.0".to_owned()), }, ) .unwrap(); - assert_eq!(selected.candidate.version, "1.1.0"); + assert_eq!(result.status, UpdateCheckStatus::UpToDate); + assert_eq!(result.installed_version.as_deref(), Some("1.0.0")); + assert_eq!(result.effective_local_version.as_deref(), Some("1.2.0")); + assert!(result.selected.is_none()); } #[test] @@ -449,7 +428,7 @@ mod tests { version: OFFLINE_UPDATE_CHECK_VERSION, package_id: "android/org.fdroid.fdroid".parse().unwrap(), installed_version: Some("1.0.0".to_owned()), - ignored_version: None, + pin_version: None, candidates: vec![candidate("1.0.1"), candidate("1.2.0")], }) .unwrap(); @@ -487,35 +466,20 @@ mod tests { } #[test] - fn offline_update_check_reports_ignored_when_only_update_is_ignored() { + fn offline_update_check_uses_pin_version_as_baseline() { let result = check_updates_offline( "android/org.fdroid.fdroid".parse().unwrap(), Some("1.0.0".to_owned()), - vec![candidate("1.2.0")], - UpdateSelectionPolicy { - ignored_version: Some("1.2.0".to_owned()), - }, - ) - .unwrap(); - - assert_eq!(result.status, UpdateCheckStatus::Ignored); - assert!(result.selected.is_none()); - } - - #[test] - fn offline_update_check_falls_back_below_ignored_latest() { - let result = check_updates_offline( - "android/org.fdroid.fdroid".parse().unwrap(), - Some("1.0.0".to_owned()), - vec![candidate("1.1.0"), candidate("1.2.0")], + vec![candidate("1.1.0"), candidate("1.2.0"), candidate("1.3.0")], UpdateSelectionPolicy { - ignored_version: Some("1.2.0".to_owned()), + pin_version: Some("1.2.0".to_owned()), }, ) .unwrap(); assert_eq!(result.status, UpdateCheckStatus::UpdateAvailable); - assert_eq!(result.selected.as_ref().unwrap().candidate.version, "1.1.0"); + assert_eq!(result.selected.as_ref().unwrap().candidate.version, "1.3.0"); + assert_eq!(result.effective_local_version.as_deref(), Some("1.2.0")); } #[test] @@ -561,7 +525,7 @@ mod tests { version: OFFLINE_UPDATE_CHECK_VERSION, package_id: "android/org.fdroid.fdroid".parse().unwrap(), installed_version: None, - ignored_version: None, + pin_version: None, candidates: Vec::new(), }) .unwrap_err(); diff --git a/crates/getter-operations/src/legacy_room.rs b/crates/getter-operations/src/legacy_room.rs index 40c7c34..352205e 100644 --- a/crates/getter-operations/src/legacy_room.rs +++ b/crates/getter-operations/src/legacy_room.rs @@ -174,6 +174,7 @@ fn import_legacy_room_db( "imported_records": import.apps.len(), "source_counts": source_counts_json(import), "warnings": import_warnings_json(&import.warnings), + "notices": [migration_pin_version_notice()], }) .to_string(); db.import_tracked_packages_with_migration_record( @@ -194,7 +195,7 @@ fn tracked_package_upsert( package_id: mapping.package_id, enabled: true, favorite: mapping.user_state.favorite, - ignored_version: mapping.user_state.ignored_version, + pin_version: mapping.user_state.pin_version, repository_id: None, package_resolution: stored_resolution(mapping.package_resolution), } @@ -249,7 +250,7 @@ fn tracked_packages_json(packages: Vec) -> Vec { "id": package.package_id.to_string(), "enabled": package.enabled, "favorite": package.favorite, - "ignored_version": package.ignored_version, + "pin_version": package.pin_version, "repository_id": package.repository_id.map(|id| id.to_string()), "package_resolution": package.package_resolution.as_str(), }) @@ -266,6 +267,13 @@ fn source_counts_json(import: &LegacyRoomDbImport) -> Value { }) } +fn migration_pin_version_notice() -> Value { + json!({ + "code": "migration.renamed_ignored_version_to_pin_version", + "message": "Legacy ignored version state was preserved as pin_version", + }) +} + fn import_warnings_json(warnings: &[LegacyRoomImportWarning]) -> Vec { warnings .iter() @@ -310,6 +318,7 @@ fn create_migration_report_with_source_counts( imported_records, tracked_records, warnings, + notices: migration_report_notices(code), source_counts, }; let bytes = serde_json::to_vec_pretty(&report).map_err(|source| { @@ -325,6 +334,14 @@ fn report_file_name(code: &str) -> String { format!("{}.json", code.replace('.', "-")) } +fn migration_report_notices(code: &str) -> Vec { + if code == "migration.imported" { + vec![migration_pin_version_notice()] + } else { + Vec::new() + } +} + fn list_migration_reports(data_dir: &Path) -> LegacyRoomOperationResult> { let reports_dir = data_dir.join(MIGRATION_REPORTS_DIR); if !reports_dir.exists() { @@ -377,6 +394,10 @@ fn list_migration_reports(data_dir: &Path) -> LegacyRoomOperationResult { imported_records: u64, tracked_records: u64, warnings: &'a [Value], + #[serde(skip_serializing_if = "Vec::is_empty")] + notices: Vec, #[serde(skip_serializing_if = "Option::is_none")] source_counts: Option<&'a Value>, } diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 9887f15..f6bfa38 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -140,7 +140,7 @@ pub fn issue_action_from_registered_package_json( request.installed_version, package.updates.clone(), UpdateSelectionPolicy { - ignored_version: request.ignored_version, + pin_version: request.pin_version, }, )?; let action = if update.actions.is_empty() { @@ -316,8 +316,8 @@ struct RegisteredPackageUpdateActionRequest { repository_id: Option, #[serde(default)] installed_version: Option, - #[serde(default)] - ignored_version: Option, + #[serde(default, alias = "ignored_version")] + pin_version: Option, } #[derive(Debug, Deserialize)] @@ -689,7 +689,7 @@ return package_def { version: OFFLINE_UPDATE_CHECK_VERSION, package_id: package_id.parse().unwrap(), installed_version: installed_version.map(str::to_owned), - ignored_version: None, + pin_version: None, candidates: versions .into_iter() .map(|version| UpdateCandidate { diff --git a/crates/getter-storage/src/legacy_room.rs b/crates/getter-storage/src/legacy_room.rs index 711ea1f..cf225f7 100644 --- a/crates/getter-storage/src/legacy_room.rs +++ b/crates/getter-storage/src/legacy_room.rs @@ -51,7 +51,7 @@ pub enum LegacyPackageResolution { #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct LegacyUserStateMapping { - pub ignored_version: Option, + pub pin_version: Option, pub favorite: bool, } @@ -157,10 +157,9 @@ pub fn map_legacy_app( _ => Vec::new(), }; let user_state = LegacyUserStateMapping { - ignored_version: extra.and_then(|extra| extra.ignored_version.clone()), + pin_version: extra.and_then(|extra| extra.ignored_version.clone()), favorite: extra.is_some_and(|extra| extra.favorite), }; - Ok(LegacyAppMapping { package_id, package_resolution, @@ -506,7 +505,7 @@ mod tests { ) .unwrap(); - assert_eq!(mapping.user_state.ignored_version.as_deref(), Some("1.2.3")); + assert_eq!(mapping.user_state.pin_version.as_deref(), Some("1.2.3")); assert!(mapping.user_state.favorite); } diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index 23b0e8a..2127cbc 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -35,6 +35,8 @@ pub enum StorageError { InvalidTaskTransition { task_id: String, reason: String }, #[error("invalid task request: {0}")] InvalidTaskRequest(String), + #[error("storage invariant failed: {0}")] + Invariant(String), #[error("JSON error: {0}")] Json(#[from] serde_json::Error), } @@ -85,7 +87,7 @@ CREATE TABLE IF NOT EXISTS tracked_packages ( package_id TEXT PRIMARY KEY, enabled INTEGER NOT NULL DEFAULT 1, favorite INTEGER NOT NULL DEFAULT 0, - ignored_version TEXT, + pin_version TEXT, repository_id TEXT, package_resolution TEXT NOT NULL DEFAULT 'missing_package_definition', FOREIGN KEY(repository_id) REFERENCES repositories(id) @@ -136,7 +138,8 @@ CREATE TABLE IF NOT EXISTS install_handoffs ( ); "#, )?; - self.ensure_column("tracked_packages", "ignored_version", "TEXT")?; + self.ensure_column("tracked_packages", "pin_version", "TEXT")?; + self.migrate_ignored_version_to_pin_version()?; self.ensure_column( "tracked_packages", "package_resolution", @@ -168,6 +171,22 @@ CREATE TABLE IF NOT EXISTS install_handoffs ( Ok(()) } + fn migrate_ignored_version_to_pin_version(&self) -> Result<(), StorageError> { + let columns = self.table_columns("tracked_packages")?; + if columns.iter().any(|existing| existing == "ignored_version") { + self.conn.execute( + r#" +UPDATE tracked_packages +SET pin_version = ignored_version +WHERE pin_version IS NULL + AND ignored_version IS NOT NULL +"#, + [], + )?; + } + Ok(()) + } + fn table_columns(&self, table: &'static str) -> Result, StorageError> { let mut stmt = self.conn.prepare(&format!("PRAGMA table_info({table})"))?; let rows = stmt.query_map([], |row| row.get::<_, String>(1))?; @@ -244,6 +263,46 @@ ON CONFLICT(id) DO UPDATE SET Ok(()) } + pub fn set_tracked_package_pin_version( + &self, + package_id: &PackageId, + pin_version: Option<&str>, + ) -> Result { + self.conn.execute( + r#" +INSERT INTO tracked_packages( + package_id, + enabled, + favorite, + pin_version, + repository_id, + package_resolution +) +VALUES (?1, 1, 0, ?2, NULL, ?3) +ON CONFLICT(package_id) DO UPDATE SET + pin_version = excluded.pin_version +"#, + params![ + package_id.to_string(), + pin_version, + StoredPackageResolution::MissingPackageDefinition.as_str(), + ], + )?; + self.tracked_package(package_id)?.ok_or_else(|| { + StorageError::Invariant("tracked package upsert did not create a row".to_owned()) + }) + } + + pub fn tracked_package( + &self, + package_id: &PackageId, + ) -> Result, StorageError> { + Ok(self + .tracked_packages()? + .into_iter() + .find(|package| &package.package_id == package_id)) + } + pub fn upsert_generated_tracked_package_preserving_user_state( &self, package_id: &PackageId, @@ -255,7 +314,7 @@ INSERT INTO tracked_packages( package_id, enabled, favorite, - ignored_version, + pin_version, repository_id, package_resolution ) @@ -329,7 +388,7 @@ WHERE package_id = ?1 pub fn tracked_packages(&self) -> Result, StorageError> { let mut stmt = self.conn.prepare( r#" -SELECT package_id, enabled, favorite, ignored_version, repository_id, package_resolution +SELECT package_id, enabled, favorite, pin_version, repository_id, package_resolution FROM tracked_packages ORDER BY package_id ASC "#, @@ -346,12 +405,12 @@ ORDER BY package_id ASC })?; let mut packages = Vec::new(); for row in rows { - let (package_id, enabled, favorite, ignored_version, repository_id, resolution) = row?; + let (package_id, enabled, favorite, pin_version, repository_id, resolution) = row?; packages.push(StoredTrackedPackage { package_id: PackageId::from_str(&package_id)?, enabled: enabled != 0, favorite: favorite != 0, - ignored_version, + pin_version, repository_id: repository_id.map(RepositoryId::new).transpose()?, package_resolution: StoredPackageResolution::from_str(&resolution)?, }); @@ -842,7 +901,7 @@ pub struct TrackedPackageUpsert { pub package_id: PackageId, pub enabled: bool, pub favorite: bool, - pub ignored_version: Option, + pub pin_version: Option, pub repository_id: Option, pub package_resolution: StoredPackageResolution, } @@ -852,7 +911,7 @@ pub struct StoredTrackedPackage { pub package_id: PackageId, pub enabled: bool, pub favorite: bool, - pub ignored_version: Option, + pub pin_version: Option, pub repository_id: Option, pub package_resolution: StoredPackageResolution, } @@ -957,7 +1016,7 @@ INSERT INTO tracked_packages( package_id, enabled, favorite, - ignored_version, + pin_version, repository_id, package_resolution ) @@ -965,7 +1024,7 @@ VALUES (?1, ?2, ?3, ?4, ?5, ?6) ON CONFLICT(package_id) DO UPDATE SET enabled = excluded.enabled, favorite = excluded.favorite, - ignored_version = excluded.ignored_version, + pin_version = excluded.pin_version, repository_id = excluded.repository_id, package_resolution = excluded.package_resolution "#, @@ -973,7 +1032,7 @@ ON CONFLICT(package_id) DO UPDATE SET package.package_id.to_string(), bool_to_i64(package.enabled), bool_to_i64(package.favorite), - package.ignored_version.as_deref(), + package.pin_version.as_deref(), package.repository_id.as_ref().map(RepositoryId::as_str), package.package_resolution.as_str(), ], @@ -1105,7 +1164,7 @@ mod tests { package_id: "android/org.fdroid.fdroid".parse().unwrap(), enabled: true, favorite: true, - ignored_version: Some("1.2.3".to_owned()), + pin_version: Some("1.2.3".to_owned()), repository_id: None, package_resolution: StoredPackageResolution::OfficialRepositoryPackage, }) @@ -1119,13 +1178,58 @@ mod tests { ); assert!(packages[0].enabled); assert!(packages[0].favorite); - assert_eq!(packages[0].ignored_version.as_deref(), Some("1.2.3")); + assert_eq!(packages[0].pin_version.as_deref(), Some("1.2.3")); assert_eq!( packages[0].package_resolution, StoredPackageResolution::OfficialRepositoryPackage ); } + #[test] + fn main_db_pins_and_unpins_tracked_package_version() { + let db = MainDb::open_in_memory().unwrap(); + let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + + let pinned = db + .set_tracked_package_pin_version(&package_id, Some("1.2.3")) + .unwrap(); + assert_eq!(pinned.pin_version.as_deref(), Some("1.2.3")); + assert_eq!( + pinned.package_resolution, + StoredPackageResolution::MissingPackageDefinition + ); + + let unpinned = db + .set_tracked_package_pin_version(&package_id, None) + .unwrap(); + assert_eq!(unpinned.pin_version, None); + } + + #[test] + fn main_db_migrates_legacy_ignored_version_column_to_pin_version() { + let conn = Connection::open_in_memory().unwrap(); + conn.execute_batch( + r#" +CREATE TABLE tracked_packages ( + package_id TEXT PRIMARY KEY, + enabled INTEGER NOT NULL DEFAULT 1, + favorite INTEGER NOT NULL DEFAULT 0, + ignored_version TEXT, + repository_id TEXT, + package_resolution TEXT NOT NULL DEFAULT 'missing_package_definition' +); +INSERT INTO tracked_packages(package_id, ignored_version) +VALUES ('android/org.fdroid.fdroid', '1.2.3'); +"#, + ) + .unwrap(); + let db = MainDb { conn }; + db.migrate().unwrap(); + + let packages = db.tracked_packages().unwrap(); + assert_eq!(packages[0].pin_version.as_deref(), Some("1.2.3")); + } + #[test] fn generated_tracking_preserves_existing_user_state_on_conflict() { let db = MainDb::open_in_memory().unwrap(); @@ -1135,7 +1239,7 @@ mod tests { package_id: package_id.clone(), enabled: false, favorite: true, - ignored_version: Some("9.9.9".to_owned()), + pin_version: Some("9.9.9".to_owned()), repository_id: None, package_resolution: StoredPackageResolution::OfficialRepositoryPackage, }) @@ -1148,7 +1252,7 @@ mod tests { assert_eq!(packages.len(), 1); assert!(!packages[0].enabled); assert!(packages[0].favorite); - assert_eq!(packages[0].ignored_version.as_deref(), Some("9.9.9")); + assert_eq!(packages[0].pin_version.as_deref(), Some("9.9.9")); assert_eq!(packages[0].repository_id, None); assert_eq!( packages[0].package_resolution, @@ -1165,7 +1269,7 @@ mod tests { package_id: package_id.clone(), enabled: false, favorite: true, - ignored_version: Some("9.9.9".to_owned()), + pin_version: Some("9.9.9".to_owned()), repository_id: None, package_resolution: StoredPackageResolution::MissingPackageDefinition, }) @@ -1178,7 +1282,7 @@ mod tests { assert_eq!(packages.len(), 1); assert!(!packages[0].enabled); assert!(packages[0].favorite); - assert_eq!(packages[0].ignored_version.as_deref(), Some("9.9.9")); + assert_eq!(packages[0].pin_version.as_deref(), Some("9.9.9")); assert_eq!(packages[0].repository_id.as_ref(), Some(&local_autogen)); assert_eq!( packages[0].package_resolution, @@ -1195,7 +1299,7 @@ mod tests { package_id: package_id.clone(), enabled: true, favorite: true, - ignored_version: Some("9.9.9".to_owned()), + pin_version: Some("9.9.9".to_owned()), repository_id: None, package_resolution: StoredPackageResolution::OfficialRepositoryPackage, }) @@ -1210,7 +1314,7 @@ mod tests { package_id: package_id.clone(), enabled: true, favorite: true, - ignored_version: Some("9.9.9".to_owned()), + pin_version: Some("9.9.9".to_owned()), repository_id: None, package_resolution: StoredPackageResolution::MissingPackageDefinition, }) From ae5b00ccf0989ee1b0b69521bb508d4136c2bcb4 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 13:41:37 +0800 Subject: [PATCH 25/71] feat(cli): add runtime script debug tasks --- crates/getter-cli/src/lib.rs | 507 ++++++++++++++++-- crates/getter-cli/tests/bdd_cli.rs | 154 +++++- .../tests/features/cli/task_lifecycle.feature | 52 +- 3 files changed, 621 insertions(+), 92 deletions(-) diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index ed72425..d8317fe 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -8,6 +8,7 @@ use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; +use getter_core::runtime::{GetterRuntime, SealedActionPlan}; use getter_core::task::{ DownloadTaskRequest, InstallHandoffStatus, TaskEventPage, DOWNLOAD_REQUEST_FORMAT, DOWNLOAD_REQUEST_VERSION, @@ -19,6 +20,7 @@ use getter_downloader::{ }; use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; +use getter_operations::runtime as runtime_operations; use getter_storage::legacy_room::{ map_legacy_app, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, }; @@ -74,24 +76,27 @@ pub enum CliCommand { UpdateCheck { fixture: PathBuf, }, - TaskSubmit { + DebugFakeTaskSubmit { request: PathBuf, }, - TaskRun { + DebugFakeTaskRun { task_id: String, }, - TaskList, - TaskCancel { + DebugFakeTaskList, + DebugFakeTaskCancel { task_id: String, }, - TaskEvents { + DebugFakeTaskEvents { after: u64, limit: usize, }, - TaskInstallResult { + DebugFakeTaskInstallResult { handoff_id: String, status: InstallHandoffStatus, }, + RuntimeScript { + script: PathBuf, + }, AutogenInstalledPreview { inventory: PathBuf, }, @@ -145,6 +150,8 @@ pub enum CliError { Update(String), #[error("download task error: {0}")] Download(String), + #[error("runtime error: {0}")] + Runtime(String), #[error("autogen error: {0}")] Autogen(String), #[error("Legacy Room export bundle is invalid")] @@ -162,9 +169,11 @@ impl CliError { match self { Self::Usage(_) => ExitCode::Usage, Self::Storage(_) => ExitCode::Storage, - Self::Repository(_) | Self::PackageEval(_) | Self::Update(_) | Self::Autogen(_) => { - ExitCode::GenericFailure - } + Self::Repository(_) + | Self::PackageEval(_) + | Self::Update(_) + | Self::Runtime(_) + | Self::Autogen(_) => ExitCode::GenericFailure, Self::Download(_) => ExitCode::Download, Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } @@ -181,6 +190,7 @@ impl CliError { Self::PackageEval(_) => "package.eval_error", Self::Update(_) => "update.check_error", Self::Download(_) => "download.task_error", + Self::Runtime(_) => "runtime.error", Self::Autogen(_) => "autogen.error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", @@ -197,6 +207,7 @@ impl CliError { Self::PackageEval(_) => "Getter package evaluation failed", Self::Update(_) => "Getter update check failed", Self::Download(_) => "Getter download task operation failed", + Self::Runtime(_) => "Getter runtime operation failed", Self::Autogen(_) => "Getter autogen operation failed", Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", Self::UnsupportedLegacyBundle { .. } => { @@ -215,6 +226,7 @@ impl CliError { | Self::PackageEval(detail) | Self::Update(detail) | Self::Download(detail) + | Self::Runtime(detail) | Self::Autogen(detail) => Some(detail.as_str()), Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } @@ -235,6 +247,7 @@ impl CliError { | Self::PackageEval(_) | Self::Update(_) | Self::Download(_) + | Self::Runtime(_) | Self::Autogen(_) => None, } } @@ -405,41 +418,65 @@ where fixture: PathBuf::from(fixture), } } - [domain, command, flag, request] - if domain == "task" && command == "submit" && flag == "--request" => + [domain, subject, command, flag, request] + if domain == "debug" + && subject == "fake-task" + && command == "submit" + && flag == "--request" => { - CliCommand::TaskSubmit { + CliCommand::DebugFakeTaskSubmit { request: PathBuf::from(request), } } - [domain, command, task_id] if domain == "task" && command == "run" => CliCommand::TaskRun { - task_id: task_id.clone(), - }, - [domain, command] if domain == "task" && command == "list" => CliCommand::TaskList, - [domain, command, task_id] if domain == "task" && command == "cancel" => { - CliCommand::TaskCancel { + [domain, subject, command, task_id] + if domain == "debug" && subject == "fake-task" && command == "run" => + { + CliCommand::DebugFakeTaskRun { task_id: task_id.clone(), } } - [domain, command, after_flag, after, limit_flag, limit] - if domain == "task" + [domain, subject, command] + if domain == "debug" && subject == "fake-task" && command == "list" => + { + CliCommand::DebugFakeTaskList + } + [domain, subject, command, task_id] + if domain == "debug" && subject == "fake-task" && command == "cancel" => + { + CliCommand::DebugFakeTaskCancel { + task_id: task_id.clone(), + } + } + [domain, subject, command, after_flag, after, limit_flag, limit] + if domain == "debug" + && subject == "fake-task" && command == "events" && after_flag == "--after" && limit_flag == "--limit" => { - CliCommand::TaskEvents { + CliCommand::DebugFakeTaskEvents { after: parse_u64(after, "--after")?, limit: parse_positive_usize(limit, "--limit")?, } } - [domain, command, handoff_id, status_flag, status] - if domain == "task" && command == "install-result" && status_flag == "--status" => + [domain, subject, command, handoff_id, status_flag, status] + if domain == "debug" + && subject == "fake-task" + && command == "install-result" + && status_flag == "--status" => { - CliCommand::TaskInstallResult { + CliCommand::DebugFakeTaskInstallResult { handoff_id: handoff_id.clone(), status: parse_install_handoff_status(status)?, } } + [domain, command, flag, script] + if domain == "runtime" && command == "script" && flag == "--script" => + { + CliCommand::RuntimeScript { + script: PathBuf::from(script), + } + } [domain, subject, action, flag, inventory] if domain == "autogen" && subject == "installed" @@ -608,46 +645,49 @@ fn execute(invocation: CliInvocation) -> Result { CliError::Update(format!("failed to serialize update check: {source}")) }) } - CliCommand::TaskSubmit { request } => { + CliCommand::DebugFakeTaskSubmit { request } => { let db = open_main_db(&invocation.data_dir)?; let request = read_download_task_request(&request)?; serde_json::to_value(submit_fake_download_task(&db, request).map_err(|source| { - CliError::Download(format!("offline task submit failed: {source}")) + CliError::Download(format!("offline fake task submit failed: {source}")) })?) .map_err(|source| CliError::Download(format!("failed to serialize task: {source}"))) } - CliCommand::TaskRun { task_id } => { + CliCommand::DebugFakeTaskRun { task_id } => { let db = open_main_db(&invocation.data_dir)?; serde_json::to_value(run_fake_download_task(&db, &task_id).map_err(|source| { - CliError::Download(format!("offline task run failed: {source}")) + CliError::Download(format!("offline fake task run failed: {source}")) })?) .map_err(|source| CliError::Download(format!("failed to serialize task: {source}"))) } - CliCommand::TaskList => { + CliCommand::DebugFakeTaskList => { let db = open_main_db(&invocation.data_dir)?; Ok(json!({ "tasks": db.download_tasks()? })) } - CliCommand::TaskCancel { task_id } => { + CliCommand::DebugFakeTaskCancel { task_id } => { let db = open_main_db(&invocation.data_dir)?; serde_json::to_value(cancel_download_task(&db, &task_id).map_err(|source| { - CliError::Download(format!("offline task cancel failed: {source}")) + CliError::Download(format!("offline fake task cancel failed: {source}")) })?) .map_err(|source| CliError::Download(format!("failed to serialize task: {source}"))) } - CliCommand::TaskEvents { after, limit } => { + CliCommand::DebugFakeTaskEvents { after, limit } => { let db = open_main_db(&invocation.data_dir)?; let events: TaskEventPage = db.task_events_after(after, limit)?; serde_json::to_value(events).map_err(|source| { - CliError::Download(format!("failed to serialize task events: {source}")) + CliError::Download(format!("failed to serialize fake task events: {source}")) }) } - CliCommand::TaskInstallResult { handoff_id, status } => { + CliCommand::DebugFakeTaskInstallResult { handoff_id, status } => { let db = open_main_db(&invocation.data_dir)?; serde_json::to_value(record_install_result(&db, &handoff_id, status).map_err( - |source| CliError::Download(format!("offline install result failed: {source}")), + |source| { + CliError::Download(format!("offline fake install result failed: {source}")) + }, )?) .map_err(|source| CliError::Download(format!("failed to serialize handoff: {source}"))) } + CliCommand::RuntimeScript { script } => run_runtime_script(&script), CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; @@ -954,6 +994,227 @@ fn read_download_task_request(path: &Path) -> Result, +} + +#[derive(Debug, Deserialize)] +struct RuntimeScriptStep { + operation: String, + #[serde(default)] + payload: Value, + #[serde(default)] + plan: Option, +} + +fn run_runtime_script(path: &Path) -> Result { + let bytes = fs::read(path) + .map_err(|source| CliError::Runtime(format!("failed to read runtime script: {source}")))?; + let script: RuntimeScript = serde_json::from_slice(&bytes).map_err(|source| { + CliError::Runtime(format!("failed to parse runtime script JSON: {source}")) + })?; + let mut runtime = GetterRuntime::new(); + let mut context = RuntimeScriptContext::default(); + let mut outputs = Vec::new(); + for step in script.steps { + let data = execute_runtime_script_step(&mut runtime, &mut context, step)?; + outputs.push(data); + } + Ok(json!({ "steps": outputs })) +} + +#[derive(Default)] +struct RuntimeScriptContext { + last_action_id: Option, + last_task_id: Option, +} + +fn execute_runtime_script_step( + runtime: &mut GetterRuntime, + context: &mut RuntimeScriptContext, + step: RuntimeScriptStep, +) -> Result { + let operation = step.operation.as_str(); + let data = match operation { + "issue_action" => { + let plan = match step.plan { + Some(plan) => plan, + None => serde_json::from_value(step.payload).map_err(|source| { + CliError::Runtime(format!("failed to parse issue_action plan: {source}")) + })?, + }; + runtime_operations::issue_action(runtime, plan) + } + "submit_action" => runtime_json_operation( + runtime, + runtime_operations::submit_action_json, + default_action_payload(context, step.payload)?, + )?, + "task_get" => runtime_json_query_operation( + runtime, + runtime_operations::task_get_json, + default_task_payload(context, step.payload)?, + )?, + "task_list" => runtime_json_query_operation( + runtime, + runtime_operations::task_list_json, + empty_object_payload(step.payload), + )?, + "task_start" => runtime_json_operation( + runtime, + runtime_operations::task_start_json, + default_task_payload(context, step.payload)?, + )?, + "task_download_progress" => runtime_json_operation( + runtime, + runtime_operations::task_download_progress_json, + default_task_payload(context, step.payload)?, + )?, + "task_complete_download" => runtime_json_operation( + runtime, + runtime_operations::task_complete_download_json, + default_task_payload(context, step.payload)?, + )?, + "task_pause" => runtime_json_operation( + runtime, + runtime_operations::task_pause_json, + default_task_payload(context, step.payload)?, + )?, + "task_resume" => runtime_json_operation( + runtime, + runtime_operations::task_resume_json, + default_task_payload(context, step.payload)?, + )?, + "task_user_result" => runtime_json_operation( + runtime, + runtime_operations::task_user_result_json, + default_task_payload(context, step.payload)?, + )?, + "task_cancel" => runtime_json_operation( + runtime, + runtime_operations::task_cancel_json, + default_task_payload(context, step.payload)?, + )?, + "task_retry" => runtime_json_operation( + runtime, + runtime_operations::task_retry_json, + default_task_payload(context, step.payload)?, + )?, + "task_remove" => runtime_json_operation( + runtime, + runtime_operations::task_remove_json, + default_task_payload(context, step.payload)?, + )?, + "task_clean" => runtime_json_operation( + runtime, + runtime_operations::task_clean_json, + empty_object_payload(step.payload), + )?, + other => { + return Err(CliError::Runtime(format!( + "unsupported runtime script operation '{other}'" + ))) + } + }; + remember_runtime_script_ids(context, &data); + Ok(json!({ "operation": operation, "data": data })) +} + +fn runtime_json_operation( + runtime: &mut GetterRuntime, + operation: fn( + &mut GetterRuntime, + &str, + ) -> Result, + payload: Value, +) -> Result { + let payload = serde_json::to_string(&payload).map_err(|source| { + CliError::Runtime(format!("failed to serialize runtime request: {source}")) + })?; + operation(runtime, &payload).map_err(|source| CliError::Runtime(source.to_string())) +} + +fn runtime_json_query_operation( + runtime: &GetterRuntime, + operation: fn(&GetterRuntime, &str) -> Result, + payload: Value, +) -> Result { + let payload = serde_json::to_string(&payload).map_err(|source| { + CliError::Runtime(format!("failed to serialize runtime request: {source}")) + })?; + operation(runtime, &payload).map_err(|source| CliError::Runtime(source.to_string())) +} + +fn empty_object_payload(payload: Value) -> Value { + if payload.is_null() { + json!({}) + } else { + payload + } +} + +fn default_action_payload( + context: &RuntimeScriptContext, + payload: Value, +) -> Result { + if payload.is_null() { + let action_id = context.last_action_id.as_ref().ok_or_else(|| { + CliError::Runtime("runtime script has no previous action_id".to_owned()) + })?; + return Ok(json!({ "action_id": action_id })); + } + replace_runtime_script_tokens(context, payload) +} + +fn default_task_payload(context: &RuntimeScriptContext, payload: Value) -> Result { + if payload.is_null() { + let task_id = context.last_task_id.as_ref().ok_or_else(|| { + CliError::Runtime("runtime script has no previous task_id".to_owned()) + })?; + return Ok(json!({ "task_id": task_id })); + } + replace_runtime_script_tokens(context, payload) +} + +fn replace_runtime_script_tokens( + context: &RuntimeScriptContext, + payload: Value, +) -> Result { + match payload { + Value::String(value) if value == "$last_action_id" => { + Ok(Value::String(context.last_action_id.clone().ok_or_else( + || CliError::Runtime("runtime script has no previous action_id".to_owned()), + )?)) + } + Value::String(value) if value == "$last_task_id" => { + Ok(Value::String(context.last_task_id.clone().ok_or_else( + || CliError::Runtime("runtime script has no previous task_id".to_owned()), + )?)) + } + Value::Array(values) => values + .into_iter() + .map(|value| replace_runtime_script_tokens(context, value)) + .collect::, _>>() + .map(Value::Array), + Value::Object(values) => values + .into_iter() + .map(|(key, value)| Ok((key, replace_runtime_script_tokens(context, value)?))) + .collect::, CliError>>() + .map(Value::Object), + other => Ok(other), + } +} + +fn remember_runtime_script_ids(context: &mut RuntimeScriptContext, data: &Value) { + if let Some(action_id) = data.get("action_id").and_then(Value::as_str) { + context.last_action_id = Some(action_id.to_owned()); + } + if let Some(task_id) = data.get("task_id").and_then(Value::as_str) { + context.last_task_id = Some(task_id.to_owned()); + } +} + fn repo_path(repo: &StoredRepository) -> Result { repo.path .as_ref() @@ -1214,7 +1475,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |task submit --request |task run |task list|task cancel |task events --after --limit |task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1270,12 +1531,13 @@ impl CliCommand { Self::VersionPin { .. } => "version pin", Self::VersionUnpin { .. } => "version unpin", Self::UpdateCheck { .. } => "update check", - Self::TaskSubmit { .. } => "task submit", - Self::TaskRun { .. } => "task run", - Self::TaskList => "task list", - Self::TaskCancel { .. } => "task cancel", - Self::TaskEvents { .. } => "task events", - Self::TaskInstallResult { .. } => "task install-result", + Self::DebugFakeTaskSubmit { .. } => "debug fake-task submit", + Self::DebugFakeTaskRun { .. } => "debug fake-task run", + Self::DebugFakeTaskList => "debug fake-task list", + Self::DebugFakeTaskCancel { .. } => "debug fake-task cancel", + Self::DebugFakeTaskEvents { .. } => "debug fake-task events", + Self::DebugFakeTaskInstallResult { .. } => "debug fake-task install-result", + Self::RuntimeScript { .. } => "runtime script", Self::AutogenInstalledPreview { .. } => "autogen installed preview", Self::AutogenInstalledApply { .. } => "autogen installed apply", Self::AutogenCleanupPreview { .. } => "autogen cleanup preview", @@ -1337,6 +1599,167 @@ mod tests { ); } + #[test] + fn old_task_namespace_is_not_public_cli_surface() { + let output = run(["getter", "--data-dir", "/tmp/ua-getter", "task", "list"]); + assert_eq!(output.exit_code, ExitCode::Usage); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["error"]["code"], "cli.usage"); + } + + #[test] + fn parses_runtime_script_command() { + let parsed = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "runtime", + "script", + "--script", + "/tmp/runtime-script.json", + ]) + .unwrap(); + assert_eq!( + parsed.command, + CliCommand::RuntimeScript { + script: PathBuf::from("/tmp/runtime-script.json"), + } + ); + } + + #[test] + fn run_runtime_script_exercises_in_memory_task_remove_and_clean() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path().join("getter-data"); + let script = temp.path().join("runtime-script.json"); + fs::write( + &script, + serde_json::to_vec_pretty(&json!({ + "steps": [ + { + "operation": "issue_action", + "plan": { + "package_id": "android/org.fdroid.fdroid", + "actions": [ + { + "type": "download", + "url": "https://example.invalid/app.apk", + "file_name": "app.apk" + } + ], + "lua_object": { + "object_id": "debug:android/org.fdroid.fdroid", + "dependency_digest": "debug-digest" + } + } + }, + { "operation": "submit_action" }, + { "operation": "task_start" }, + { + "operation": "task_download_progress", + "payload": { + "task_id": "$last_task_id", + "current_bits": 10, + "total_bits": 20 + } + }, + { "operation": "task_complete_download" }, + { "operation": "task_remove" }, + { "operation": "task_clean", "payload": { "mode": "all_inactive" } } + ] + })) + .unwrap(), + ) + .unwrap(); + + let output = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "runtime".to_owned(), + "script".to_owned(), + "--script".to_owned(), + script.to_string_lossy().to_string(), + ]); + + assert_eq!(output.exit_code, ExitCode::Success); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["command"], "runtime script"); + let steps = json["data"]["steps"].as_array().unwrap(); + assert_eq!(steps[0]["data"]["action_id"], "action-1"); + assert_eq!(steps[1]["data"]["task_id"], "task-1"); + assert_eq!(steps[4]["data"]["status"], "completed"); + assert_eq!(steps[5]["data"]["task_id"], "task-1"); + assert_eq!(steps[6]["data"]["tasks"].as_array().unwrap().len(), 0); + } + + #[test] + fn runtime_script_does_not_preserve_tasks_across_cli_invocations() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path().join("getter-data"); + let create_script = temp.path().join("create-runtime-task.json"); + fs::write( + &create_script, + serde_json::to_vec_pretty(&json!({ + "steps": [ + { + "operation": "issue_action", + "plan": { + "package_id": "android/org.fdroid.fdroid", + "actions": [], + "lua_object": { + "object_id": "debug:android/org.fdroid.fdroid", + "dependency_digest": "debug-digest" + } + } + }, + { "operation": "submit_action" } + ] + })) + .unwrap(), + ) + .unwrap(); + let list_script = temp.path().join("list-runtime-tasks.json"); + fs::write( + &list_script, + serde_json::to_vec_pretty(&json!({ + "steps": [{ "operation": "task_list" }] + })) + .unwrap(), + ) + .unwrap(); + + let create = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "runtime".to_owned(), + "script".to_owned(), + "--script".to_owned(), + create_script.to_string_lossy().to_string(), + ]); + assert_eq!(create.exit_code, ExitCode::Success); + + let list = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "runtime".to_owned(), + "script".to_owned(), + "--script".to_owned(), + list_script.to_string_lossy().to_string(), + ]); + assert_eq!(list.exit_code, ExitCode::Success); + let json: Value = serde_json::from_str(&list.stdout).unwrap(); + assert_eq!( + json["data"]["steps"][0]["data"]["tasks"] + .as_array() + .unwrap() + .len(), + 0 + ); + } + #[test] fn run_init_creates_sqlite_database_files_and_json_envelope() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 733827f..c721b01 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -16,6 +16,7 @@ struct CliWorld { autogen_preview: Option, update_fixture: Option, task_request: Option, + runtime_script: Option, remembered_task_id: Option, remembered_event_cursor: Option, remembered_handoff_id: Option, @@ -303,6 +304,44 @@ fn malformed_offline_download_request(world: &mut CliWorld) { world.task_request = Some(request); } +#[given("a runtime script that submits completes removes and cleans a task")] +fn runtime_script_submits_completes_removes_and_cleans_task(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let script = temp.path().join("runtime-script.json"); + fs::write( + &script, + serde_json::to_vec_pretty(&serde_json::json!({ + "steps": [ + { + "operation": "issue_action", + "plan": { + "package_id": "android/org.fdroid.fdroid", + "actions": [ + { + "type": "download", + "url": "https://example.invalid/app.apk", + "file_name": "app.apk" + } + ], + "lua_object": { + "object_id": "debug:android/org.fdroid.fdroid", + "dependency_digest": "debug-digest" + } + } + }, + { "operation": "submit_action" }, + { "operation": "task_start" }, + { "operation": "task_complete_download" }, + { "operation": "task_remove" }, + { "operation": "task_clean", "payload": { "mode": "all_inactive" } } + ] + })) + .expect("runtime script serializes"), + ) + .expect("write runtime script"); + world.runtime_script = Some(script); +} + #[given(expr = "a fixture Lua repository {string} with package {string}")] fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); @@ -562,13 +601,14 @@ fn run_getter_version_unpin(world: &mut CliWorld, package_id: String) { world.json = None; } -#[when("I run getter task submit for that request")] -fn run_getter_task_submit(world: &mut CliWorld) { +#[when("I run getter debug fake-task submit for that request")] +fn run_getter_debug_fake_task_submit(world: &mut CliWorld) { let request = world.task_request.as_ref().expect("task request exists"); let output = run_getter( world, [ - "task".to_owned(), + "debug".to_owned(), + "fake-task".to_owned(), "submit".to_owned(), "--request".to_owned(), request.to_string_lossy().to_string(), @@ -578,43 +618,67 @@ fn run_getter_task_submit(world: &mut CliWorld) { world.json = None; } -#[when("I run getter task list")] -fn run_getter_task_list(world: &mut CliWorld) { - let output = run_getter(world, ["task".to_owned(), "list".to_owned()]); +#[when("I run getter debug fake-task list")] +fn run_getter_debug_fake_task_list(world: &mut CliWorld) { + let output = run_getter( + world, + [ + "debug".to_owned(), + "fake-task".to_owned(), + "list".to_owned(), + ], + ); world.output = Some(output); world.json = None; } -#[when("I run getter task cancel for the remembered task")] -fn run_getter_task_cancel(world: &mut CliWorld) { +#[when("I run getter debug fake-task cancel for the remembered task")] +fn run_getter_debug_fake_task_cancel(world: &mut CliWorld) { let task_id = world .remembered_task_id .as_ref() .expect("remembered task id exists") .clone(); - let output = run_getter(world, ["task".to_owned(), "cancel".to_owned(), task_id]); + let output = run_getter( + world, + [ + "debug".to_owned(), + "fake-task".to_owned(), + "cancel".to_owned(), + task_id, + ], + ); world.output = Some(output); world.json = None; } -#[when("I run getter task run for the remembered task")] -fn run_getter_task_run(world: &mut CliWorld) { +#[when("I run getter debug fake-task run for the remembered task")] +fn run_getter_debug_fake_task_run(world: &mut CliWorld) { let task_id = world .remembered_task_id .as_ref() .expect("remembered task id exists") .clone(); - let output = run_getter(world, ["task".to_owned(), "run".to_owned(), task_id]); + let output = run_getter( + world, + [ + "debug".to_owned(), + "fake-task".to_owned(), + "run".to_owned(), + task_id, + ], + ); world.output = Some(output); world.json = None; } -#[when(expr = "I run getter task events after {int} limit {int}")] -fn run_getter_task_events_after_limit(world: &mut CliWorld, after: u64, limit: u64) { +#[when(expr = "I run getter debug fake-task events after {int} limit {int}")] +fn run_getter_debug_fake_task_events_after_limit(world: &mut CliWorld, after: u64, limit: u64) { let output = run_getter( world, [ - "task".to_owned(), + "debug".to_owned(), + "fake-task".to_owned(), "events".to_owned(), "--after".to_owned(), after.to_string(), @@ -626,16 +690,16 @@ fn run_getter_task_events_after_limit(world: &mut CliWorld, after: u64, limit: u world.json = None; } -#[when(expr = "I run getter task events after the remembered cursor limit {int}")] -fn run_getter_task_events_after_remembered_cursor(world: &mut CliWorld, limit: u64) { +#[when(expr = "I run getter debug fake-task events after the remembered cursor limit {int}")] +fn run_getter_debug_fake_task_events_after_remembered_cursor(world: &mut CliWorld, limit: u64) { let after = world .remembered_event_cursor .expect("remembered event cursor exists"); - run_getter_task_events_after_limit(world, after, limit); + run_getter_debug_fake_task_events_after_limit(world, after, limit); } -#[when(expr = "I run getter task install-result {string} for the remembered handoff")] -fn run_getter_task_install_result(world: &mut CliWorld, status: String) { +#[when(expr = "I run getter debug fake-task install-result {string} for the remembered handoff")] +fn run_getter_debug_fake_task_install_result(world: &mut CliWorld, status: String) { let handoff_id = world .remembered_handoff_id .as_ref() @@ -644,7 +708,8 @@ fn run_getter_task_install_result(world: &mut CliWorld, status: String) { let output = run_getter( world, [ - "task".to_owned(), + "debug".to_owned(), + "fake-task".to_owned(), "install-result".to_owned(), handoff_id, "--status".to_owned(), @@ -655,6 +720,25 @@ fn run_getter_task_install_result(world: &mut CliWorld, status: String) { world.json = None; } +#[when("I run getter runtime script for that script")] +fn run_getter_runtime_script(world: &mut CliWorld) { + let script = world + .runtime_script + .as_ref() + .expect("runtime script exists"); + let output = run_getter( + world, + [ + "runtime".to_owned(), + "script".to_owned(), + "--script".to_owned(), + script.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen installed preview for that inventory")] fn run_getter_autogen_installed_preview(world: &mut CliWorld) { let inventory = world.inventory.as_ref().expect("inventory exists"); @@ -892,7 +976,7 @@ fn output_contains_empty_repository_list(world: &mut CliWorld) { #[then("I remember the submitted task id")] fn remember_submitted_task_id(world: &mut CliWorld) { let json = current_json(world); - assert_eq!(json["command"], "task submit"); + assert_eq!(json["command"], "debug fake-task submit"); let task_id = json["data"]["task"]["id"] .as_str() .expect("task id should be a string") @@ -900,6 +984,20 @@ fn remember_submitted_task_id(world: &mut CliWorld) { world.remembered_task_id = Some(task_id); } +#[then("the runtime script output removes the completed task")] +fn runtime_script_output_removes_completed_task(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["command"], "runtime script"); + let steps = json["data"]["steps"].as_array().expect("runtime steps"); + assert_eq!(steps[0]["data"]["action_id"], "action-1"); + assert_eq!(steps[1]["data"]["task_id"], "task-1"); + assert_eq!(steps[3]["data"]["status"], "completed"); + assert_eq!(steps[4]["operation"], "task_remove"); + assert_eq!(steps[4]["data"]["task_id"], "task-1"); + assert_eq!(steps[5]["operation"], "task_clean"); + assert_eq!(steps[5]["data"]["tasks"].as_array().unwrap().len(), 0); +} + #[then(expr = "the task list contains the remembered task with status {string}")] fn task_list_contains_remembered_task_with_status(world: &mut CliWorld, status: String) { let task_id = world @@ -908,7 +1006,7 @@ fn task_list_contains_remembered_task_with_status(world: &mut CliWorld, status: .expect("remembered task id exists") .clone(); let json = current_json(world); - assert_eq!(json["command"], "task list"); + assert_eq!(json["command"], "debug fake-task list"); let tasks = json["data"]["tasks"].as_array().expect("tasks array"); let task = tasks .iter() @@ -920,7 +1018,7 @@ fn task_list_contains_remembered_task_with_status(world: &mut CliWorld, status: #[then(expr = "the task cancel result has status {string} and changed true")] fn task_cancel_result_changed_true(world: &mut CliWorld, status: String) { let json = current_json(world); - assert_eq!(json["command"], "task cancel"); + assert_eq!(json["command"], "debug fake-task cancel"); assert_eq!(json["data"]["status"], status); assert_eq!(json["data"]["changed"], true); } @@ -928,7 +1026,7 @@ fn task_cancel_result_changed_true(world: &mut CliWorld, status: String) { #[then(expr = "the task cancel result has status {string} and changed false")] fn task_cancel_result_changed_false(world: &mut CliWorld, status: String) { let json = current_json(world); - assert_eq!(json["command"], "task cancel"); + assert_eq!(json["command"], "debug fake-task cancel"); assert_eq!(json["data"]["status"], status); assert_eq!(json["data"]["changed"], false); } @@ -940,7 +1038,7 @@ fn task_run_result_has_status_and_install_handoff( handoff_status: String, ) { let json = current_json(world); - assert_eq!(json["command"], "task run"); + assert_eq!(json["command"], "debug fake-task run"); assert_eq!(json["data"]["task"]["status"], status); assert_eq!(json["data"]["install_handoff"]["status"], handoff_status); } @@ -948,7 +1046,7 @@ fn task_run_result_has_status_and_install_handoff( #[then(expr = "the task events output contains {int} events and has more events")] fn task_events_output_contains_events_and_has_more(world: &mut CliWorld, count: usize) { let json = current_json(world); - assert_eq!(json["command"], "task events"); + assert_eq!(json["command"], "debug fake-task events"); assert_eq!(json["data"]["events"].as_array().unwrap().len(), count); assert_eq!(json["data"]["has_more"], true); } @@ -989,7 +1087,7 @@ fn remember_install_handoff_id(world: &mut CliWorld) { #[then(expr = "the install result output has status {string}")] fn install_result_output_has_status(world: &mut CliWorld, status: String) { let json = current_json(world); - assert_eq!(json["command"], "task install-result"); + assert_eq!(json["command"], "debug fake-task install-result"); assert_eq!(json["data"]["handoff"]["status"], status); } diff --git a/crates/getter-cli/tests/features/cli/task_lifecycle.feature b/crates/getter-cli/tests/features/cli/task_lifecycle.feature index 6927548..34f71f9 100644 --- a/crates/getter-cli/tests/features/cli/task_lifecycle.feature +++ b/crates/getter-cli/tests/features/cli/task_lifecycle.feature @@ -1,89 +1,97 @@ -@getter-cli @task -Feature: Offline task lifecycle +@getter-cli @debug-fake-task +Feature: Offline fake task lifecycle + Scenario: Runtime script exercises ADR-0011 in-memory remove and clean + Given an initialized getter data directory + And a runtime script that submits completes removes and cleans a task + When I run getter runtime script for that script + Then the command succeeds + And the output is valid JSON + And the runtime script output removes the completed task + Scenario: User submits and lists an offline fake download task Given an initialized getter data directory And an offline download request for package "android/org.fdroid.fdroid" - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command succeeds And the output is valid JSON And I remember the submitted task id - When I run getter task list + When I run getter debug fake-task list Then the command succeeds And the task list contains the remembered task with status "queued" Scenario: User cancels a queued offline task Given an initialized getter data directory And an offline download request for package "android/org.fdroid.fdroid" - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command succeeds And I remember the submitted task id - When I run getter task cancel for the remembered task + When I run getter debug fake-task cancel for the remembered task Then the command succeeds And the task cancel result has status "canceled" and changed true - When I run getter task cancel for the remembered task + When I run getter debug fake-task cancel for the remembered task Then the command succeeds And the task cancel result has status "canceled" and changed false Scenario: User cannot cancel a succeeded offline task Given an initialized getter data directory And an offline download request for package "android/org.fdroid.fdroid" - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command succeeds And I remember the submitted task id - When I run getter task run for the remembered task + When I run getter debug fake-task run for the remembered task Then the command succeeds And the task run result has status "succeeded" and install handoff "requested" - When I run getter task cancel for the remembered task + When I run getter debug fake-task cancel for the remembered task Then the command fails with a download task error Scenario: User polls offline task events with cursor and limit Given an initialized getter data directory And an offline download request for package "android/org.fdroid.fdroid" - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command succeeds And I remember the submitted task id - When I run getter task run for the remembered task + When I run getter debug fake-task run for the remembered task Then the command succeeds - When I run getter task events after 0 limit 2 + When I run getter debug fake-task events after 0 limit 2 Then the command succeeds And the task events output contains 2 events and has more events And I remember the next event cursor - When I run getter task events after the remembered cursor limit 10 + When I run getter debug fake-task events after the remembered cursor limit 10 Then the command succeeds And the task events output contains event "install_handoff_requested" Scenario: User records an offline install handoff result Given an initialized getter data directory And an offline download request for package "android/org.fdroid.fdroid" - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command succeeds And I remember the submitted task id - When I run getter task run for the remembered task + When I run getter debug fake-task run for the remembered task Then the command succeeds And I remember the install handoff id - When I run getter task install-result "succeeded" for the remembered handoff + When I run getter debug fake-task install-result "succeeded" for the remembered handoff Then the command succeeds And the install result output has status "succeeded" Scenario: User cannot record getter-created requested state as an install result Given an initialized getter data directory And an offline download request for package "android/org.fdroid.fdroid" - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command succeeds And I remember the submitted task id - When I run getter task run for the remembered task + When I run getter debug fake-task run for the remembered task Then the command succeeds And I remember the install handoff id - When I run getter task install-result "requested" for the remembered handoff + When I run getter debug fake-task install-result "requested" for the remembered handoff Then the command fails with a CLI usage error Scenario: User cannot poll task events with a zero limit Given an initialized getter data directory - When I run getter task events after 0 limit 0 + When I run getter debug fake-task events after 0 limit 0 Then the command fails with a CLI usage error Scenario: User receives structured errors for malformed task requests Given an initialized getter data directory And a malformed offline download request - When I run getter task submit for that request + When I run getter debug fake-task submit for that request Then the command fails with a download task error From 1cde4f4bf01c5f703724f4ca62cc89496886eedd Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 13:55:29 +0800 Subject: [PATCH 26/71] feat(providers): wrap static updates as mock provider --- Cargo.lock | 1 + crates/getter-operations/Cargo.toml | 1 + crates/getter-operations/src/runtime.rs | 5 ++- crates/getter-providers/Cargo.toml | 2 +- crates/getter-providers/src/lib.rs | 54 ++++++++++++++++++++++++- 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e033e93..eefb4be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -565,6 +565,7 @@ name = "getter-operations" version = "0.1.0" dependencies = [ "getter-core", + "getter-providers", "getter-storage", "serde", "serde_json", diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index a36ba74..c81da6b 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -9,6 +9,7 @@ lua = ["getter-core/lua"] [dependencies] getter-core = { path = "../getter-core", default-features = false } +getter-providers = { path = "../getter-providers" } getter-storage = { path = "../getter-storage" } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index f6bfa38..1392322 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -22,6 +22,8 @@ use getter_core::{ #[cfg(feature = "lua")] use getter_core::{update::check_updates_offline, update::UpdateSelectionPolicy, RepositoryId}; #[cfg(feature = "lua")] +use getter_providers::StaticPackageUpdatesProvider; +#[cfg(feature = "lua")] use getter_storage::{MainDb, StorageError}; use serde::Deserialize; use serde_json::{json, Value}; @@ -135,10 +137,11 @@ pub fn issue_action_from_registered_package_json( ) -> Result { let request: RegisteredPackageUpdateActionRequest = parse_request(request_json)?; let (package, dependency_digest) = evaluate_registered_package(db, &request)?; + let candidates = StaticPackageUpdatesProvider.check_updates(&package); let update = check_updates_offline( package.id.clone(), request.installed_version, - package.updates.clone(), + candidates, UpdateSelectionPolicy { pin_version: request.pin_version, }, diff --git a/crates/getter-providers/Cargo.toml b/crates/getter-providers/Cargo.toml index 5a44be9..f5ae23c 100644 --- a/crates/getter-providers/Cargo.toml +++ b/crates/getter-providers/Cargo.toml @@ -4,4 +4,4 @@ version.workspace = true edition.workspace = true [dependencies] -getter-core = { path = "../getter-core" } +getter-core = { path = "../getter-core", default-features = false } diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index 35371aa..ee714b9 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -1,3 +1,55 @@ -//! getter-providers rewrite crate skeleton. +//! Provider executor scaffolding for the UpgradeAll getter rewrite. +//! +//! Real network/provider execution is intentionally deferred to a later ADR. +//! The first Phase D bridge uses package-declared static update candidates as a +//! mock provider so the rest of the runtime can exercise getter-owned update +//! selection and opaque action issuance without direct network side effects. pub use getter_core as core; + +use getter_core::{ResolvedPackage, UpdateCandidate}; + +/// Mock provider that returns the static `updates` candidates materialized from +/// a resolved Lua package table. +/// +/// This is development scaffolding, not the final live provider model. Keeping +/// it behind a provider-shaped boundary prevents operation code from treating +/// `package.updates` as the product update-check architecture. +#[derive(Debug, Default, Clone, Copy)] +pub struct StaticPackageUpdatesProvider; + +impl StaticPackageUpdatesProvider { + pub fn check_updates(self, package: &ResolvedPackage) -> Vec { + package.updates.clone() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::PackagePermissions; + + #[test] + fn static_provider_returns_package_declared_update_candidates() { + let package = ResolvedPackage { + id: "android/org.fdroid.fdroid".parse().unwrap(), + name: "F-Droid".to_owned(), + repository: "official".parse().unwrap(), + installed: Vec::new(), + permissions: PackagePermissions::default(), + source_priority: Vec::new(), + updates: vec![UpdateCandidate { + version: "1.2.0".to_owned(), + channel: Some("stable".to_owned()), + source: Some("fixture".to_owned()), + artifacts: Vec::new(), + }], + }; + + let candidates = StaticPackageUpdatesProvider.check_updates(&package); + + assert_eq!(candidates.len(), 1); + assert_eq!(candidates[0].version, "1.2.0"); + assert_eq!(candidates[0].source.as_deref(), Some("fixture")); + } +} From 60a65158aed40e14f0dea17427b4a92ec1e43818 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 25 Jun 2026 18:09:36 +0800 Subject: [PATCH 27/71] feat(operations): expose read model operations --- crates/getter-operations/src/lib.rs | 1 + crates/getter-operations/src/read_model.rs | 325 +++++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 crates/getter-operations/src/read_model.rs diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 6d75c88..4bd9337 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -7,4 +7,5 @@ pub mod autogen; pub mod legacy_room; +pub mod read_model; pub mod runtime; diff --git a/crates/getter-operations/src/read_model.rs b/crates/getter-operations/src/read_model.rs new file mode 100644 index 0000000..898864b --- /dev/null +++ b/crates/getter-operations/src/read_model.rs @@ -0,0 +1,325 @@ +//! Getter-owned read-model JSON operations for product UI snapshots. +//! +//! Flutter and Android bridge code should request these operations instead of +//! fabricating app/repository state in the UI layer. The operations read the +//! getter storage/repository/Lua model and return DTO-shaped JSON for adapters +//! to parse and render. + +#[cfg(feature = "lua")] +use getter_core::lua::evaluate_package_file; +#[cfg(feature = "lua")] +use getter_core::repository::{RepositoryLayout, RepositoryLoadError}; +#[cfg(feature = "lua")] +use getter_core::{PackageId, RepositoryId}; +use getter_storage::{MainDb, StorageError, StoredRepository, StoredTrackedPackage}; +#[cfg(feature = "lua")] +use serde::Deserialize; +use serde_json::{json, Value}; +use std::path::{Path, PathBuf}; + +const MAIN_DB_FILE: &str = "main.db"; + +#[derive(Debug, thiserror::Error)] +pub enum ReadModelOperationError { + #[error("invalid read-model request: {0}")] + InvalidRequest(String), + #[error("storage operation failed: {0}")] + Storage(#[from] StorageError), + #[cfg(feature = "lua")] + #[error("repository operation failed: {0}")] + Repository(#[from] RepositoryLoadError), + #[cfg(feature = "lua")] + #[error("package evaluation failed: {0}")] + PackageEval(String), + #[error("read-model response serialization failed: {0}")] + Serialize(String), +} + +impl ReadModelOperationError { + pub fn code(&self) -> &'static str { + match self { + Self::InvalidRequest(_) => "read_model.invalid_request", + Self::Storage(_) => "storage.error", + #[cfg(feature = "lua")] + Self::Repository(_) => "repository.error", + #[cfg(feature = "lua")] + Self::PackageEval(_) => "package.eval_error", + Self::Serialize(_) => "read_model.serialize_error", + } + } + + pub fn message(&self) -> &'static str { + match self { + Self::InvalidRequest(_) => "Getter read-model request is invalid", + Self::Storage(_) => "Getter storage operation failed", + #[cfg(feature = "lua")] + Self::Repository(_) => "Getter repository operation failed", + #[cfg(feature = "lua")] + Self::PackageEval(_) => "Getter package evaluation failed", + Self::Serialize(_) => "Getter read-model response serialization failed", + } + } + + pub fn detail(&self) -> Option { + match self { + Self::InvalidRequest(detail) | Self::Serialize(detail) => Some(detail.clone()), + Self::Storage(error) => Some(error.to_string()), + #[cfg(feature = "lua")] + Self::Repository(error) => Some(error.to_string()), + #[cfg(feature = "lua")] + Self::PackageEval(detail) => Some(detail.clone()), + } + } +} + +pub fn repository_list_json(data_dir: &Path) -> Result { + let db = open_main_db(data_dir)?; + Ok(json!({ + "repositories": db + .repositories()? + .into_iter() + .map(repository_json) + .collect::>(), + })) +} + +pub fn tracked_package_list_json(data_dir: &Path) -> Result { + let db = open_main_db(data_dir)?; + Ok(json!({ + "packages": db + .tracked_packages()? + .into_iter() + .map(tracked_package_json) + .collect::>(), + })) +} + +#[cfg(feature = "lua")] +pub fn package_eval_json( + data_dir: &Path, + request_json: &str, +) -> Result { + let request: PackageEvalRequest = parse_request(request_json)?; + let db = open_main_db(data_dir)?; + let package = match request.repository_id { + Some(repository_id) => { + evaluate_package_from_repo(&db, &repository_id, &request.package_id)? + } + None => evaluate_highest_priority_package(&db, &request.package_id)?, + }; + let package = serde_json::to_value(package) + .map_err(|source| ReadModelOperationError::Serialize(source.to_string()))?; + Ok(json!({ "package": package })) +} + +#[cfg(feature = "lua")] +#[derive(Debug, Deserialize)] +struct PackageEvalRequest { + package_id: PackageId, + #[serde(default)] + repository_id: Option, +} + +#[cfg(feature = "lua")] +fn parse_request(request_json: &str) -> Result +where + T: for<'de> Deserialize<'de>, +{ + serde_json::from_str(request_json) + .map_err(|source| ReadModelOperationError::InvalidRequest(source.to_string())) +} + +fn open_main_db(data_dir: &Path) -> Result { + Ok(MainDb::open(main_db_path(data_dir))?) +} + +#[cfg(feature = "lua")] +fn evaluate_package_from_repo( + db: &MainDb, + repo_id: &RepositoryId, + package_id: &PackageId, +) -> Result { + let repo = find_repository(db, repo_id)?; + let path = repo_path(&repo)?; + let layout = RepositoryLayout::load(&path)?; + let package_file = layout.package_file(package_id).ok_or_else(|| { + ReadModelOperationError::PackageEval(format!( + "package '{}' was not found in repository '{}'", + package_id, repo_id + )) + })?; + evaluate_package_file(&layout, &package_file.path) + .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())) +} + +#[cfg(feature = "lua")] +fn evaluate_highest_priority_package( + db: &MainDb, + package_id: &PackageId, +) -> Result { + for repo in db.repositories()? { + let path = repo_path(&repo)?; + let layout = RepositoryLayout::load(&path)?; + if let Some(package_file) = layout.package_file(package_id) { + return evaluate_package_file(&layout, &package_file.path) + .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())); + } + } + Err(ReadModelOperationError::PackageEval(format!( + "package '{package_id}' was not found in any registered repository" + ))) +} + +#[cfg(feature = "lua")] +fn find_repository( + db: &MainDb, + id: &RepositoryId, +) -> Result { + db.repositories()? + .into_iter() + .find(|repo| &repo.id == id) + .ok_or_else(|| { + ReadModelOperationError::PackageEval(format!("repository '{id}' is not registered")) + }) +} + +#[cfg(feature = "lua")] +fn repo_path(repo: &StoredRepository) -> Result { + repo.path.as_ref().map(PathBuf::from).ok_or_else(|| { + ReadModelOperationError::PackageEval(format!("repository '{}' has no path", repo.id)) + }) +} + +fn repository_json(repo: StoredRepository) -> Value { + json!({ + "id": repo.id.as_str(), + "name": repo.name, + "priority": repo.priority.value(), + "api_version": repo.api_version, + "path": repo.path, + "revision": repo.revision, + }) +} + +fn tracked_package_json(package: StoredTrackedPackage) -> Value { + json!({ + "id": package.package_id.to_string(), + "enabled": package.enabled, + "favorite": package.favorite, + "pin_version": package.pin_version, + "repository_id": package.repository_id.map(|id| id.to_string()), + "package_resolution": package.package_resolution.as_str(), + }) +} + +fn main_db_path(data_dir: &Path) -> PathBuf { + data_dir.join(MAIN_DB_FILE) +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; + use getter_core::{RepositoryId, RepositoryPriority}; + use getter_storage::{StoredPackageResolution, TrackedPackageUpsert}; + #[cfg(feature = "lua")] + use std::fs; + use tempfile::tempdir; + + #[test] + fn repository_and_tracked_package_lists_use_getter_storage_shapes() { + let temp = tempdir().unwrap(); + let db = MainDb::open(temp.path().join(MAIN_DB_FILE)).unwrap(); + let repo_id: RepositoryId = "official".parse().unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: repo_id.clone(), + name: "Official".to_owned(), + priority: RepositoryPriority::new(10), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(Path::new("/tmp/official")), + Some("rev-1"), + ) + .unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/org.fdroid.fdroid".parse().unwrap(), + enabled: true, + favorite: true, + pin_version: Some("1.2.3".to_owned()), + repository_id: Some(repo_id), + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + + let repositories = repository_list_json(temp.path()).unwrap(); + assert_eq!(repositories["repositories"][0]["id"], "official"); + assert_eq!(repositories["repositories"][0]["name"], "Official"); + assert_eq!(repositories["repositories"][0]["priority"], 10); + assert_eq!(repositories["repositories"][0]["revision"], "rev-1"); + + let packages = tracked_package_list_json(temp.path()).unwrap(); + assert_eq!(packages["packages"][0]["id"], "android/org.fdroid.fdroid"); + assert_eq!(packages["packages"][0]["favorite"], true); + assert_eq!(packages["packages"][0]["pin_version"], "1.2.3"); + assert_eq!(packages["packages"][0]["repository_id"], "official"); + assert_eq!( + packages["packages"][0]["package_resolution"], + "official_repository_package" + ); + } + + #[cfg(feature = "lua")] + #[test] + fn package_eval_reads_registered_lua_repository() { + let temp = tempdir().unwrap(); + let repo_path = temp.path().join("repo"); + fs::create_dir_all(repo_path.join("packages/android")).unwrap(); + fs::create_dir_all(repo_path.join("lib")).unwrap(); + fs::create_dir_all(repo_path.join("templates")).unwrap(); + fs::write( + repo_path.join("repo.toml"), + r#"id = "official" +name = "Official" +priority = 10 +api_version = "getter.repo.v1" +"#, + ) + .unwrap(); + fs::write( + repo_path.join("packages/android/org.fdroid.fdroid.lua"), + r#"return package_def { + id = "android/org.fdroid.fdroid", + name = "F-Droid", + installed = { + { kind = "android_package", package_name = "org.fdroid.fdroid" }, + }, + permissions = { free_network = true }, +} +"#, + ) + .unwrap(); + + let db = MainDb::open(temp.path().join(MAIN_DB_FILE)).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::new(10), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + ) + .unwrap(); + + let result = package_eval_json( + temp.path(), + r#"{"package_id":"android/org.fdroid.fdroid","repository_id":"official"}"#, + ) + .unwrap(); + assert_eq!(result["package"]["id"], "android/org.fdroid.fdroid"); + assert_eq!(result["package"]["name"], "F-Droid"); + assert_eq!(result["package"]["permissions"]["free_network"], true); + } +} From 3d268807a1a23e38789434a7e0a0659692ae8897 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Fri, 26 Jun 2026 18:26:34 +0800 Subject: [PATCH 28/71] feat(repository): load ADR-0012 data-dir config --- Cargo.lock | 7 + crates/getter-cli/src/lib.rs | 54 ++- crates/getter-cli/tests/bdd_cli.rs | 60 +-- .../features/cli/autogen_installed.feature | 10 +- crates/getter-core/Cargo.toml | 1 + crates/getter-core/src/autogen.rs | 62 +-- crates/getter-core/src/diagnostics.rs | 29 ++ crates/getter-core/src/lib.rs | 13 +- crates/getter-core/src/repository.rs | 388 +++++++++++++++++- crates/getter-operations/src/autogen.rs | 284 ++++++++++--- crates/getter-storage/src/lib.rs | 30 +- 11 files changed, 792 insertions(+), 146 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eefb4be..23480e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -536,6 +536,7 @@ dependencies = [ name = "getter-core" version = "0.1.0" dependencies = [ + "json_comments", "mlua", "serde", "serde_json", @@ -788,6 +789,12 @@ dependencies = [ "syn", ] +[[package]] +name = "json_comments" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbbfed4e59ba9750e15ba154fdfd9329cee16ff3df539c2666b70f58cc32105" + [[package]] name = "libc" version = "0.2.186" diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index d8317fe..020e1f9 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -7,7 +7,9 @@ use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; -use getter_core::repository::{RepositoryLayout, RepositoryMetadata}; +use getter_core::repository::{ + GetterDataDirLayout, RepositoryLayout, RepositoryMetadata, REPOSITORY_ROOT_METADATA_FILE, +}; use getter_core::runtime::{GetterRuntime, SealedActionPlan}; use getter_core::task::{ DownloadTaskRequest, InstallHandoffStatus, TaskEventPage, DOWNLOAD_REQUEST_FORMAT, @@ -37,6 +39,18 @@ const MAIN_DB_FILE: &str = "main.db"; const CACHE_DB_FILE: &str = "cache.db"; const MIGRATION_REPORTS_DIR: &str = "migration-reports"; const LEGACY_ROOM_MIGRATION_ID: &str = "legacy-room-v17"; +const REPOSITORY_ROOT_METADATA_STARTER: &str = r#"{ + "version": 1, + // Autogen writes to "autogen" by default. Uncomment and change this + // if generated packages should target another existing repository alias. + // "generated_repository": "autogen", + "priority": { + "local": 100, + "official": 0, + "autogen": -1 + } +} +"#; #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliInvocation { @@ -264,6 +278,9 @@ impl From for CliError { match value { AutogenOperationError::Storage(source) => Self::Storage(source.to_string()), AutogenOperationError::Repository(detail) => Self::Repository(detail), + AutogenOperationError::MissingGeneratedRepository { .. } => { + Self::Autogen(value.to_string()) + } AutogenOperationError::Autogen(detail) => Self::Autogen(detail), } } @@ -547,10 +564,14 @@ fn execute(invocation: CliInvocation) -> Result { match invocation.command { CliCommand::Init => { initialize_storage(&invocation.data_dir)?; + let layout = initialize_data_dir_layout(&invocation.data_dir)?; Ok(json!({ - "data_dir": invocation.data_dir, - "main_db": main_db_path(&invocation.data_dir), - "cache_db": cache_db_path(&invocation.data_dir), + "data_dir": layout.root, + "main_db": layout.main_db, + "cache_db": layout.cache_db, + "repo": layout.repository_root, + "rc": layout.runtime_config_root, + "repo_metadata": layout.repository_root.join(REPOSITORY_ROOT_METADATA_FILE), })) } CliCommand::AppList => { @@ -691,8 +712,9 @@ fn execute(invocation: CliInvocation) -> Result { CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; - let plan = autogen::build_local_autogen_plan(&db, &inventory)?; - Ok(autogen::installed_preview_json(&invocation.data_dir, &plan)) + let plan = + autogen::build_installed_autogen_plan(&invocation.data_dir, &db, &inventory)?; + autogen::installed_preview_json(&invocation.data_dir, &plan).map_err(CliError::from) } CliCommand::AutogenInstalledApply { preview, @@ -807,6 +829,26 @@ fn initialize_storage(data_dir: &Path) -> Result<(), CliError> { Ok(()) } +fn initialize_data_dir_layout(data_dir: &Path) -> Result { + let layout = GetterDataDirLayout::new(data_dir); + fs::create_dir_all(&layout.repository_root).map_err(|source| { + CliError::Storage(format!("failed to create repository root: {source}")) + })?; + fs::create_dir_all(&layout.runtime_config_root).map_err(|source| { + CliError::Storage(format!("failed to create runtime config root: {source}")) + })?; + let metadata_path = layout.repository_root.join(REPOSITORY_ROOT_METADATA_FILE); + if !metadata_path.exists() { + fs::write(&metadata_path, REPOSITORY_ROOT_METADATA_STARTER).map_err(|source| { + CliError::Storage(format!( + "failed to write repository root metadata '{}': {source}", + metadata_path.display() + )) + })?; + } + Ok(layout) +} + fn open_initialized_storage(data_dir: &Path) -> Result<(), CliError> { initialize_storage(data_dir) } diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index c721b01..073bb63 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -167,8 +167,8 @@ fn tampered_autogen_cleanup_preview_for_package(world: &mut CliWorld, package_id &preview, serde_json::to_vec_pretty(&serde_json::json!({ "operation": "cleanup.preview", - "target_repo_id": "local_autogen", - "target_repo_path": local_autogen_repo_path(world), + "target_repo_id": "autogen", + "target_repo_path": autogen_repo_path(world), "summary": { "candidate_count": 1, "skipped_count": 0, @@ -815,9 +815,9 @@ fn run_getter_autogen_cleanup_apply_accept_all(world: &mut CliWorld) { world.json = None; } -#[when("I run getter repo validate for local_autogen")] -fn run_getter_repo_validate_for_local_autogen(world: &mut CliWorld) { - let repo_path = local_autogen_repo_path(world); +#[when("I run getter repo validate for autogen")] +fn run_getter_repo_validate_for_autogen(world: &mut CliWorld) { + let repo_path = autogen_repo_path(world); let output = run_getter( world, [ @@ -830,8 +830,8 @@ fn run_getter_repo_validate_for_local_autogen(world: &mut CliWorld) { world.json = None; } -#[when(expr = "I run getter package eval for package {string} from local_autogen")] -fn run_getter_package_eval_from_local_autogen(world: &mut CliWorld, package_id: String) { +#[when(expr = "I run getter package eval for package {string} from autogen")] +fn run_getter_package_eval_from_autogen(world: &mut CliWorld, package_id: String) { let output = run_getter( world, [ @@ -839,7 +839,7 @@ fn run_getter_package_eval_from_local_autogen(world: &mut CliWorld, package_id: "eval".to_owned(), package_id, "--repo".to_owned(), - "local_autogen".to_owned(), + "autogen".to_owned(), ], ); world.output = Some(output); @@ -944,6 +944,13 @@ fn getter_data_directory_is_usable(world: &mut CliWorld) { let data_dir = world.data_dir.as_ref().expect("data dir exists"); assert!(data_dir.join("main.db").is_file(), "main.db should exist"); assert!(data_dir.join("cache.db").is_file(), "cache.db should exist"); + assert!(data_dir.join("repo").is_dir(), "repo root should exist"); + assert!(data_dir.join("rc").is_dir(), "rc root should exist"); + let repo_metadata = data_dir.join("repo/metadata.jsonc"); + assert!(repo_metadata.is_file(), "repo metadata should exist"); + let metadata = fs::read_to_string(repo_metadata).expect("repo metadata readable"); + assert!(metadata.contains("\"autogen\": -1")); + assert!(metadata.contains("// \"generated_repository\": \"autogen\"")); let output = run_getter(world, ["app".to_owned(), "list".to_owned()]); assert_success(&output); @@ -1259,11 +1266,11 @@ fn autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) ); } -#[then("the local_autogen repository has not been written")] -fn local_autogen_repository_has_not_been_written(world: &mut CliWorld) { +#[then("the autogen repository has not been written")] +fn autogen_repository_has_not_been_written(world: &mut CliWorld) { assert!( - !local_autogen_repo_path(world).exists(), - "preview must not create local_autogen" + !autogen_repo_path(world).exists(), + "preview must not create autogen repository" ); } @@ -1283,12 +1290,12 @@ fn save_autogen_preview_to_file(world: &mut CliWorld) { world.autogen_preview = Some(preview); } -#[then(expr = "the local_autogen repository contains generated package {string}")] -fn local_autogen_repository_contains_generated_package(world: &mut CliWorld, package_id: String) { - let path = local_autogen_repo_path(world).join(package_relative_path(&package_id)); +#[then(expr = "the autogen repository contains generated package {string}")] +fn autogen_repository_contains_generated_package(world: &mut CliWorld, package_id: String) { + let path = autogen_repo_path(world).join(package_relative_path(&package_id)); assert!(path.is_file(), "generated package should exist: {path:?}"); let content = fs::read_to_string(&path).expect("generated package readable"); - assert!(content.contains("@generated by UpgradeAll getter local_autogen")); + assert!(content.contains("@generated by UpgradeAll getter autogen")); } #[then(expr = "the app list contains autogen tracked package {string}")] @@ -1301,7 +1308,7 @@ fn app_list_contains_autogen_tracked_package(world: &mut CliWorld, package_id: S .iter() .find(|app| app["id"].as_str() == Some(package_id.as_str())) .unwrap_or_else(|| panic!("app list should contain {package_id}: {apps:?}")); - assert_eq!(app["repository_id"], "local_autogen"); + assert_eq!(app["repository_id"], "autogen"); assert_eq!(app["package_resolution"], "generate_local_package"); } @@ -1341,12 +1348,9 @@ fn autogen_cleanup_preview_contains_delete_candidate(world: &mut CliWorld, packa assert_eq!(candidate["action"], "delete"); } -#[then(expr = "the local_autogen repository does not contain generated package {string}")] -fn local_autogen_repository_does_not_contain_generated_package( - world: &mut CliWorld, - package_id: String, -) { - let path = local_autogen_repo_path(world).join(package_relative_path(&package_id)); +#[then(expr = "the autogen repository does not contain generated package {string}")] +fn autogen_repository_does_not_contain_generated_package(world: &mut CliWorld, package_id: String) { + let path = autogen_repo_path(world).join(package_relative_path(&package_id)); assert!( !path.exists(), "generated package should be deleted: {path:?}" @@ -1371,7 +1375,7 @@ fn replace_generated_autogen_package_with_user_edited_content( world: &mut CliWorld, package_id: String, ) { - let path = local_autogen_repo_path(world).join(package_relative_path(&package_id)); + let path = autogen_repo_path(world).join(package_relative_path(&package_id)); assert!(path.is_file(), "generated package should exist before edit"); fs::write( &path, @@ -1389,7 +1393,7 @@ fn local_repository_contains_preserved_package(world: &mut CliWorld, package_id: .data_dir .as_ref() .expect("data dir exists") - .join("repositories") + .join("repo") .join("local") .join(package_relative_path(&package_id)); assert!( @@ -1572,13 +1576,13 @@ fn current_json(world: &mut CliWorld) -> &Value { .get_or_insert_with(|| parse_stdout(world.output.as_ref().expect("command output exists"))) } -fn local_autogen_repo_path(world: &CliWorld) -> PathBuf { +fn autogen_repo_path(world: &CliWorld) -> PathBuf { world .data_dir .as_ref() .expect("data dir exists") - .join("repositories") - .join("local_autogen") + .join("repo") + .join("autogen") } fn package_relative_path(package_id: &str) -> PathBuf { diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index fc3c16a..5bbb8c9 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -7,7 +7,7 @@ Feature: Installed app autogen Then the command succeeds And the output is valid JSON And the autogen preview contains candidate "android/com.example.autogen" - And the local_autogen repository has not been written + And the autogen repository has not been written Scenario: User applies installed app autogen and evaluates the generated fallback package Given an initialized getter data directory @@ -17,11 +17,11 @@ Feature: Installed app autogen And I save the autogen preview to a file When I run getter autogen installed apply for that preview with accept-all Then the command succeeds - And the local_autogen repository contains generated package "android/com.example.autogen" + And the autogen repository contains generated package "android/com.example.autogen" And the app list contains autogen tracked package "android/com.example.autogen" - When I run getter repo validate for local_autogen + When I run getter repo validate for autogen Then the output reports a valid repository without network - When I run getter package eval for package "android/com.example.autogen" from local_autogen + When I run getter package eval for package "android/com.example.autogen" from autogen Then the output contains package "android/com.example.autogen" named "Example Autogen" Scenario: Higher-priority repositories suppress installed app autogen candidates @@ -49,7 +49,7 @@ Feature: Installed app autogen And I save the autogen preview to a file When I run getter autogen cleanup apply for that preview with accept-all Then the command succeeds - And the local_autogen repository does not contain generated package "android/com.example.old" + And the autogen repository does not contain generated package "android/com.example.old" And the app list does not contain package "android/com.example.old" Scenario: Cleanup rejects tampered previews for non-autogen tracked packages diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml index 187fcf6..4eac396 100644 --- a/crates/getter-core/Cargo.toml +++ b/crates/getter-core/Cargo.toml @@ -8,6 +8,7 @@ default = ["lua"] lua = ["dep:mlua"] [dependencies] +json_comments = "0.2" mlua = { version = "0.10", features = ["luajit", "vendored"], optional = true } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs index 2001ac5..a3bfe8e 100644 --- a/crates/getter-core/src/autogen.rs +++ b/crates/getter-core/src/autogen.rs @@ -15,9 +15,9 @@ pub const INSTALLED_INVENTORY_VERSION: u32 = 1; pub const AUTOGEN_MANIFEST_VERSION: u32 = 1; pub const LOCAL_REPOSITORY_ID: &str = "local"; pub const LOCAL_REPOSITORY_NAME: &str = "Local"; -pub const LOCAL_AUTOGEN_REPOSITORY_ID: &str = "local_autogen"; -pub const LOCAL_AUTOGEN_REPOSITORY_NAME: &str = "UpgradeAll Local Autogen"; -pub const GENERATED_MARKER: &str = "@generated by UpgradeAll getter local_autogen"; +pub const DEFAULT_AUTOGEN_REPOSITORY_ID: &str = "autogen"; +pub const DEFAULT_AUTOGEN_REPOSITORY_NAME: &str = "UpgradeAll Autogen"; +pub const GENERATED_MARKER: &str = "@generated by UpgradeAll getter autogen"; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct InstalledInventory { @@ -107,8 +107,8 @@ impl AutogenManifest { pub fn from_candidates(candidates: &[AutogenCandidate]) -> Self { Self { version: AUTOGEN_MANIFEST_VERSION, - repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID) - .expect("local_autogen is a valid repository id"), + repository_id: RepositoryId::new(DEFAULT_AUTOGEN_REPOSITORY_ID) + .expect("autogen is a valid repository id"), packages: candidates .iter() .map(|candidate| AutogenManifestEntry { @@ -146,13 +146,13 @@ pub enum AutogenError { PackageId(#[from] crate::PackageIdError), } -/// Build a deterministic local_autogen preview from installed inventory. +/// Build a deterministic installed-autogen preview from installed inventory. /// /// `covered_packages` contains package ids already supplied by repositories with -/// priority higher than `local_autogen`. Those packages are skipped so generated -/// fallback files do not compete with user-authored `local` or upstream package -/// files. -pub fn plan_local_autogen( +/// priority higher than the generated repository. Those packages are skipped so +/// generated fallback files do not compete with user-authored `local` or +/// upstream package files. +pub fn plan_installed_autogen( inventory: &InstalledInventory, covered_packages: &HashMap, ) -> Result { @@ -186,10 +186,10 @@ pub fn plan_local_autogen( skipped.sort_by_key(|skip| skip.package_id.to_string()); Ok(AutogenPlan { - repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID) - .expect("local_autogen is a valid repository id"), - repository_name: LOCAL_AUTOGEN_REPOSITORY_NAME.to_owned(), - repository_priority: RepositoryPriority::LOCAL_AUTOGEN, + repository_id: RepositoryId::new(DEFAULT_AUTOGEN_REPOSITORY_ID) + .expect("autogen is a valid repository id"), + repository_name: DEFAULT_AUTOGEN_REPOSITORY_NAME.to_owned(), + repository_priority: RepositoryPriority::GENERATED_FALLBACK, candidates, skipped, }) @@ -210,11 +210,11 @@ pub fn validate_installed_inventory(inventory: &InstalledInventory) -> Result<() Ok(()) } -pub fn local_autogen_repo_toml() -> String { +pub fn default_autogen_repo_toml() -> String { repo_toml( - LOCAL_AUTOGEN_REPOSITORY_ID, - LOCAL_AUTOGEN_REPOSITORY_NAME, - RepositoryPriority::LOCAL_AUTOGEN, + DEFAULT_AUTOGEN_REPOSITORY_ID, + DEFAULT_AUTOGEN_REPOSITORY_NAME, + RepositoryPriority::GENERATED_FALLBACK, ) } @@ -385,10 +385,13 @@ mod tests { version_code: Some(1020000), }]); - let plan = plan_local_autogen(&inventory, &HashMap::new()).unwrap(); + let plan = plan_installed_autogen(&inventory, &HashMap::new()).unwrap(); - assert_eq!(plan.repository_id.as_str(), LOCAL_AUTOGEN_REPOSITORY_ID); - assert_eq!(plan.repository_priority, RepositoryPriority::LOCAL_AUTOGEN); + assert_eq!(plan.repository_id.as_str(), DEFAULT_AUTOGEN_REPOSITORY_ID); + assert_eq!( + plan.repository_priority, + RepositoryPriority::GENERATED_FALLBACK + ); assert_eq!(plan.candidates.len(), 1); let candidate = &plan.candidates[0]; assert_eq!( @@ -416,7 +419,7 @@ mod tests { version_code: None, }]); - let plan = plan_local_autogen(&inventory, &covered).unwrap(); + let plan = plan_installed_autogen(&inventory, &covered).unwrap(); assert!(plan.candidates.is_empty()); assert_eq!(plan.skipped.len(), 1); @@ -450,7 +453,7 @@ mod tests { }, ]); - let plan = plan_local_autogen(&inventory, &HashMap::new()).unwrap(); + let plan = plan_installed_autogen(&inventory, &HashMap::new()).unwrap(); assert_eq!(plan.candidates.len(), 1); assert_eq!(plan.skipped.len(), 1); @@ -469,7 +472,7 @@ mod tests { }; assert!(matches!( - plan_local_autogen(&inventory, &HashMap::new()), + plan_installed_autogen(&inventory, &HashMap::new()), Err(AutogenError::UnsupportedInventoryFormat(_)) )); } @@ -482,12 +485,15 @@ mod tests { version_name: None, version_code: None, }]); - let plan = plan_local_autogen(&inventory, &HashMap::new()).unwrap(); + let plan = plan_installed_autogen(&inventory, &HashMap::new()).unwrap(); let manifest = AutogenManifest::from_candidates(&plan.candidates); assert_eq!(manifest.version, AUTOGEN_MANIFEST_VERSION); - assert_eq!(manifest.repository_id.as_str(), LOCAL_AUTOGEN_REPOSITORY_ID); + assert_eq!( + manifest.repository_id.as_str(), + DEFAULT_AUTOGEN_REPOSITORY_ID + ); assert_eq!(manifest.packages.len(), 1); assert_eq!( manifest.packages[0].content_hash, @@ -500,7 +506,7 @@ mod tests { fn generated_lua_evaluates_through_package_boundary() { let temp = tempfile::tempdir().unwrap(); let repo = temp.path(); - fs::write(repo.join("repo.toml"), local_autogen_repo_toml()).unwrap(); + fs::write(repo.join("repo.toml"), default_autogen_repo_toml()).unwrap(); fs::create_dir_all(repo.join("packages/android")).unwrap(); fs::create_dir(repo.join("lib")).unwrap(); fs::create_dir(repo.join("templates")).unwrap(); @@ -510,7 +516,7 @@ mod tests { version_name: None, version_code: None, }]); - let candidate = plan_local_autogen(&inventory, &HashMap::new()) + let candidate = plan_installed_autogen(&inventory, &HashMap::new()) .unwrap() .candidates .remove(0); diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index 62ed870..d338055 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -87,6 +87,35 @@ pub fn validate_repository_path(path: impl AsRef) -> RepositoryValidationR fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDiagnostic { let (code, path, message) = match error { + RepositoryLoadError::ReadRootMetadata { path, source } => ( + "repository.read_root_metadata", + path, + format!("failed to read repository root metadata: {source}"), + ), + RepositoryLoadError::ParseRootMetadata { path, source } => ( + "repository.parse_root_metadata", + path, + format!("failed to parse repository root metadata: {source}"), + ), + RepositoryLoadError::UnsupportedRootMetadataVersion { + path, + found, + expected, + } => ( + "repository.unsupported_root_metadata_version", + path, + format!("unsupported repository root metadata version {found}; expected {expected}"), + ), + RepositoryLoadError::ReadRepositoryRoot { path, source } => ( + "repository.read_root", + path, + format!("failed to read repository root: {source}"), + ), + RepositoryLoadError::MissingGeneratedRepository { alias, path } => ( + "repository.missing_generated_repository", + path, + format!("configured generated repository '{alias}' does not exist"), + ), RepositoryLoadError::ReadRepoToml { path, source } => ( "repository.read_repo_toml", path, diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index b1d759e..f02c4b0 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -146,7 +146,7 @@ impl From for String { } /// Repository identifier such as `official`, `community`, `local`, or -/// `local_autogen`. +/// `autogen`. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(try_from = "String", into = "String")] pub struct RepositoryId(String); @@ -206,7 +206,7 @@ pub struct RepositoryPriority(i32); impl RepositoryPriority { pub const LOCAL: Self = Self(100); pub const DEFAULT: Self = Self(0); - pub const LOCAL_AUTOGEN: Self = Self(-1); + pub const GENERATED_FALLBACK: Self = Self(-1); pub const fn new(value: i32) -> Self { Self(value) @@ -401,18 +401,15 @@ mod tests { #[test] fn repository_priority_higher_number_wins() { assert!(RepositoryPriority::LOCAL > RepositoryPriority::DEFAULT); - assert!(RepositoryPriority::DEFAULT > RepositoryPriority::LOCAL_AUTOGEN); + assert!(RepositoryPriority::DEFAULT > RepositoryPriority::GENERATED_FALLBACK); assert_eq!(RepositoryPriority::LOCAL.value(), 100); assert_eq!(RepositoryPriority::DEFAULT.value(), 0); - assert_eq!(RepositoryPriority::LOCAL_AUTOGEN.value(), -1); + assert_eq!(RepositoryPriority::GENERATED_FALLBACK.value(), -1); } #[test] fn repository_id_accepts_named_repositories() { - assert_eq!( - RepositoryId::new("local_autogen").unwrap().as_str(), - "local_autogen" - ); + assert_eq!(RepositoryId::new("autogen").unwrap().as_str(), "autogen"); assert_eq!( RepositoryId::new("community.1").unwrap().to_string(), "community.1" diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index c383acc..95b7fe8 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -2,10 +2,19 @@ use crate::{PackageId, PackageIdError, RepositoryId, RepositoryIdError, RepositoryPriority}; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; pub const REPO_API_VERSION_V1: &str = "getter.repo.v1"; +pub const MAIN_DB_FILE: &str = "main.db"; +pub const CACHE_DB_FILE: &str = "cache.db"; +pub const REPOSITORY_ROOT_DIR: &str = "repo"; +pub const RUNTIME_CONFIG_DIR: &str = "rc"; +pub const REPOSITORY_ROOT_METADATA_FILE: &str = "metadata.jsonc"; +pub const REPOSITORY_ROOT_METADATA_VERSION: u32 = 1; +pub const LOCAL_REPOSITORY_ALIAS: &str = "local"; +pub const DEFAULT_GENERATED_REPOSITORY_ALIAS: &str = "autogen"; #[derive(Debug, Clone, PartialEq, Eq)] pub struct RepositoryLayout { @@ -25,6 +34,200 @@ pub struct RepositoryMetadata { pub api_version: String, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GetterDataDirLayout { + pub root: PathBuf, + pub main_db: PathBuf, + pub cache_db: PathBuf, + pub repository_root: PathBuf, + pub runtime_config_root: PathBuf, +} + +impl GetterDataDirLayout { + pub fn new(root: impl AsRef) -> Self { + let root = root.as_ref().to_path_buf(); + Self { + main_db: root.join(MAIN_DB_FILE), + cache_db: root.join(CACHE_DB_FILE), + repository_root: root.join(REPOSITORY_ROOT_DIR), + runtime_config_root: root.join(RUNTIME_CONFIG_DIR), + root, + } + } + + pub fn repository_path(&self, alias: &RepositoryId) -> PathBuf { + self.repository_root.join(alias.as_str()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryRootConfig { + pub generated_repository: RepositoryId, + priority: HashMap, +} + +impl RepositoryRootConfig { + pub fn load(repository_root: impl AsRef) -> Result { + let path = repository_root.as_ref().join(REPOSITORY_ROOT_METADATA_FILE); + if !path.exists() { + return Ok(Self::default()); + } + let bytes = fs::read(&path).map_err(|source| RepositoryLoadError::ReadRootMetadata { + path: path.clone(), + source, + })?; + let raw: RawRepositoryRootConfig = serde_json::from_reader( + json_comments::StripComments::new(bytes.as_slice()), + ) + .map_err(|source| RepositoryLoadError::ParseRootMetadata { + path: path.clone(), + source, + })?; + if raw.version != REPOSITORY_ROOT_METADATA_VERSION { + return Err(RepositoryLoadError::UnsupportedRootMetadataVersion { + path, + found: raw.version, + expected: REPOSITORY_ROOT_METADATA_VERSION, + }); + } + let priority = raw + .priority + .into_iter() + .map(|(alias, value)| (alias, RepositoryPriority::new(value))) + .collect(); + Ok(Self { + generated_repository: RepositoryId::new( + raw.generated_repository + .unwrap_or_else(|| DEFAULT_GENERATED_REPOSITORY_ALIAS.to_owned()), + )?, + priority, + }) + } + + pub fn priority_for(&self, alias: &RepositoryId) -> RepositoryPriority { + self.priority + .get(alias.as_str()) + .copied() + .unwrap_or_else(|| default_repository_priority(alias.as_str())) + } +} + +impl Default for RepositoryRootConfig { + fn default() -> Self { + Self { + generated_repository: RepositoryId::new(DEFAULT_GENERATED_REPOSITORY_ALIAS) + .expect("default generated repository alias is valid"), + priority: HashMap::new(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryAliasEntry { + pub alias: RepositoryId, + pub path: PathBuf, + pub priority: RepositoryPriority, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryRootLayout { + pub root: PathBuf, + pub config: RepositoryRootConfig, + pub repositories: Vec, +} + +impl RepositoryRootLayout { + pub fn load(root: impl AsRef) -> Result { + let root = root.as_ref().to_path_buf(); + let config = RepositoryRootConfig::load(&root)?; + let mut repositories = Vec::new(); + match fs::read_dir(&root) { + Ok(entries) => { + for entry in entries { + let entry = + entry.map_err(|source| RepositoryLoadError::ReadRepositoryRoot { + path: root.clone(), + source, + })?; + let file_type = entry.file_type().map_err(|source| { + RepositoryLoadError::ReadRepositoryRoot { + path: root.clone(), + source, + } + })?; + if !file_type.is_dir() { + continue; + } + let alias_text = entry.file_name().to_string_lossy().into_owned(); + let alias = RepositoryId::new(alias_text)?; + let priority = config.priority_for(&alias); + repositories.push(RepositoryAliasEntry { + alias, + path: entry.path(), + priority, + }); + } + } + Err(source) if source.kind() == std::io::ErrorKind::NotFound => {} + Err(source) => { + return Err(RepositoryLoadError::ReadRepositoryRoot { + path: root.clone(), + source, + }) + } + } + repositories.sort_by(|left, right| { + right + .priority + .cmp(&left.priority) + .then_with(|| left.alias.as_str().cmp(right.alias.as_str())) + }); + Ok(Self { + root, + config, + repositories, + }) + } +} + +pub fn default_repository_priority(alias: &str) -> RepositoryPriority { + match alias { + LOCAL_REPOSITORY_ALIAS => RepositoryPriority::LOCAL, + DEFAULT_GENERATED_REPOSITORY_ALIAS => RepositoryPriority::GENERATED_FALLBACK, + _ => RepositoryPriority::DEFAULT, + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum GeneratedRepositoryTarget { + CreateDefault { alias: RepositoryId, path: PathBuf }, + Existing { alias: RepositoryId, path: PathBuf }, +} + +pub fn generated_repository_target( + data_dir: impl AsRef, +) -> Result { + let layout = GetterDataDirLayout::new(data_dir); + let config = RepositoryRootConfig::load(&layout.repository_root)?; + let path = layout.repository_path(&config.generated_repository); + if config.generated_repository.as_str() == DEFAULT_GENERATED_REPOSITORY_ALIAS { + Ok(GeneratedRepositoryTarget::CreateDefault { + alias: config.generated_repository, + path, + }) + } else if path.is_dir() { + Ok(GeneratedRepositoryTarget::Existing { + alias: config.generated_repository, + path, + }) + } else { + Err(RepositoryLoadError::MissingGeneratedRepository { + alias: config.generated_repository, + path, + }) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct PackageFile { pub id: PackageId, @@ -39,8 +242,43 @@ pub struct RepositoryPackageCacheKey { pub package_file_hash: String, } +#[derive(Debug, Deserialize)] +struct RawRepositoryRootConfig { + version: u32, + #[serde(default)] + generated_repository: Option, + #[serde(default)] + priority: HashMap, +} + #[derive(Debug, thiserror::Error)] pub enum RepositoryLoadError { + #[error("failed to read repository root metadata at {path}: {source}")] + ReadRootMetadata { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("failed to parse repository root metadata at {path}: {source}")] + ParseRootMetadata { + path: PathBuf, + #[source] + source: serde_json::Error, + }, + #[error("unsupported repository root metadata version {found} at {path}; expected {expected}")] + UnsupportedRootMetadataVersion { + path: PathBuf, + found: u32, + expected: u32, + }, + #[error("failed to read repository root {path}: {source}")] + ReadRepositoryRoot { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("configured generated repository '{alias}' does not exist at {path}")] + MissingGeneratedRepository { alias: RepositoryId, path: PathBuf }, #[error("failed to read repo.toml at {path}: {source}")] ReadRepoToml { path: PathBuf, @@ -280,6 +518,154 @@ mod tests { use super::*; use std::io::Write; + #[test] + fn data_dir_layout_uses_repo_and_rc_roots() { + let layout = GetterDataDirLayout::new("/tmp/ua-getter"); + + assert_eq!(layout.main_db, PathBuf::from("/tmp/ua-getter/main.db")); + assert_eq!(layout.cache_db, PathBuf::from("/tmp/ua-getter/cache.db")); + assert_eq!(layout.repository_root, PathBuf::from("/tmp/ua-getter/repo")); + assert_eq!( + layout.runtime_config_root, + PathBuf::from("/tmp/ua-getter/rc") + ); + } + + #[test] + fn missing_root_metadata_uses_default_priorities_and_generated_target() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path().join("repo"); + fs::create_dir_all(root.join("community")).unwrap(); + fs::create_dir_all(root.join("local")).unwrap(); + fs::create_dir_all(root.join("autogen")).unwrap(); + + let layout = RepositoryRootLayout::load(&root).unwrap(); + + assert_eq!( + layout.config.generated_repository.as_str(), + DEFAULT_GENERATED_REPOSITORY_ALIAS + ); + assert_eq!( + layout + .repositories + .iter() + .map(|repo| (repo.alias.as_str().to_owned(), repo.priority.value())) + .collect::>(), + vec![ + ("local".to_owned(), 100), + ("community".to_owned(), 0), + ("autogen".to_owned(), -1), + ] + ); + } + + #[test] + fn root_metadata_priority_map_is_lookup_only_for_existing_aliases() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path().join("repo"); + fs::create_dir_all(root.join("official")).unwrap(); + fs::write( + root.join(REPOSITORY_ROOT_METADATA_FILE), + r#"{ + "version": 1, + // This entry must not create or sort a missing repository. + "generated_repository": "autogen", + "priority": { + "missing": 500, + "not an alias": 400, + "official": 7 + } +} +"#, + ) + .unwrap(); + + let layout = RepositoryRootLayout::load(&root).unwrap(); + + assert_eq!(layout.repositories.len(), 1); + assert_eq!(layout.repositories[0].alias.as_str(), "official"); + assert_eq!(layout.repositories[0].priority.value(), 7); + assert_eq!( + layout.config.generated_repository.as_str(), + DEFAULT_GENERATED_REPOSITORY_ALIAS + ); + } + + #[test] + fn malformed_root_metadata_is_an_error_instead_of_fallback() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path().join("repo"); + fs::create_dir_all(&root).unwrap(); + fs::write(root.join(REPOSITORY_ROOT_METADATA_FILE), "{not-json").unwrap(); + + assert!(matches!( + RepositoryRootLayout::load(&root), + Err(RepositoryLoadError::ParseRootMetadata { .. }) + )); + } + + #[test] + fn generated_repository_target_creates_only_default_alias() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + + let target = generated_repository_target(data_dir).unwrap(); + + assert!(matches!( + target, + GeneratedRepositoryTarget::CreateDefault { ref alias, ref path } + if alias.as_str() == DEFAULT_GENERATED_REPOSITORY_ALIAS + && path == &data_dir.join("repo").join(DEFAULT_GENERATED_REPOSITORY_ALIAS) + )); + } + + #[test] + fn generated_repository_target_rejects_missing_custom_alias() { + let temp = tempfile::tempdir().unwrap(); + let repo_root = temp.path().join("repo"); + fs::create_dir_all(&repo_root).unwrap(); + fs::write( + repo_root.join(REPOSITORY_ROOT_METADATA_FILE), + r#"{ + "version": 1, + "generated_repository": "generated", + "priority": {} +} +"#, + ) + .unwrap(); + + assert!(matches!( + generated_repository_target(temp.path()), + Err(RepositoryLoadError::MissingGeneratedRepository { ref alias, .. }) + if alias.as_str() == "generated" + )); + } + + #[test] + fn generated_repository_target_accepts_existing_custom_alias() { + let temp = tempfile::tempdir().unwrap(); + let repo_root = temp.path().join("repo"); + fs::create_dir_all(repo_root.join("generated")).unwrap(); + fs::write( + repo_root.join(REPOSITORY_ROOT_METADATA_FILE), + r#"{ + "version": 1, + "generated_repository": "generated" +} +"#, + ) + .unwrap(); + + let target = generated_repository_target(temp.path()).unwrap(); + + assert!(matches!( + target, + GeneratedRepositoryTarget::Existing { ref alias, ref path } + if alias.as_str() == "generated" && path == &repo_root.join("generated") + )); + } + #[test] fn derives_package_id_from_lua_path() { let root = PathBuf::from("repo/packages"); @@ -362,7 +748,7 @@ api_version = "getter.repo.v1" #[test] fn highest_priority_selects_larger_number() { let priorities = [ - RepositoryPriority::LOCAL_AUTOGEN, + RepositoryPriority::GENERATED_FALLBACK, RepositoryPriority::DEFAULT, RepositoryPriority::LOCAL, ]; diff --git a/crates/getter-operations/src/autogen.rs b/crates/getter-operations/src/autogen.rs index caab4aa..faa06a9 100644 --- a/crates/getter-operations/src/autogen.rs +++ b/crates/getter-operations/src/autogen.rs @@ -1,18 +1,20 @@ //! Getter-owned installed-inventory autogen operations. //! //! The CLI and native bridge both call this module so there is one implementation -//! of `local_autogen` preview/apply semantics. Platform layers provide installed +//! of installed-autogen preview/apply semantics. Platform layers provide installed //! inventory facts; this module decides generated package ids, repository //! coverage, file writes, manifest updates, preservation behavior, and tracked //! state updates. use getter_core::autogen::{ - content_hash, local_autogen_repo_toml, local_repo_toml, plan_local_autogen, AutogenManifest, - AutogenManifestEntry, AutogenPlan, AutogenSkipReason, InstalledInventory, - LOCAL_AUTOGEN_REPOSITORY_ID, LOCAL_AUTOGEN_REPOSITORY_NAME, LOCAL_REPOSITORY_ID, - LOCAL_REPOSITORY_NAME, + content_hash, local_repo_toml, plan_installed_autogen, AutogenManifest, AutogenManifestEntry, + AutogenPlan, AutogenSkipReason, InstalledInventory, DEFAULT_AUTOGEN_REPOSITORY_ID, + DEFAULT_AUTOGEN_REPOSITORY_NAME, LOCAL_REPOSITORY_ID, LOCAL_REPOSITORY_NAME, +}; +use getter_core::repository::{ + generated_repository_target, GeneratedRepositoryTarget, GetterDataDirLayout, RepositoryLayout, + RepositoryLoadError, RepositoryMetadata, RepositoryRootConfig, REPO_API_VERSION_V1, }; -use getter_core::repository::{RepositoryLayout, RepositoryMetadata, REPO_API_VERSION_V1}; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; use getter_storage::{MainDb, StorageError, StoredRepository}; use serde_json::{json, Value}; @@ -34,28 +36,55 @@ pub enum AutogenOperationError { Storage(#[from] StorageError), #[error("repository error: {0}")] Repository(String), + #[error("configured generated repository '{alias}' does not exist at {path}")] + MissingGeneratedRepository { alias: RepositoryId, path: PathBuf }, #[error("autogen error: {0}")] Autogen(String), } +impl From for AutogenOperationError { + fn from(value: RepositoryLoadError) -> Self { + match value { + RepositoryLoadError::MissingGeneratedRepository { alias, path } => { + Self::MissingGeneratedRepository { alias, path } + } + other => Self::Repository(other.to_string()), + } + } +} + pub type AutogenOperationResult = Result; -pub fn build_local_autogen_plan( +pub fn build_installed_autogen_plan( + data_dir: &Path, db: &MainDb, inventory: &InstalledInventory, ) -> AutogenOperationResult { - let covered = higher_priority_package_coverage(db)?; - plan_local_autogen(inventory, &covered) - .map_err(|source| AutogenOperationError::Autogen(source.to_string())) + let (target_alias, _target_path, target_priority) = generated_repository_config(data_dir)?; + let covered = higher_priority_package_coverage(db, &target_alias, target_priority)?; + let mut plan = plan_installed_autogen(inventory, &covered) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + plan.repository_id = target_alias.clone(); + plan.repository_name = if target_alias.as_str() == DEFAULT_AUTOGEN_REPOSITORY_ID { + DEFAULT_AUTOGEN_REPOSITORY_NAME.to_owned() + } else { + target_alias.to_string() + }; + plan.repository_priority = target_priority; + Ok(plan) } -pub fn installed_preview_json(data_dir: &Path, plan: &AutogenPlan) -> Value { +pub fn installed_preview_json( + data_dir: &Path, + plan: &AutogenPlan, +) -> AutogenOperationResult { + let (target_alias, target_path, _target_priority) = generated_repository_config(data_dir)?; let candidates: Vec = plan.candidates.iter().map(autogen_candidate_json).collect(); let skipped: Vec = plan.skipped.iter().map(autogen_skip_json).collect(); - json!({ + Ok(json!({ "operation": "installed.preview", - "target_repo_id": plan.repository_id.as_str(), - "target_repo_path": local_autogen_repo_path(data_dir), + "target_repo_id": target_alias.as_str(), + "target_repo_path": target_path, "summary": { "candidate_count": candidates.len(), "skipped_count": skipped.len(), @@ -65,7 +94,7 @@ pub fn installed_preview_json(data_dir: &Path, plan: &AutogenPlan) -> Value { "candidates": candidates, "skipped": skipped, "diagnostics": [], - }) + })) } pub fn cleanup_preview_json( @@ -73,11 +102,11 @@ pub fn cleanup_preview_json( db: &MainDb, inventory: &InstalledInventory, ) -> AutogenOperationResult { - let repo_path = local_autogen_repo_path(data_dir); + let (target_alias, repo_path, _target_priority) = generated_repository_config(data_dir)?; let Some(manifest) = read_autogen_manifest(&repo_path)? else { return Ok(json!({ "operation": "cleanup.preview", - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_id": target_alias.as_str(), "target_repo_path": repo_path, "summary": { "candidate_count": 0, "skipped_count": 0, "write_count": 0, "delete_count": 0 }, "candidates": [], @@ -85,7 +114,7 @@ pub fn cleanup_preview_json( "diagnostics": [], })); }; - let plan = build_local_autogen_plan(db, inventory)?; + let plan = build_installed_autogen_plan(data_dir, db, inventory)?; let installed_ids: BTreeSet = plan .candidates .iter() @@ -113,7 +142,7 @@ pub fn cleanup_preview_json( }); Ok(json!({ "operation": "cleanup.preview", - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_id": target_alias.as_str(), "target_repo_path": repo_path, "summary": { "candidate_count": candidates.len(), @@ -133,10 +162,17 @@ pub fn apply_installed_preview( preview: &Value, acceptance: &AutogenAcceptance, ) -> AutogenOperationResult { - let repo_path = local_autogen_repo_path(data_dir); - ensure_local_autogen_repository(data_dir, db)?; + let (target_alias, repo_path, target_priority) = generated_repository_config(data_dir)?; + if preview.get("target_repo_id").and_then(Value::as_str) != Some(target_alias.as_str()) { + return Err(AutogenOperationError::Autogen(format!( + "installed preview target_repo_id must be '{}'", + target_alias.as_str() + ))); + } + ensure_generated_repository(&repo_path, db, &target_alias, target_priority)?; let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); + let mut manifest = + read_autogen_manifest(&repo_path)?.unwrap_or_else(|| empty_autogen_manifest(&target_alias)); let mut applied = Vec::new(); let mut preserved = Vec::new(); @@ -203,10 +239,7 @@ pub fn apply_installed_preview( content_hash: expected_hash.to_owned(), }, ); - db.upsert_generated_tracked_package_preserving_user_state( - &package_id, - &RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), - )?; + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &target_alias)?; applied.push(json!({ "package_id": package_id.to_string(), "output_relative_path": relative_path, @@ -215,7 +248,7 @@ pub fn apply_installed_preview( write_autogen_manifest(&repo_path, &manifest)?; Ok(json!({ - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_id": target_alias.as_str(), "target_repo_path": repo_path, "applied_count": applied.len(), "applied": applied, @@ -229,17 +262,18 @@ pub fn apply_cleanup_preview( preview: &Value, acceptance: &AutogenAcceptance, ) -> AutogenOperationResult { - if preview.get("target_repo_id").and_then(Value::as_str) != Some(LOCAL_AUTOGEN_REPOSITORY_ID) { + let (target_alias, repo_path, _target_priority) = generated_repository_config(data_dir)?; + if preview.get("target_repo_id").and_then(Value::as_str) != Some(target_alias.as_str()) { return Err(AutogenOperationError::Autogen(format!( - "cleanup preview target_repo_id must be '{LOCAL_AUTOGEN_REPOSITORY_ID}'" + "cleanup preview target_repo_id must be '{}'", + target_alias.as_str() ))); } - let repo_path = local_autogen_repo_path(data_dir); let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut manifest = read_autogen_manifest(&repo_path)?.unwrap_or_else(empty_autogen_manifest); + let mut manifest = + read_autogen_manifest(&repo_path)?.unwrap_or_else(|| empty_autogen_manifest(&target_alias)); let mut deleted = Vec::new(); let mut preserved = Vec::new(); - let local_autogen_id = RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"); for candidate in accepted { let package_id = preview_package_id(candidate)?; let relative_path = preview_relative_path(candidate)?; @@ -253,14 +287,14 @@ pub fn apply_cleanup_preview( })?; let manifest_entry = manifest.package(&package_id).ok_or_else(|| { AutogenOperationError::Autogen(format!( - "cleanup preview candidate {package_id} is not managed by local_autogen manifest" + "cleanup preview candidate {package_id} is not managed by autogen manifest" )) })?; if manifest_entry.relative_path != relative_path || manifest_entry.content_hash != expected_hash { return Err(AutogenOperationError::Autogen(format!( - "cleanup preview candidate {package_id} does not match local_autogen manifest" + "cleanup preview candidate {package_id} does not match autogen manifest" ))); } let target = safe_join(&repo_path, &relative_path)?; @@ -289,7 +323,7 @@ pub fn apply_cleanup_preview( manifest .packages .retain(|entry| entry.package_id != package_id); - db.delete_generated_tracked_package(&package_id, &local_autogen_id)?; + db.delete_generated_tracked_package(&package_id, &target_alias)?; deleted.push(json!({ "package_id": package_id.to_string(), "output_relative_path": relative_path, @@ -297,7 +331,7 @@ pub fn apply_cleanup_preview( } write_autogen_manifest(&repo_path, &manifest)?; Ok(json!({ - "target_repo_id": LOCAL_AUTOGEN_REPOSITORY_ID, + "target_repo_id": target_alias.as_str(), "target_repo_path": repo_path, "deleted_count": deleted.len(), "deleted": deleted, @@ -322,21 +356,36 @@ pub fn unwrap_preview_payload( Ok(payload) } -pub fn local_autogen_repo_path(data_dir: &Path) -> PathBuf { - data_dir - .join("repositories") - .join(LOCAL_AUTOGEN_REPOSITORY_ID) +pub fn default_autogen_repo_path(data_dir: &Path) -> PathBuf { + GetterDataDirLayout::new(data_dir) + .repository_path(&RepositoryId::new(DEFAULT_AUTOGEN_REPOSITORY_ID).expect("valid id")) +} + +fn generated_repository_config( + data_dir: &Path, +) -> AutogenOperationResult<(RepositoryId, PathBuf, RepositoryPriority)> { + let layout = GetterDataDirLayout::new(data_dir); + let config = RepositoryRootConfig::load(&layout.repository_root)?; + let target = generated_repository_target(data_dir)?; + let (alias, path) = match target { + GeneratedRepositoryTarget::CreateDefault { alias, path } + | GeneratedRepositoryTarget::Existing { alias, path } => (alias, path), + }; + let priority = config.priority_for(&alias); + Ok((alias, path, priority)) } fn higher_priority_package_coverage( db: &MainDb, + target_alias: &RepositoryId, + target_priority: RepositoryPriority, ) -> AutogenOperationResult> { let mut covered = HashMap::new(); for repo in db.repositories()? { - if repo.id.as_str() == LOCAL_AUTOGEN_REPOSITORY_ID { + if &repo.id == target_alias { continue; } - if repo.priority <= RepositoryPriority::LOCAL_AUTOGEN { + if repo.priority <= target_priority { continue; } let Some(path) = repo.path.as_ref() else { @@ -431,23 +480,41 @@ fn preview_relative_path(candidate: &Value) -> AutogenOperationResult { }) } -fn ensure_local_autogen_repository( - data_dir: &Path, +fn ensure_generated_repository( + repo_path: &Path, db: &MainDb, -) -> AutogenOperationResult { - let repo_path = local_autogen_repo_path(data_dir); - ensure_repository_layout(&repo_path, &local_autogen_repo_toml())?; + alias: &RepositoryId, + priority: RepositoryPriority, +) -> AutogenOperationResult<()> { + ensure_repository_layout(repo_path, &generated_repo_toml(alias, priority))?; db.upsert_repository( &RepositoryMetadata { - id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), - name: LOCAL_AUTOGEN_REPOSITORY_NAME.to_owned(), - priority: RepositoryPriority::LOCAL_AUTOGEN, + id: alias.clone(), + name: generated_repository_name(alias), + priority, api_version: REPO_API_VERSION_V1.to_owned(), }, - Some(&repo_path), + Some(repo_path), None, )?; - Ok(repo_path) + Ok(()) +} + +fn generated_repo_toml(alias: &RepositoryId, priority: RepositoryPriority) -> String { + format!( + "id = \"{}\"\nname = \"{}\"\npriority = {}\napi_version = \"{REPO_API_VERSION_V1}\"\n", + alias.as_str(), + generated_repository_name(alias), + priority.value() + ) +} + +fn generated_repository_name(alias: &RepositoryId) -> String { + if alias.as_str() == DEFAULT_AUTOGEN_REPOSITORY_ID { + DEFAULT_AUTOGEN_REPOSITORY_NAME.to_owned() + } else { + alias.to_string() + } } fn ensure_local_repository(data_dir: &Path, db: &MainDb) -> AutogenOperationResult { @@ -458,7 +525,8 @@ fn ensure_local_repository(data_dir: &Path, db: &MainDb) -> AutogenOperationResu return Ok(repo_path); } - let repo_path = data_dir.join("repositories").join(LOCAL_REPOSITORY_ID); + let repo_path = GetterDataDirLayout::new(data_dir) + .repository_path(&RepositoryId::new(LOCAL_REPOSITORY_ID).expect("valid id")); ensure_repository_layout(&repo_path, &local_repo_toml())?; db.upsert_repository( &RepositoryMetadata { @@ -601,10 +669,10 @@ fn write_autogen_manifest( }) } -fn empty_autogen_manifest() -> AutogenManifest { +fn empty_autogen_manifest(repository_id: &RepositoryId) -> AutogenManifest { AutogenManifest { version: getter_core::autogen::AUTOGEN_MANIFEST_VERSION, - repository_id: RepositoryId::new(LOCAL_AUTOGEN_REPOSITORY_ID).expect("valid id"), + repository_id: repository_id.clone(), packages: Vec::new(), } } @@ -635,3 +703,109 @@ fn safe_join(root: &Path, relative: &Path) -> AutogenOperationResult { } Ok(root.join(relative)) } + +#[cfg(test)] +mod tests { + use super::*; + use getter_storage::MainDb; + + fn test_inventory() -> InstalledInventory { + InstalledInventory::new(vec![ + getter_core::autogen::InstalledInventoryItem::AndroidPackage { + package_name: "com.example.autogen".to_owned(), + label: Some("Example Autogen".to_owned()), + version_name: None, + version_code: None, + }, + ]) + } + + #[test] + fn preview_uses_default_generated_repository_under_repo_root() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + + assert_eq!(preview["target_repo_id"], "autogen"); + assert_eq!( + preview["target_repo_path"].as_str(), + data_dir.join("repo/autogen").to_str() + ); + assert!(!data_dir.join("repo/autogen").exists()); + } + + #[test] + fn apply_creates_default_generated_repository_and_registers_it() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + + let result = + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll) + .unwrap(); + + assert_eq!(result["target_repo_id"], "autogen"); + assert!(data_dir.join("repo/autogen/repo.toml").is_file()); + assert!(data_dir + .join("repo/autogen/packages/android/com.example.autogen.lua") + .is_file()); + let repos = db.repositories().unwrap(); + assert_eq!(repos[0].id.as_str(), "autogen"); + assert_eq!(repos[0].priority.value(), -1); + } + + #[test] + fn custom_generated_repository_must_already_exist() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + fs::create_dir_all(data_dir.join("repo")).unwrap(); + fs::write( + data_dir.join("repo/metadata.jsonc"), + r#"{ "version": 1, "generated_repository": "generated" }"#, + ) + .unwrap(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + + let error = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap_err(); + + assert!(matches!( + error, + AutogenOperationError::MissingGeneratedRepository { ref alias, .. } + if alias.as_str() == "generated" + )); + } + + #[test] + fn custom_generated_repository_uses_configured_priority() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + fs::create_dir_all(data_dir.join("repo/generated")).unwrap(); + fs::write( + data_dir.join("repo/metadata.jsonc"), + r#"{ + "version": 1, + "generated_repository": "generated", + "priority": { "generated": -5 } +}"#, + ) + .unwrap(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + let result = + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll) + .unwrap(); + + assert_eq!(preview["target_repo_id"], "generated"); + assert_eq!(result["target_repo_id"], "generated"); + let repos = db.repositories().unwrap(); + assert_eq!(repos[0].id.as_str(), "generated"); + assert_eq!(repos[0].priority.value(), -5); + } +} diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index 2127cbc..e8d7b02 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -1234,7 +1234,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); fn generated_tracking_preserves_existing_user_state_on_conflict() { let db = MainDb::open_in_memory().unwrap(); let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); - let local_autogen = RepositoryId::new("local_autogen").unwrap(); + let autogen = RepositoryId::new("autogen").unwrap(); db.upsert_tracked_package(&TrackedPackageUpsert { package_id: package_id.clone(), enabled: false, @@ -1245,7 +1245,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); }) .unwrap(); - db.upsert_generated_tracked_package_preserving_user_state(&package_id, &local_autogen) + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &autogen) .unwrap(); let packages = db.tracked_packages().unwrap(); @@ -1264,7 +1264,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); fn generated_tracking_fills_unresolved_tracking_metadata() { let db = MainDb::open_in_memory().unwrap(); let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); - let local_autogen = insert_local_autogen_repo(&db); + let autogen = insert_autogen_repo(&db); db.upsert_tracked_package(&TrackedPackageUpsert { package_id: package_id.clone(), enabled: false, @@ -1275,7 +1275,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); }) .unwrap(); - db.upsert_generated_tracked_package_preserving_user_state(&package_id, &local_autogen) + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &autogen) .unwrap(); let packages = db.tracked_packages().unwrap(); @@ -1283,7 +1283,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); assert!(!packages[0].enabled); assert!(packages[0].favorite); assert_eq!(packages[0].pin_version.as_deref(), Some("9.9.9")); - assert_eq!(packages[0].repository_id.as_ref(), Some(&local_autogen)); + assert_eq!(packages[0].repository_id.as_ref(), Some(&autogen)); assert_eq!( packages[0].package_resolution, StoredPackageResolution::GenerateLocalPackage @@ -1294,7 +1294,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); fn generated_tracking_delete_is_guarded_by_repo_and_resolution() { let db = MainDb::open_in_memory().unwrap(); let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); - let local_autogen = insert_local_autogen_repo(&db); + let autogen = insert_autogen_repo(&db); db.upsert_tracked_package(&TrackedPackageUpsert { package_id: package_id.clone(), enabled: true, @@ -1306,7 +1306,7 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); .unwrap(); assert!(!db - .delete_generated_tracked_package(&package_id, &local_autogen) + .delete_generated_tracked_package(&package_id, &autogen) .unwrap()); assert_eq!(db.tracked_packages().unwrap().len(), 1); @@ -1319,28 +1319,28 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); package_resolution: StoredPackageResolution::MissingPackageDefinition, }) .unwrap(); - db.upsert_generated_tracked_package_preserving_user_state(&package_id, &local_autogen) + db.upsert_generated_tracked_package_preserving_user_state(&package_id, &autogen) .unwrap(); assert!(db - .delete_generated_tracked_package(&package_id, &local_autogen) + .delete_generated_tracked_package(&package_id, &autogen) .unwrap()); assert!(db.tracked_packages().unwrap().is_empty()); } - fn insert_local_autogen_repo(db: &MainDb) -> RepositoryId { - let local_autogen = RepositoryId::new("local_autogen").unwrap(); + fn insert_autogen_repo(db: &MainDb) -> RepositoryId { + let autogen = RepositoryId::new("autogen").unwrap(); db.upsert_repository( &RepositoryMetadata { - id: local_autogen.clone(), - name: "Local Autogen".to_owned(), - priority: RepositoryPriority::LOCAL_AUTOGEN, + id: autogen.clone(), + name: "Autogen".to_owned(), + priority: RepositoryPriority::GENERATED_FALLBACK, api_version: REPO_API_VERSION_V1.to_owned(), }, None, None, ) .unwrap(); - local_autogen + autogen } #[test] From 3388b78dc891dc487bc406d71050e919ce6e4023 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Fri, 26 Jun 2026 22:39:51 +0800 Subject: [PATCH 29/71] feat(repository): discover package directories --- crates/getter-cli/tests/bdd_cli.rs | 22 + .../tests/features/cli/repo_validate.feature | 7 + crates/getter-core/src/diagnostics.rs | 86 +++- crates/getter-core/src/repository.rs | 402 +++++++++++++++++- 4 files changed, 498 insertions(+), 19 deletions(-) diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 073bb63..8a8e950 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -347,6 +347,28 @@ fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: Str create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); } +#[given(expr = "a package-directory repository {string} with package {string}")] +fn package_directory_repository(world: &mut CliWorld, repo_id: String, package_id: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let repo_path = temp.path().join(format!("repo-{repo_id}")); + let package_dir = repo_path.join(package_id.replace('/', std::path::MAIN_SEPARATOR_STR)); + fs::create_dir_all(&package_dir).expect("create package dir"); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + ) + .expect("write package metadata"); + fs::write( + package_dir.join("1.20.0.lua"), + "#!/bin/upa-lua v1\nreturn {}", + ) + .expect("write version Lua"); + + world.fixture_repo_id = Some(repo_id); + world.fixture_repo_path = Some(repo_path); + world.fixture_package_id = Some(package_id); +} + #[given(expr = "a fixture Lua repository {string} with package {string} named {string}")] fn fixture_lua_repository_named( world: &mut CliWorld, diff --git a/crates/getter-cli/tests/features/cli/repo_validate.feature b/crates/getter-cli/tests/features/cli/repo_validate.feature index 592d4bf..9c6d1a1 100644 --- a/crates/getter-cli/tests/features/cli/repo_validate.feature +++ b/crates/getter-cli/tests/features/cli/repo_validate.feature @@ -6,6 +6,13 @@ Feature: Getter CLI repository validation Then the command succeeds And the output reports a valid repository without network + Scenario: User validates a package-directory repository offline + Given an initialized getter data directory + And a package-directory repository "official" with package "android/app/org.fdroid.fdroid" + When I run getter repo validate for that repository + Then the command succeeds + And the output reports a valid repository without network + Scenario: User receives diagnostics for invalid Lua Given an initialized getter data directory And a fixture Lua repository "broken" with invalid Lua package "android/org.fdroid.fdroid" diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index d338055..817b310 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -4,7 +4,10 @@ //! describe what getter observed; Flutter should only render them. use crate::lua::{evaluate_package_file, LuaPackageError}; -use crate::repository::{package_cache_key, RepositoryLayout, RepositoryLoadError}; +use crate::repository::{ + package_cache_key, InvalidPackageDirectory, RepositoryLayout, RepositoryLoadError, + RepositoryPackageDirectoryLayout, +}; use crate::PackageId; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; @@ -62,6 +65,14 @@ impl RepositoryValidationReport { /// not perform provider/network checks. pub fn validate_repository_path(path: impl AsRef) -> RepositoryValidationReport { let root = path.as_ref(); + if root.join("repo.toml").is_file() { + validate_legacy_repository_path(root) + } else { + validate_package_directory_repository_path(root) + } +} + +fn validate_legacy_repository_path(root: &Path) -> RepositoryValidationReport { let layout = match RepositoryLayout::load(root) { Ok(layout) => layout, Err(error) => { @@ -85,6 +96,21 @@ pub fn validate_repository_path(path: impl AsRef) -> RepositoryValidationR RepositoryValidationReport::new(package_count, diagnostics) } +fn validate_package_directory_repository_path(root: &Path) -> RepositoryValidationReport { + let layout = match RepositoryPackageDirectoryLayout::load(root) { + Ok(layout) => layout, + Err(error) => { + return RepositoryValidationReport::new(0, vec![repository_load_diagnostic(error)]) + } + }; + let diagnostics = layout + .invalid_packages + .iter() + .map(invalid_package_directory_diagnostic) + .collect(); + RepositoryValidationReport::new(layout.packages.len(), diagnostics) +} + fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDiagnostic { let (code, path, message) = match error { RepositoryLoadError::ReadRootMetadata { path, source } => ( @@ -163,6 +189,18 @@ fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDi diagnostic(code, message, path, None, None) } +fn invalid_package_directory_diagnostic( + package: &InvalidPackageDirectory, +) -> PackageValidationDiagnostic { + diagnostic( + "package.metadata", + package.reason.clone(), + package.metadata_path.clone(), + None, + package.id.clone(), + ) +} + fn lua_diagnostic( error: LuaPackageError, fallback_package_id: Option, @@ -275,6 +313,52 @@ api_version = "getter.repo.v1" assert!(!report.network_required); } + #[test] + fn package_directory_repository_has_no_diagnostics() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + ) + .unwrap(); + fs::write( + package_dir.join("1.20.0.lua"), + "#!/bin/upa-lua v1\nreturn {}", + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + + assert!(report.valid, "{report:?}"); + assert_eq!(report.package_count, 1); + assert!(report.diagnostics.is_empty()); + assert!(!report.network_required); + } + + #[test] + fn package_directory_metadata_error_is_stable_package_diagnostic() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write(package_dir.join("metadata.jsonc"), "[]").unwrap(); + + let report = validate_repository_path(temp.path()); + + assert!(!report.valid); + assert_eq!(report.package_count, 0); + assert_eq!(report.diagnostics[0].code, "package.metadata"); + assert_eq!( + report.diagnostics[0] + .package_id + .as_ref() + .unwrap() + .to_string(), + "android/app/org.fdroid.fdroid" + ); + } + #[test] fn missing_directory_is_stable_repository_diagnostic() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index 95b7fe8..fd16ef5 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -13,6 +13,10 @@ pub const REPOSITORY_ROOT_DIR: &str = "repo"; pub const RUNTIME_CONFIG_DIR: &str = "rc"; pub const REPOSITORY_ROOT_METADATA_FILE: &str = "metadata.jsonc"; pub const REPOSITORY_ROOT_METADATA_VERSION: u32 = 1; +pub const REPOSITORY_SELF_METADATA_DIR: &str = ".metadata"; +pub const REPOSITORY_LUACLASS_DIR: &str = "luaclass"; +pub const PACKAGE_METADATA_FILE: &str = "metadata.jsonc"; +pub const LUA_SCRIPT_EXTENSION: &str = "lua"; pub const LOCAL_REPOSITORY_ALIAS: &str = "local"; pub const DEFAULT_GENERATED_REPOSITORY_ALIAS: &str = "autogen"; @@ -234,6 +238,36 @@ pub struct PackageFile { pub path: PathBuf, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryPackageDirectoryLayout { + pub root: PathBuf, + pub packages: Vec, + pub invalid_packages: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PackageDirectory { + pub id: PackageId, + pub path: PathBuf, + pub metadata_path: PathBuf, + pub version_scripts: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PackageVersionScript { + pub version: String, + pub file_name: String, + pub path: PathBuf, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InvalidPackageDirectory { + pub id: Option, + pub path: PathBuf, + pub metadata_path: PathBuf, + pub reason: String, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct RepositoryPackageCacheKey { pub repository_id: RepositoryId, @@ -322,6 +356,26 @@ pub enum RepositoryLoadError { }, } +impl RepositoryPackageDirectoryLayout { + pub fn load(root: impl AsRef) -> Result { + let root = root.as_ref().to_path_buf(); + let mut packages = Vec::new(); + let mut invalid_packages = Vec::new(); + collect_package_directories(&root, &root, &mut packages, &mut invalid_packages)?; + packages.sort_by_key(|package| package.id.to_string()); + invalid_packages.sort_by_key(|package| package.path.clone()); + Ok(Self { + root, + packages, + invalid_packages, + }) + } + + pub fn package(&self, id: &PackageId) -> Option<&PackageDirectory> { + self.packages.iter().find(|package| &package.id == id) + } +} + impl RepositoryLayout { pub fn load(root: impl AsRef) -> Result { let root = root.as_ref().to_path_buf(); @@ -403,25 +457,16 @@ fn collect_package_files( current: &Path, out: &mut Vec, ) -> Result<(), RepositoryLoadError> { - for entry in fs::read_dir(current).map_err(|source| RepositoryLoadError::ReadPackagesDir { - path: current.to_path_buf(), - source, - })? { - let entry = entry.map_err(|source| RepositoryLoadError::ReadPackagesDir { - path: current.to_path_buf(), - source, - })?; + for entry in read_dir_entries(current)? { let path = entry.path(); - let file_type = - entry - .file_type() - .map_err(|source| RepositoryLoadError::ReadPackagesDir { - path: current.to_path_buf(), - source, - })?; + let file_type = entry_file_type(&entry, current)?; if file_type.is_dir() { collect_package_files(packages_root, &path, out)?; - } else if file_type.is_file() && path.extension().is_some_and(|ext| ext == "lua") { + } else if file_type.is_file() + && path + .extension() + .is_some_and(|ext| ext == LUA_SCRIPT_EXTENSION) + { let id = package_id_from_path(packages_root, &path)?; out.push(PackageFile { id, path }); } @@ -429,6 +474,171 @@ fn collect_package_files( Ok(()) } +fn collect_package_directories( + repository_root: &Path, + current: &Path, + packages: &mut Vec, + invalid_packages: &mut Vec, +) -> Result<(), RepositoryLoadError> { + if current == repository_root.join(REPOSITORY_SELF_METADATA_DIR) + || current == repository_root.join(REPOSITORY_LUACLASS_DIR) + { + return Ok(()); + } + + let metadata_path = current.join(PACKAGE_METADATA_FILE); + if metadata_path.is_file() { + collect_package_boundary( + repository_root, + current, + &metadata_path, + packages, + invalid_packages, + )?; + return Ok(()); + } + + for entry in read_dir_entries(current)? { + let file_type = entry_file_type(&entry, current)?; + if file_type.is_dir() { + collect_package_directories( + repository_root, + &entry.path(), + packages, + invalid_packages, + )?; + } + } + Ok(()) +} + +fn collect_package_boundary( + repository_root: &Path, + package_dir: &Path, + metadata_path: &Path, + packages: &mut Vec, + invalid_packages: &mut Vec, +) -> Result<(), RepositoryLoadError> { + let id = package_id_from_package_dir(repository_root, package_dir); + if let Err(error) = parse_package_metadata(metadata_path) { + invalid_packages.push(InvalidPackageDirectory { + id: id.ok(), + path: package_dir.to_path_buf(), + metadata_path: metadata_path.to_path_buf(), + reason: error, + }); + return Ok(()); + } + let id = match id { + Ok(id) => id, + Err(error) => { + invalid_packages.push(InvalidPackageDirectory { + id: None, + path: package_dir.to_path_buf(), + metadata_path: metadata_path.to_path_buf(), + reason: error.to_string(), + }); + return Ok(()); + } + }; + packages.push(PackageDirectory { + id, + path: package_dir.to_path_buf(), + metadata_path: metadata_path.to_path_buf(), + version_scripts: discover_version_scripts(package_dir)?, + }); + Ok(()) +} + +fn parse_package_metadata(metadata_path: &Path) -> Result<(), String> { + let bytes = fs::read(metadata_path) + .map_err(|source| format!("failed to read package metadata: {source}"))?; + let value = serde_json::from_reader::<_, serde_json::Value>(json_comments::StripComments::new( + bytes.as_slice(), + )) + .map_err(|source| format!("failed to parse package metadata: {source}"))?; + if value.is_object() { + Ok(()) + } else { + Err("package metadata must be a JSON object".to_owned()) + } +} + +fn discover_version_scripts( + package_dir: &Path, +) -> Result, RepositoryLoadError> { + let mut scripts = Vec::new(); + for entry in read_dir_entries(package_dir)? { + let path = entry.path(); + let file_type = entry_file_type(&entry, package_dir)?; + if !file_type.is_file() { + continue; + } + let Some(file_name) = path.file_name().and_then(|name| name.to_str()) else { + continue; + }; + if file_name.starts_with('.') { + continue; + } + if path + .extension() + .is_none_or(|extension| extension != LUA_SCRIPT_EXTENSION) + { + continue; + } + let Some(version) = path.file_stem().and_then(|stem| stem.to_str()) else { + continue; + }; + scripts.push(PackageVersionScript { + version: version.to_owned(), + file_name: file_name.to_owned(), + path, + }); + } + scripts.sort_by_key(|script| script.file_name.clone()); + Ok(scripts) +} + +fn read_dir_entries(path: &Path) -> Result, RepositoryLoadError> { + fs::read_dir(path) + .map_err(|source| RepositoryLoadError::ReadPackagesDir { + path: path.to_path_buf(), + source, + })? + .collect::, _>>() + .map_err(|source| RepositoryLoadError::ReadPackagesDir { + path: path.to_path_buf(), + source, + }) +} + +fn entry_file_type( + entry: &fs::DirEntry, + parent: &Path, +) -> Result { + entry + .file_type() + .map_err(|source| RepositoryLoadError::ReadPackagesDir { + path: parent.to_path_buf(), + source, + }) +} + +pub fn package_id_from_package_dir( + repository_root: impl AsRef, + package_dir: impl AsRef, +) -> Result { + let repository_root = repository_root.as_ref(); + let package_dir = package_dir.as_ref(); + let relative = package_dir.strip_prefix(repository_root).map_err(|_| { + RepositoryLoadError::InvalidPackagePath { + path: package_dir.to_path_buf(), + reason: format!("path is not under {}", repository_root.display()), + } + })?; + package_id_from_relative_path(package_dir, relative) +} + pub fn package_id_from_path( packages_root: impl AsRef, path: impl AsRef, @@ -447,8 +657,14 @@ pub fn package_id_from_path( reason: "package file must have .lua extension".to_owned(), }); } - let without_extension = relative.with_extension(""); - let mut parts = without_extension.components(); + package_id_from_relative_path(path, &relative.with_extension("")) +} + +fn package_id_from_relative_path( + path: &Path, + relative: &Path, +) -> Result { + let mut parts = relative.components(); let kind = parts .next() .ok_or_else(|| RepositoryLoadError::InvalidPackagePath { @@ -674,6 +890,156 @@ mod tests { assert_eq!(id.to_string(), "android/org.fdroid.fdroid"); } + #[test] + fn derives_package_id_from_package_directory() { + let root = PathBuf::from("repo/official"); + let id = package_id_from_package_dir(&root, "repo/official/android/f-droid/magisk/hello") + .unwrap(); + assert_eq!(id.to_string(), "android/f-droid/magisk/hello"); + } + + #[test] + fn discovers_package_directories_and_direct_child_version_scripts() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + let package_dir = root.join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(package_dir.join("nested")).unwrap(); + fs::write( + package_dir.join(PACKAGE_METADATA_FILE), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + fs::write(package_dir.join("1.2.3.lua"), "return {}").unwrap(); + fs::write(package_dir.join("9999.lua"), "return {}").unwrap(); + fs::write(package_dir.join(".disabled.lua"), "return {}").unwrap(); + fs::write(package_dir.join("nested/2.0.lua"), "return {}").unwrap(); + + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + + assert_eq!(layout.invalid_packages, Vec::new()); + assert_eq!(layout.packages.len(), 1); + let package = &layout.packages[0]; + assert_eq!(package.id.to_string(), "android/app/org.fdroid.fdroid"); + assert_eq!(package.path, package_dir); + assert_eq!( + package.metadata_path, + package.path.join(PACKAGE_METADATA_FILE) + ); + assert_eq!( + package + .version_scripts + .iter() + .map(|script| (script.version.as_str(), script.file_name.as_str())) + .collect::>(), + vec![("1.2.3", "1.2.3.lua"), ("9999", "9999.lua")] + ); + } + + #[test] + fn package_metadata_boundary_stops_nested_discovery_even_when_invalid() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + let package_dir = root.join("android/app/broken"); + fs::create_dir_all(package_dir.join("nested/android/app/hidden")).unwrap(); + fs::write(package_dir.join(PACKAGE_METADATA_FILE), "{not-json").unwrap(); + fs::write( + package_dir.join("nested/android/app/hidden/metadata.jsonc"), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + + assert!(layout.packages.is_empty()); + assert_eq!(layout.invalid_packages.len(), 1); + assert_eq!( + layout.invalid_packages[0] + .id + .as_ref() + .map(ToString::to_string), + Some("android/app/broken".to_owned()) + ); + } + + #[test] + fn repository_reserved_roots_are_not_package_discovery_roots() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::create_dir_all(root.join(".metadata/android/app/hidden")).unwrap(); + fs::write( + root.join(".metadata/android/app/hidden/metadata.jsonc"), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + fs::create_dir_all(root.join("luaclass/android/app/hidden")).unwrap(); + fs::write( + root.join("luaclass/android/app/hidden/metadata.jsonc"), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + fs::create_dir_all(root.join("android/app/visible")).unwrap(); + fs::write( + root.join("android/app/visible/metadata.jsonc"), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + + assert_eq!(layout.packages.len(), 1); + assert_eq!(layout.packages[0].id.to_string(), "android/app/visible"); + } + + #[test] + fn autogen_record_alone_does_not_create_package_boundary() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::create_dir_all(root.join("android/app/generated")).unwrap(); + fs::write(root.join("android/app/generated/.autogen.jsonc"), "{}").unwrap(); + + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + + assert!(layout.packages.is_empty()); + assert!(layout.invalid_packages.is_empty()); + } + + #[test] + fn package_metadata_must_be_an_object() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::create_dir_all(root.join("android/app/not-object")).unwrap(); + fs::write(root.join("android/app/not-object/metadata.jsonc"), "[]").unwrap(); + + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + + assert!(layout.packages.is_empty()); + assert_eq!(layout.invalid_packages.len(), 1); + assert_eq!( + layout.invalid_packages[0].reason, + "package metadata must be a JSON object" + ); + } + + #[test] + fn invalid_package_path_is_reported_as_invalid_package() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + fs::create_dir_all(root.join("invalidkind/app/example")).unwrap(); + fs::write( + root.join("invalidkind/app/example/metadata.jsonc"), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + + assert!(layout.packages.is_empty()); + assert_eq!(layout.invalid_packages.len(), 1); + assert!(layout.invalid_packages[0] + .reason + .contains("unsupported package kind")); + } + #[test] fn loads_repository_layout_with_required_directories() { let temp = tempfile::tempdir().unwrap(); From 8e880e689457ae48aad3e73ccf7dc12acbaabb0e Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 00:23:38 +0800 Subject: [PATCH 30/71] feat(autogen): write installed packages as directories --- Cargo.lock | 67 ++ crates/getter-cli/src/lib.rs | 44 +- crates/getter-cli/tests/bdd_cli.rs | 130 ++- .../features/cli/autogen_installed.feature | 28 +- crates/getter-core/Cargo.toml | 1 + crates/getter-core/src/autogen.rs | 380 ++++---- crates/getter-operations/Cargo.toml | 1 + crates/getter-operations/src/autogen.rs | 840 +++++++++++------- 8 files changed, 942 insertions(+), 549 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23480e3..34622f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,6 +91,15 @@ version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bstr" version = "1.12.1" @@ -229,6 +238,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -254,6 +272,16 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "cucumber" version = "0.23.0" @@ -334,6 +362,16 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "either" version = "1.16.0" @@ -480,6 +518,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -540,6 +588,7 @@ dependencies = [ "mlua", "serde", "serde_json", + "sha2", "tempfile", "thiserror 1.0.69", "toml", @@ -568,6 +617,7 @@ dependencies = [ "getter-core", "getter-providers", "getter-storage", + "json_comments", "serde", "serde_json", "tempfile", @@ -1364,6 +1414,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "2.0.1" @@ -1625,6 +1686,12 @@ dependencies = [ "syn", ] +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + [[package]] name = "unicode-ident" version = "1.0.24" diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 020e1f9..9b03e76 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -8,7 +8,8 @@ use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_file; use getter_core::repository::{ - GetterDataDirLayout, RepositoryLayout, RepositoryMetadata, REPOSITORY_ROOT_METADATA_FILE, + default_repository_priority, GetterDataDirLayout, RepositoryLayout, RepositoryMetadata, + RepositoryPackageDirectoryLayout, REPOSITORY_ROOT_METADATA_FILE, REPO_API_VERSION_V1, }; use getter_core::runtime::{GetterRuntime, SealedActionPlan}; use getter_core::task::{ @@ -588,17 +589,7 @@ fn execute(invocation: CliInvocation) -> Result { } CliCommand::RepoAdd { id, path, priority } => { let db = open_main_db(&invocation.data_dir)?; - let layout = load_repository_layout(&path)?; - if layout.metadata.id != id { - return Err(CliError::Repository(format!( - "repo.toml id '{}' does not match requested id '{}'", - layout.metadata.id, id - ))); - } - let metadata = RepositoryMetadata { - priority: priority.unwrap_or(layout.metadata.priority), - ..layout.metadata.clone() - }; + let metadata = load_repository_metadata(&id, &path, priority)?; db.upsert_repository(&metadata, Some(&path), None)?; Ok(json!({ "repository": repository_metadata_json(&metadata, Some(&path), None) })) } @@ -938,6 +929,35 @@ fn load_repository_layout(path: &Path) -> Result { RepositoryLayout::load(path).map_err(|source| CliError::Repository(source.to_string())) } +fn load_repository_metadata( + id: &RepositoryId, + path: &Path, + priority: Option, +) -> Result { + if path.join("repo.toml").is_file() { + let layout = load_repository_layout(path)?; + if &layout.metadata.id != id { + return Err(CliError::Repository(format!( + "repo.toml id '{}' does not match requested id '{}'", + layout.metadata.id, id + ))); + } + return Ok(RepositoryMetadata { + priority: priority.unwrap_or(layout.metadata.priority), + ..layout.metadata + }); + } + + RepositoryPackageDirectoryLayout::load(path) + .map_err(|source| CliError::Repository(source.to_string()))?; + Ok(RepositoryMetadata { + id: id.clone(), + name: id.to_string(), + priority: priority.unwrap_or_else(|| default_repository_priority(id.as_str())), + api_version: REPO_API_VERSION_V1.to_owned(), + }) +} + fn list_repositories(db: &MainDb) -> Result, CliError> { Ok(db .repositories()? diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 8a8e950..e5e2787 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -180,7 +180,7 @@ fn tampered_autogen_cleanup_preview_for_package(world: &mut CliWorld, package_id "package_id": package_id, "action": "delete", "output_relative_path": package_relative_path(&package_id), - "content_hash": "fnv1a64:0000000000000000", + "content_hash": "sha512:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "reason": "not_in_installed_inventory", } ], @@ -897,7 +897,10 @@ fn command_fails_with_autogen_error(world: &mut CliWorld) { assert_eq!(output.status.code(), Some(1)); let json = parse_stdout(output); assert_eq!(json["ok"], false); - assert_eq!(json["command"], "autogen cleanup apply"); + assert!(matches!( + json["command"].as_str(), + Some("autogen cleanup apply" | "autogen installed apply") + )); assert_eq!(json["error"]["code"], "autogen.error"); world.json = Some(json); } @@ -1315,8 +1318,16 @@ fn save_autogen_preview_to_file(world: &mut CliWorld) { #[then(expr = "the autogen repository contains generated package {string}")] fn autogen_repository_contains_generated_package(world: &mut CliWorld, package_id: String) { let path = autogen_repo_path(world).join(package_relative_path(&package_id)); - assert!(path.is_file(), "generated package should exist: {path:?}"); - let content = fs::read_to_string(&path).expect("generated package readable"); + assert!( + path.is_dir(), + "generated package directory should exist: {path:?}" + ); + assert!(path.join("metadata.jsonc").is_file()); + assert!(path.join("Manifest").is_file()); + let version_script = path.join("9999.lua"); + assert!(version_script.is_file()); + assert!(path.join(".autogen.jsonc").is_file()); + let content = fs::read_to_string(version_script).expect("generated package readable"); assert!(content.contains("@generated by UpgradeAll getter autogen")); } @@ -1374,8 +1385,15 @@ fn autogen_cleanup_preview_contains_delete_candidate(world: &mut CliWorld, packa fn autogen_repository_does_not_contain_generated_package(world: &mut CliWorld, package_id: String) { let path = autogen_repo_path(world).join(package_relative_path(&package_id)); assert!( - !path.exists(), - "generated package should be deleted: {path:?}" + path.is_dir(), + "cleanup keeps generated package directory: {path:?}" + ); + assert_eq!( + fs::read_dir(&path) + .expect("generated package dir readable") + .count(), + 0, + "generated package directory should be empty after cleanup: {path:?}" ); } @@ -1398,32 +1416,30 @@ fn replace_generated_autogen_package_with_user_edited_content( package_id: String, ) { let path = autogen_repo_path(world).join(package_relative_path(&package_id)); - assert!(path.is_file(), "generated package should exist before edit"); + let version_script = path.join("9999.lua"); + assert!( + version_script.is_file(), + "generated package version script should exist before edit" + ); fs::write( - &path, - format!( - "-- user edited\nreturn package_def {{ id = {id:?}, name = \"Edited Autogen\" }}\n", - id = package_id - ), + &version_script, + "#!/bin/upa-lua v1\n-- user edited\nreturn package_version {}\n", ) .expect("overwrite generated package with user edit"); } -#[then(expr = "local repository contains preserved package {string}")] -fn local_repository_contains_preserved_package(world: &mut CliWorld, package_id: String) { +#[then("local repository has not been written")] +fn local_repository_has_not_been_written(world: &mut CliWorld) { let local_path = world .data_dir .as_ref() .expect("data dir exists") .join("repo") - .join("local") - .join(package_relative_path(&package_id)); + .join("local"); assert!( - local_path.is_file(), - "modified autogen file should be preserved into local: {local_path:?}" + !local_path.exists(), + "autogen ownership conflicts must not preserve files into local: {local_path:?}" ); - let content = fs::read_to_string(local_path).expect("preserved local file readable"); - assert!(content.contains("Edited Autogen")); } #[then("no partially usable imported state is created")] @@ -1608,12 +1624,7 @@ fn autogen_repo_path(world: &CliWorld) -> PathBuf { } fn package_relative_path(package_id: &str) -> PathBuf { - let (kind, name) = package_id - .split_once('/') - .expect("test package id has kind/name"); - PathBuf::from("packages") - .join(kind) - .join(format!("{name}.lua")) + package_id.split('/').collect() } fn create_fixture_lua_repository( @@ -1624,23 +1635,49 @@ fn create_fixture_lua_repository( ) { let temp = world.temp.as_ref().expect("tempdir exists"); let repo_path = temp.path().join(format!("repo-{repo_id}")); - let package_name_path = package_id - .strip_prefix("android/") - .expect("fixture package id should be android package id"); - fs::create_dir_all(repo_path.join("packages/android")).expect("create packages dir"); - fs::create_dir(repo_path.join("lib")).expect("create lib dir"); - fs::create_dir(repo_path.join("templates")).expect("create templates dir"); - fs::write( - repo_path.join("repo.toml"), - format!( - "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" - ), - ) - .expect("write repo.toml"); - fs::write( - repo_path.join(format!("packages/android/{package_name_path}.lua")), - format!( - r#" + if let Some(package_name_path) = package_id.strip_prefix("android/app/") { + let package_dir = repo_path.join(package_relative_path(&package_id)); + fs::create_dir_all(&package_dir).expect("create package dir"); + fs::write( + package_dir.join("metadata.jsonc"), + format!( + r#"{{ "type": "android:app", "display_name": {name:?}, "android": {{ "package_name": {package_name_path:?} }} }}"#, + name = package_name, + ), + ) + .expect("write package metadata"); + fs::write(package_dir.join("Manifest"), "").expect("write Manifest"); + fs::write( + package_dir.join("9999.lua"), + format!( + r#"#!/bin/upa-lua v1 +return package_version {{ + installed = {{ + {{ kind = "android_package", package_name = "{package_name_path}" }}, + }}, +}} +"# + ), + ) + .expect("write package Lua"); + } else { + let package_name_path = package_id + .strip_prefix("android/") + .expect("fixture package id should be android package id"); + fs::create_dir_all(repo_path.join("packages/android")).expect("create packages dir"); + fs::create_dir(repo_path.join("lib")).expect("create lib dir"); + fs::create_dir(repo_path.join("templates")).expect("create templates dir"); + fs::write( + repo_path.join("repo.toml"), + format!( + "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" + ), + ) + .expect("write repo.toml"); + fs::write( + repo_path.join(format!("packages/android/{package_name_path}.lua")), + format!( + r#" return package_def {{ id = "{package_id}", name = "{package_name}", @@ -1649,9 +1686,10 @@ return package_def {{ }}, }} "# - ), - ) - .expect("write package Lua"); + ), + ) + .expect("write package Lua"); + } world.fixture_repo_id = Some(repo_id); world.fixture_repo_path = Some(repo_path); diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index 5bbb8c9..695dc78 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -6,10 +6,10 @@ Feature: Installed app autogen When I run getter autogen installed preview for that inventory Then the command succeeds And the output is valid JSON - And the autogen preview contains candidate "android/com.example.autogen" + And the autogen preview contains candidate "android/app/com.example.autogen" And the autogen repository has not been written - Scenario: User applies installed app autogen and evaluates the generated fallback package + Scenario: User applies installed app autogen and validates the generated fallback package directory Given an initialized getter data directory And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" When I run getter autogen installed preview for that inventory @@ -17,22 +17,20 @@ Feature: Installed app autogen And I save the autogen preview to a file When I run getter autogen installed apply for that preview with accept-all Then the command succeeds - And the autogen repository contains generated package "android/com.example.autogen" - And the app list contains autogen tracked package "android/com.example.autogen" + And the autogen repository contains generated package "android/app/com.example.autogen" + And the app list contains autogen tracked package "android/app/com.example.autogen" When I run getter repo validate for autogen Then the output reports a valid repository without network - When I run getter package eval for package "android/com.example.autogen" from autogen - Then the output contains package "android/com.example.autogen" named "Example Autogen" Scenario: Higher-priority repositories suppress installed app autogen candidates Given an initialized getter data directory - And a fixture Lua repository "official" with package "android/com.example.autogen" named "Official Example" + And a fixture Lua repository "official" with package "android/app/com.example.autogen" named "Official Example" And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" When I run getter repo add for that repository with priority 0 Then the command succeeds When I run getter autogen installed preview for that inventory Then the command succeeds - And the autogen preview skips package "android/com.example.autogen" because repository "official" covers it + And the autogen preview skips package "android/app/com.example.autogen" because repository "official" covers it Scenario: Cleanup deletes only accepted generated packages missing from installed inventory Given an initialized getter data directory @@ -45,12 +43,12 @@ Feature: Installed app autogen Given an empty installed inventory When I run getter autogen cleanup preview for that inventory Then the command succeeds - And the autogen cleanup preview contains delete candidate "android/com.example.old" + And the autogen cleanup preview contains delete candidate "android/app/com.example.old" And I save the autogen preview to a file When I run getter autogen cleanup apply for that preview with accept-all Then the command succeeds - And the autogen repository does not contain generated package "android/com.example.old" - And the app list does not contain package "android/com.example.old" + And the autogen repository does not contain generated package "android/app/com.example.old" + And the app list does not contain package "android/app/com.example.old" Scenario: Cleanup rejects tampered previews for non-autogen tracked packages Given an initialized getter data directory @@ -75,7 +73,7 @@ Feature: Installed app autogen Then the command succeeds And the app list contains imported package "android/org.fdroid.fdroid" - Scenario: Applying over modified autogen preserves the user-edited file into local + Scenario: Applying over modified autogen reports an ownership conflict Given an initialized getter data directory And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" When I run getter autogen installed preview for that inventory @@ -83,10 +81,10 @@ Feature: Installed app autogen And I save the autogen preview to a file When I run getter autogen installed apply for that preview with accept-all Then the command succeeds - And I replace generated autogen package "android/com.example.autogen" with user-edited content + And I replace generated autogen package "android/app/com.example.autogen" with user-edited content When I run getter autogen installed preview for that inventory Then the command succeeds And I save the autogen preview to a file When I run getter autogen installed apply for that preview with accept-all - Then the command succeeds - And local repository contains preserved package "android/com.example.autogen" + Then the command fails with an autogen error + And local repository has not been written diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml index 4eac396..4b1f9c5 100644 --- a/crates/getter-core/Cargo.toml +++ b/crates/getter-core/Cargo.toml @@ -12,6 +12,7 @@ json_comments = "0.2" mlua = { version = "0.10", features = ["luajit", "vendored"], optional = true } serde = { version = "1", features = ["derive"] } serde_json = "1" +sha2 = "0.10" thiserror = "1" toml = "0.8" diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs index a3bfe8e..8a30220 100644 --- a/crates/getter-core/src/autogen.rs +++ b/crates/getter-core/src/autogen.rs @@ -1,20 +1,20 @@ //! Installed-inventory autogen planning for generated fallback repositories. //! //! This module is pure domain logic: platform adapters provide installed -//! inventory facts, while getter decides package ids, generated Lua shape, -//! deterministic paths, and preview DTOs. +//! inventory facts, while getter decides package ids, generated package +//! directory contents, deterministic paths, and preview DTOs. -use crate::repository::REPO_API_VERSION_V1; use crate::{InstalledTarget, PackageId, PackageKind, RepositoryId, RepositoryPriority}; use serde::{Deserialize, Serialize}; -use std::collections::{BTreeSet, HashMap}; -use std::path::PathBuf; +use sha2::{Digest, Sha512}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::path::{Path, PathBuf}; pub const INSTALLED_INVENTORY_FORMAT: &str = "upgradeall-installed-inventory"; pub const INSTALLED_INVENTORY_VERSION: u32 = 1; -pub const AUTOGEN_MANIFEST_VERSION: u32 = 1; -pub const LOCAL_REPOSITORY_ID: &str = "local"; -pub const LOCAL_REPOSITORY_NAME: &str = "Local"; +pub const AUTOGEN_RECORD_FILE: &str = ".autogen.jsonc"; +pub const AUTOGEN_RECORD_VERSION: u32 = 1; +pub const INSTALLED_AUTOGEN_GENERATOR: &str = "installed-inventory"; pub const DEFAULT_AUTOGEN_REPOSITORY_ID: &str = "autogen"; pub const DEFAULT_AUTOGEN_REPOSITORY_NAME: &str = "UpgradeAll Autogen"; pub const GENERATED_MARKER: &str = "@generated by UpgradeAll getter autogen"; @@ -76,11 +76,39 @@ pub struct AutogenCandidate { pub package_id: PackageId, pub name: String, pub installed: InstalledTarget, + pub relative_path: PathBuf, + pub files: Vec, + pub record: AutogenRecord, + /// Hash of the serialized `.autogen.jsonc` record content. + pub content_hash: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GeneratedPackageFile { pub relative_path: PathBuf, pub content: String, pub content_hash: String, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AutogenRecord { + pub version: u32, + pub generator: String, + pub package_id: PackageId, + pub output_relative_path: PathBuf, + pub input: AutogenRecordInput, + /// Getter-written generated files and their content hashes. + /// `.autogen.jsonc` itself is intentionally excluded. + pub files: BTreeMap, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum AutogenRecordInput { + InstalledAndroidPackage { package_name: String }, + InstalledMagiskModule { module_id: String }, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct AutogenSkip { pub package_id: PackageId, @@ -96,44 +124,6 @@ pub enum AutogenSkipReason { CoveredByHigherPriorityRepository, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct AutogenManifest { - pub version: u32, - pub repository_id: RepositoryId, - pub packages: Vec, -} - -impl AutogenManifest { - pub fn from_candidates(candidates: &[AutogenCandidate]) -> Self { - Self { - version: AUTOGEN_MANIFEST_VERSION, - repository_id: RepositoryId::new(DEFAULT_AUTOGEN_REPOSITORY_ID) - .expect("autogen is a valid repository id"), - packages: candidates - .iter() - .map(|candidate| AutogenManifestEntry { - package_id: candidate.package_id.clone(), - relative_path: candidate.relative_path.clone(), - content_hash: candidate.content_hash.clone(), - }) - .collect(), - } - } - - pub fn package(&self, package_id: &PackageId) -> Option<&AutogenManifestEntry> { - self.packages - .iter() - .find(|entry| &entry.package_id == package_id) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct AutogenManifestEntry { - pub package_id: PackageId, - pub relative_path: PathBuf, - pub content_hash: String, -} - #[derive(Debug, thiserror::Error)] pub enum AutogenError { #[error("unsupported installed inventory format '{0}'")] @@ -144,14 +134,16 @@ pub enum AutogenError { EmptyIdentity, #[error("failed to construct package id from installed inventory: {0}")] PackageId(#[from] crate::PackageIdError), + #[error("failed to serialize generated autogen record: {0}")] + SerializeRecord(#[from] serde_json::Error), } /// Build a deterministic installed-autogen preview from installed inventory. /// /// `covered_packages` contains package ids already supplied by repositories with /// priority higher than the generated repository. Those packages are skipped so -/// generated fallback files do not compete with user-authored `local` or -/// upstream package files. +/// generated fallback directories do not compete with user-authored `local` or +/// upstream package directories. pub fn plan_installed_autogen( inventory: &InstalledInventory, covered_packages: &HashMap, @@ -210,38 +202,25 @@ pub fn validate_installed_inventory(inventory: &InstalledInventory) -> Result<() Ok(()) } -pub fn default_autogen_repo_toml() -> String { - repo_toml( - DEFAULT_AUTOGEN_REPOSITORY_ID, - DEFAULT_AUTOGEN_REPOSITORY_NAME, - RepositoryPriority::GENERATED_FALLBACK, - ) +pub fn content_hash(content: &str) -> String { + content_hash_bytes(content.as_bytes()) } -pub fn local_repo_toml() -> String { - repo_toml( - LOCAL_REPOSITORY_ID, - LOCAL_REPOSITORY_NAME, - RepositoryPriority::LOCAL, - ) +pub fn content_hash_bytes(content: &[u8]) -> String { + let mut hasher = Sha512::new(); + hasher.update(content); + format!("sha512:{:x}", hasher.finalize()) } -fn repo_toml(id: &str, name: &str, priority: RepositoryPriority) -> String { - format!( - "id = \"{id}\"\nname = \"{name}\"\npriority = {}\napi_version = \"{REPO_API_VERSION_V1}\"\n", - priority.value() - ) -} - -pub fn generated_file_is_managed(content: &str) -> bool { - content - .lines() - .next() - .is_some_and(|line| line.contains(GENERATED_MARKER)) +pub fn render_autogen_record(record: &AutogenRecord) -> Result { + serde_json::to_string_pretty(record).map(|mut content| { + content.push('\n'); + content + }) } -pub fn content_hash(content: &str) -> String { - format!("fnv1a64:{:016x}", fnv1a64(content.as_bytes())) +pub fn package_relative_path(package_id: &PackageId) -> PathBuf { + package_id.to_string().split('/').collect() } fn candidate_from_inventory_item( @@ -254,7 +233,7 @@ fn candidate_from_inventory_item( .. } => { let package_name = non_empty(package_name)?; - let package_id = PackageId::new(PackageKind::Android, package_name)?; + let package_id = PackageId::new(PackageKind::Android, format!("app/{package_name}"))?; let name = label .as_deref() .filter(|label| !label.trim().is_empty()) @@ -263,17 +242,10 @@ fn candidate_from_inventory_item( let installed = InstalledTarget::AndroidPackage { package_name: package_name.to_owned(), }; - let relative_path = package_relative_path(&package_id); - let content = render_package_lua(&package_id, &name, &installed); - let content_hash = content_hash(&content); - Ok(AutogenCandidate { - package_id, - name, - installed, - relative_path, - content, - content_hash, - }) + let input = AutogenRecordInput::InstalledAndroidPackage { + package_name: package_name.to_owned(), + }; + candidate(package_id, name, installed, input) } InstalledInventoryItem::MagiskModule { module_id, name, .. @@ -288,38 +260,113 @@ fn candidate_from_inventory_item( let installed = InstalledTarget::MagiskModule { module_id: module_id.to_owned(), }; - let relative_path = package_relative_path(&package_id); - let content = render_package_lua(&package_id, &name, &installed); - let content_hash = content_hash(&content); - Ok(AutogenCandidate { - package_id, - name, - installed, - relative_path, - content, - content_hash, - }) + let input = AutogenRecordInput::InstalledMagiskModule { + module_id: module_id.to_owned(), + }; + candidate(package_id, name, installed, input) } } } -fn non_empty(value: &str) -> Result<&str, AutogenError> { - let value = value.trim(); - if value.is_empty() { - Err(AutogenError::EmptyIdentity) - } else { - Ok(value) +fn candidate( + package_id: PackageId, + name: String, + installed: InstalledTarget, + input: AutogenRecordInput, +) -> Result { + let relative_path = package_relative_path(&package_id); + let files = generated_package_files(&name, &installed)?; + let record = autogen_record(package_id.clone(), relative_path.clone(), input, &files); + let content_hash = content_hash(&render_autogen_record(&record)?); + Ok(AutogenCandidate { + package_id, + name, + installed, + relative_path, + files, + record, + content_hash, + }) +} + +fn generated_package_files( + name: &str, + installed: &InstalledTarget, +) -> Result, AutogenError> { + let metadata = match installed { + InstalledTarget::AndroidPackage { package_name } => serde_json::json!({ + "type": "android:app", + "display_name": name, + "android": { "package_name": package_name }, + }), + InstalledTarget::MagiskModule { module_id } => serde_json::json!({ + "type": "magisk:module", + "display_name": name, + "magisk": { "module_id": module_id }, + }), + InstalledTarget::Generic { id } => serde_json::json!({ + "type": "generic", + "display_name": name, + "generic": { "id": id }, + }), + }; + let metadata = render_pretty_json(metadata)?; + let manifest = String::new(); + let version_lua = render_version_lua(installed); + Ok(vec![ + generated_file("metadata.jsonc", metadata), + generated_file("Manifest", manifest), + generated_file("9999.lua", version_lua), + ]) +} + +fn render_pretty_json(value: serde_json::Value) -> Result { + serde_json::to_string_pretty(&value).map(|mut content| { + content.push('\n'); + content + }) +} + +fn generated_file(relative_path: &str, content: String) -> GeneratedPackageFile { + GeneratedPackageFile { + relative_path: PathBuf::from(relative_path), + content_hash: content_hash(&content), + content, } } -pub fn package_relative_path(package_id: &PackageId) -> PathBuf { - let mut path = PathBuf::from("packages"); - path.push(package_id.kind().as_str()); - path.push(format!("{}.lua", package_id.name())); - path +fn autogen_record( + package_id: PackageId, + output_relative_path: PathBuf, + input: AutogenRecordInput, + files: &[GeneratedPackageFile], +) -> AutogenRecord { + AutogenRecord { + version: AUTOGEN_RECORD_VERSION, + generator: INSTALLED_AUTOGEN_GENERATOR.to_owned(), + package_id, + output_relative_path, + input, + files: files + .iter() + .map(|file| { + ( + record_file_key(&file.relative_path), + file.content_hash.clone(), + ) + }) + .collect(), + } } -fn render_package_lua(package_id: &PackageId, name: &str, installed: &InstalledTarget) -> String { +pub fn record_file_key(path: &Path) -> String { + path.components() + .map(|component| component.as_os_str().to_string_lossy()) + .collect::>() + .join("/") +} + +fn render_version_lua(installed: &InstalledTarget) -> String { let installed_lua = match installed { InstalledTarget::AndroidPackage { package_name } => format!( "{{ kind = \"android_package\", package_name = {} }}", @@ -335,9 +382,7 @@ fn render_package_lua(package_id: &PackageId, name: &str, installed: &InstalledT }; format!( - "-- {GENERATED_MARKER}\nreturn package_def {{\n id = {},\n name = {},\n installed = {{\n {installed_lua},\n }},\n}}\n", - lua_string(&package_id.to_string()), - lua_string(name) + "#!/bin/upa-lua v1\n-- {GENERATED_MARKER}\nreturn package_version {{\n installed = {{\n {installed_lua},\n }},\n}}\n", ) } @@ -357,27 +402,23 @@ fn lua_string(value: &str) -> String { escaped } -fn fnv1a64(bytes: &[u8]) -> u64 { - let mut hash = 0xcbf29ce484222325u64; - for byte in bytes { - hash ^= u64::from(*byte); - hash = hash.wrapping_mul(0x100000001b3); +fn non_empty(value: &str) -> Result<&str, AutogenError> { + let value = value.trim(); + if value.is_empty() { + Err(AutogenError::EmptyIdentity) + } else { + Ok(value) } - hash } #[cfg(test)] mod tests { use super::*; - #[cfg(feature = "lua")] - use crate::lua::evaluate_package_file; - #[cfg(feature = "lua")] - use crate::repository::RepositoryLayout; - #[cfg(feature = "lua")] + use crate::repository::RepositoryPackageDirectoryLayout; use std::fs; #[test] - fn preview_generates_deterministic_android_package_lua() { + fn preview_generates_deterministic_android_package_directory() { let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { package_name: "org.fdroid.fdroid".to_owned(), label: Some("F-Droid".to_owned()), @@ -396,20 +437,34 @@ mod tests { let candidate = &plan.candidates[0]; assert_eq!( candidate.package_id.to_string(), - "android/org.fdroid.fdroid" + "android/app/org.fdroid.fdroid" ); assert_eq!( candidate.relative_path, - PathBuf::from("packages/android/org.fdroid.fdroid.lua") + PathBuf::from("android/app/org.fdroid.fdroid") + ); + assert_eq!(candidate.files.len(), 3); + assert!(candidate + .files + .iter() + .any(|file| file.relative_path == PathBuf::from("metadata.jsonc") + && file.content.contains("org.fdroid.fdroid"))); + assert!(candidate.files.iter().any(|file| { + file.relative_path == PathBuf::from("9999.lua") + && file.content.starts_with("#!/bin/upa-lua v1\n") + && file.content.contains(GENERATED_MARKER) + })); + assert_eq!(candidate.record.package_id, candidate.package_id); + assert_eq!(candidate.record.files.len(), candidate.files.len()); + assert_eq!( + candidate.content_hash, + content_hash(&render_autogen_record(&candidate.record).unwrap()) ); - assert!(candidate.content.contains(GENERATED_MARKER)); - assert!(candidate.content.contains("package_def")); - assert_eq!(candidate.content_hash, content_hash(&candidate.content)); } #[test] fn preview_skips_packages_covered_by_higher_priority_repositories() { - let package_id: PackageId = "android/org.fdroid.fdroid".parse().unwrap(); + let package_id: PackageId = "android/app/org.fdroid.fdroid".parse().unwrap(); let mut covered = HashMap::new(); covered.insert(package_id, RepositoryId::new("official").unwrap()); let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { @@ -478,41 +533,12 @@ mod tests { } #[test] - fn manifest_records_generated_package_hashes() { - let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { - package_name: "org.fdroid.fdroid".to_owned(), - label: Some("F-Droid".to_owned()), - version_name: None, - version_code: None, - }]); - let plan = plan_installed_autogen(&inventory, &HashMap::new()).unwrap(); - - let manifest = AutogenManifest::from_candidates(&plan.candidates); - - assert_eq!(manifest.version, AUTOGEN_MANIFEST_VERSION); - assert_eq!( - manifest.repository_id.as_str(), - DEFAULT_AUTOGEN_REPOSITORY_ID - ); - assert_eq!(manifest.packages.len(), 1); - assert_eq!( - manifest.packages[0].content_hash, - content_hash(&plan.candidates[0].content) - ); - } - - #[cfg(feature = "lua")] - #[test] - fn generated_lua_evaluates_through_package_boundary() { + fn generated_files_form_a_package_directory_boundary() { let temp = tempfile::tempdir().unwrap(); let repo = temp.path(); - fs::write(repo.join("repo.toml"), default_autogen_repo_toml()).unwrap(); - fs::create_dir_all(repo.join("packages/android")).unwrap(); - fs::create_dir(repo.join("lib")).unwrap(); - fs::create_dir(repo.join("templates")).unwrap(); let inventory = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { package_name: "org.fdroid.fdroid".to_owned(), - label: Some("F-Droid \"client\"".to_owned()), + label: Some("F-Droid".to_owned()), version_name: None, version_code: None, }]); @@ -520,27 +546,29 @@ mod tests { .unwrap() .candidates .remove(0); - let package_path = repo.join(&candidate.relative_path); - fs::write(&package_path, candidate.content).unwrap(); + let package_dir = repo.join(&candidate.relative_path); + fs::create_dir_all(&package_dir).unwrap(); + for file in &candidate.files { + fs::write(package_dir.join(&file.relative_path), &file.content).unwrap(); + } + fs::write( + package_dir.join(AUTOGEN_RECORD_FILE), + render_autogen_record(&candidate.record).unwrap(), + ) + .unwrap(); - let layout = RepositoryLayout::load(repo).unwrap(); - let package = evaluate_package_file(&layout, &package_path).unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(repo).unwrap(); - assert_eq!(package.id.to_string(), "android/org.fdroid.fdroid"); - assert_eq!(package.name, "F-Droid \"client\""); + assert_eq!(layout.packages.len(), 1); assert_eq!( - package.installed, - vec![InstalledTarget::AndroidPackage { - package_name: "org.fdroid.fdroid".to_owned(), - }] + layout.packages[0].id.to_string(), + "android/app/org.fdroid.fdroid" ); + assert_eq!(layout.packages[0].version_scripts[0].version, "9999"); } #[test] - fn generated_file_marker_identifies_managed_files_only() { - assert!(generated_file_is_managed(&format!( - "-- {GENERATED_MARKER}\nreturn {{}}" - ))); - assert!(!generated_file_is_managed("return {}")); + fn content_hash_uses_sha512_prefix() { + assert!(content_hash("hello").starts_with("sha512:")); } } diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index c81da6b..26863cb 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -8,6 +8,7 @@ default = [] lua = ["getter-core/lua"] [dependencies] +json_comments = "0.2" getter-core = { path = "../getter-core", default-features = false } getter-providers = { path = "../getter-providers" } getter-storage = { path = "../getter-storage" } diff --git a/crates/getter-operations/src/autogen.rs b/crates/getter-operations/src/autogen.rs index faa06a9..0c0b5f0 100644 --- a/crates/getter-operations/src/autogen.rs +++ b/crates/getter-operations/src/autogen.rs @@ -2,28 +2,28 @@ //! //! The CLI and native bridge both call this module so there is one implementation //! of installed-autogen preview/apply semantics. Platform layers provide installed -//! inventory facts; this module decides generated package ids, repository -//! coverage, file writes, manifest updates, preservation behavior, and tracked -//! state updates. +//! inventory facts; this module decides generated package directories, +//! repository coverage, package-local `.autogen.jsonc` ownership, file writes, +//! cleanup, and tracked state updates. use getter_core::autogen::{ - content_hash, local_repo_toml, plan_installed_autogen, AutogenManifest, AutogenManifestEntry, - AutogenPlan, AutogenSkipReason, InstalledInventory, DEFAULT_AUTOGEN_REPOSITORY_ID, - DEFAULT_AUTOGEN_REPOSITORY_NAME, LOCAL_REPOSITORY_ID, LOCAL_REPOSITORY_NAME, + content_hash, content_hash_bytes, plan_installed_autogen, record_file_key, + render_autogen_record, AutogenCandidate, AutogenPlan, AutogenRecord, AutogenSkipReason, + GeneratedPackageFile, InstalledInventory, AUTOGEN_RECORD_FILE, AUTOGEN_RECORD_VERSION, + DEFAULT_AUTOGEN_REPOSITORY_ID, DEFAULT_AUTOGEN_REPOSITORY_NAME, INSTALLED_AUTOGEN_GENERATOR, }; use getter_core::repository::{ generated_repository_target, GeneratedRepositoryTarget, GetterDataDirLayout, RepositoryLayout, - RepositoryLoadError, RepositoryMetadata, RepositoryRootConfig, REPO_API_VERSION_V1, + RepositoryLoadError, RepositoryMetadata, RepositoryPackageDirectoryLayout, + RepositoryRootConfig, REPO_API_VERSION_V1, }; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; -use getter_storage::{MainDb, StorageError, StoredRepository}; +use getter_storage::{MainDb, StorageError}; use serde_json::{json, Value}; -use std::collections::{BTreeSet, HashMap}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::fs; use std::path::{Component, Path, PathBuf}; -pub const AUTOGEN_MANIFEST_FILE: &str = "autogen-manifest.json"; - #[derive(Debug, Clone, PartialEq, Eq)] pub enum AutogenAcceptance { AcceptAll, @@ -79,7 +79,11 @@ pub fn installed_preview_json( plan: &AutogenPlan, ) -> AutogenOperationResult { let (target_alias, target_path, _target_priority) = generated_repository_config(data_dir)?; - let candidates: Vec = plan.candidates.iter().map(autogen_candidate_json).collect(); + let candidates: Vec = plan + .candidates + .iter() + .map(autogen_candidate_json) + .collect::>()?; let skipped: Vec = plan.skipped.iter().map(autogen_skip_json).collect(); Ok(json!({ "operation": "installed.preview", @@ -103,17 +107,14 @@ pub fn cleanup_preview_json( inventory: &InstalledInventory, ) -> AutogenOperationResult { let (target_alias, repo_path, _target_priority) = generated_repository_config(data_dir)?; - let Some(manifest) = read_autogen_manifest(&repo_path)? else { - return Ok(json!({ - "operation": "cleanup.preview", - "target_repo_id": target_alias.as_str(), - "target_repo_path": repo_path, - "summary": { "candidate_count": 0, "skipped_count": 0, "write_count": 0, "delete_count": 0 }, - "candidates": [], - "skipped": [], - "diagnostics": [], - })); - }; + if !repo_path.is_dir() { + return Ok(cleanup_preview_response( + target_alias, + repo_path, + Vec::new(), + Vec::new(), + )); + } let plan = build_installed_autogen_plan(data_dir, db, inventory)?; let installed_ids: BTreeSet = plan .candidates @@ -121,14 +122,32 @@ pub fn cleanup_preview_json( .map(|candidate| candidate.package_id.to_string()) .chain(plan.skipped.iter().map(|skip| skip.package_id.to_string())) .collect(); + + let layout = RepositoryPackageDirectoryLayout::load(&repo_path)?; let mut candidates = Vec::new(); - for entry in manifest.packages { - if !installed_ids.contains(&entry.package_id.to_string()) { + let mut diagnostics = Vec::new(); + for package in layout.packages { + let relative_path = relative_package_path(&repo_path, &package.path)?; + let ownership = match read_owned_record(&package.path, &package.id, &relative_path) { + Ok(ownership) => ownership, + Err(error) => { + diagnostics.push(autogen_diagnostic( + "autogen.ownership_conflict", + format!( + "generated package '{}' has invalid ownership: {error}", + package.id + ), + Some(package.id.to_string()), + )); + continue; + } + }; + if !installed_ids.contains(&package.id.to_string()) { candidates.push(json!({ - "package_id": entry.package_id.to_string(), + "package_id": package.id.to_string(), "action": "delete", - "output_relative_path": entry.relative_path, - "content_hash": entry.content_hash, + "output_relative_path": relative_path, + "content_hash": ownership.content_hash, "reason": "not_in_installed_inventory", })); } @@ -140,20 +159,12 @@ pub fn cleanup_preview_json( .unwrap_or_default() .to_owned() }); - Ok(json!({ - "operation": "cleanup.preview", - "target_repo_id": target_alias.as_str(), - "target_repo_path": repo_path, - "summary": { - "candidate_count": candidates.len(), - "skipped_count": 0, - "write_count": 0, - "delete_count": candidates.len(), - }, - "candidates": candidates, - "skipped": [], - "diagnostics": [], - })) + Ok(cleanup_preview_response( + target_alias, + repo_path, + candidates, + diagnostics, + )) } pub fn apply_installed_preview( @@ -171,74 +182,25 @@ pub fn apply_installed_preview( } ensure_generated_repository(&repo_path, db, &target_alias, target_priority)?; let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut manifest = - read_autogen_manifest(&repo_path)?.unwrap_or_else(|| empty_autogen_manifest(&target_alias)); let mut applied = Vec::new(); - let mut preserved = Vec::new(); for candidate in accepted { let package_id = preview_package_id(candidate)?; let relative_path = preview_relative_path(candidate)?; - let content = candidate - .get("content") - .and_then(Value::as_str) - .ok_or_else(|| { - AutogenOperationError::Autogen("preview candidate missing content".to_owned()) - })?; - let expected_hash = candidate - .get("content_hash") - .and_then(Value::as_str) - .ok_or_else(|| { - AutogenOperationError::Autogen("preview candidate missing content_hash".to_owned()) - })?; - if content_hash(content) != expected_hash { - return Err(AutogenOperationError::Autogen(format!( - "preview content hash mismatch for {package_id}" - ))); - } - let target = safe_join(&repo_path, &relative_path)?; - if target.exists() { - let current = fs::read_to_string(&target).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to read existing autogen file '{}': {source}", - target.display() - )) - })?; - let current_hash = content_hash(¤t); - let known_hash = manifest - .package(&package_id) - .map(|entry| entry.content_hash.as_str()); - if known_hash != Some(current_hash.as_str()) { - preserved.push(preserve_autogen_file_in_local( - data_dir, - db, - &package_id, - ¤t, - )?); - } - } - if let Some(parent) = target.parent() { - fs::create_dir_all(parent).map_err(|source| { + let payload = preview_candidate_payload(candidate, &package_id, &relative_path)?; + let target_dir = safe_join(&repo_path, &relative_path)?; + if target_dir.exists() { + read_owned_record(&target_dir, &package_id, &relative_path)?; + clear_directory_contents(&target_dir)?; + } else { + fs::create_dir_all(&target_dir).map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to create autogen package directory '{}': {source}", - parent.display() + "failed to create generated package directory '{}': {source}", + target_dir.display() )) })?; } - fs::write(&target, content).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to write autogen package '{}': {source}", - target.display() - )) - })?; - upsert_manifest_entry( - &mut manifest, - AutogenManifestEntry { - package_id: package_id.clone(), - relative_path: relative_path.clone(), - content_hash: expected_hash.to_owned(), - }, - ); + write_generated_package(&target_dir, &payload.files, &payload.record_content)?; db.upsert_generated_tracked_package_preserving_user_state(&package_id, &target_alias)?; applied.push(json!({ "package_id": package_id.to_string(), @@ -246,13 +208,11 @@ pub fn apply_installed_preview( })); } - write_autogen_manifest(&repo_path, &manifest)?; Ok(json!({ "target_repo_id": target_alias.as_str(), "target_repo_path": repo_path, "applied_count": applied.len(), "applied": applied, - "preserved_to_local": preserved, })) } @@ -270,10 +230,7 @@ pub fn apply_cleanup_preview( ))); } let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut manifest = - read_autogen_manifest(&repo_path)?.unwrap_or_else(|| empty_autogen_manifest(&target_alias)); let mut deleted = Vec::new(); - let mut preserved = Vec::new(); for candidate in accepted { let package_id = preview_package_id(candidate)?; let relative_path = preview_relative_path(candidate)?; @@ -285,57 +242,25 @@ pub fn apply_cleanup_preview( "cleanup preview candidate missing content_hash".to_owned(), ) })?; - let manifest_entry = manifest.package(&package_id).ok_or_else(|| { - AutogenOperationError::Autogen(format!( - "cleanup preview candidate {package_id} is not managed by autogen manifest" - )) - })?; - if manifest_entry.relative_path != relative_path - || manifest_entry.content_hash != expected_hash - { + let target_dir = safe_join(&repo_path, &relative_path)?; + let ownership = read_owned_record(&target_dir, &package_id, &relative_path)?; + if ownership.content_hash != expected_hash { return Err(AutogenOperationError::Autogen(format!( - "cleanup preview candidate {package_id} does not match autogen manifest" + "cleanup preview candidate {package_id} does not match current autogen record" ))); } - let target = safe_join(&repo_path, &relative_path)?; - if target.exists() { - let current = fs::read_to_string(&target).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to read existing autogen file '{}': {source}", - target.display() - )) - })?; - if content_hash(¤t) != expected_hash { - preserved.push(preserve_autogen_file_in_local( - data_dir, - db, - &package_id, - ¤t, - )?); - } - fs::remove_file(&target).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to delete autogen package '{}': {source}", - target.display() - )) - })?; - } - manifest - .packages - .retain(|entry| entry.package_id != package_id); + clear_directory_contents(&target_dir)?; db.delete_generated_tracked_package(&package_id, &target_alias)?; deleted.push(json!({ "package_id": package_id.to_string(), "output_relative_path": relative_path, })); } - write_autogen_manifest(&repo_path, &manifest)?; Ok(json!({ "target_repo_id": target_alias.as_str(), "target_repo_path": repo_path, "deleted_count": deleted.len(), "deleted": deleted, - "preserved_to_local": preserved, })) } @@ -391,21 +316,48 @@ fn higher_priority_package_coverage( let Some(path) = repo.path.as_ref() else { continue; }; - let layout = load_repository_layout(Path::new(path))?; - for package in layout.packages { - covered.entry(package.id).or_insert_with(|| repo.id.clone()); + for package_id in load_repository_package_ids(Path::new(path))? { + covered.entry(package_id).or_insert_with(|| repo.id.clone()); } } Ok(covered) } -fn load_repository_layout(path: &Path) -> AutogenOperationResult { - RepositoryLayout::load(path) - .map_err(|source| AutogenOperationError::Repository(source.to_string())) +fn load_repository_package_ids(path: &Path) -> AutogenOperationResult> { + if path.join("repo.toml").is_file() { + let layout = RepositoryLayout::load(path) + .map_err(|source| AutogenOperationError::Repository(source.to_string()))?; + Ok(layout + .packages + .into_iter() + .map(|package| package.id) + .collect()) + } else { + let layout = RepositoryPackageDirectoryLayout::load(path) + .map_err(|source| AutogenOperationError::Repository(source.to_string()))?; + Ok(layout + .packages + .into_iter() + .map(|package| package.id) + .collect()) + } } -fn autogen_candidate_json(candidate: &getter_core::autogen::AutogenCandidate) -> Value { - json!({ +fn autogen_candidate_json(candidate: &AutogenCandidate) -> AutogenOperationResult { + let record_content = render_autogen_record(&candidate.record) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + let files: Vec = candidate + .files + .iter() + .map(|file| { + json!({ + "relative_path": file.relative_path, + "content_hash": file.content_hash, + "content": file.content, + }) + }) + .collect(); + Ok(json!({ "package_id": candidate.package_id.to_string(), "kind": candidate.package_id.kind().as_str(), "display_name": candidate.name, @@ -413,8 +365,10 @@ fn autogen_candidate_json(candidate: &getter_core::autogen::AutogenCandidate) -> "action": "create", "output_relative_path": candidate.relative_path, "content_hash": candidate.content_hash, - "content": candidate.content, - }) + "content": record_content, + "autogen_record_content": record_content, + "files": files, + })) } fn autogen_skip_json(skip: &getter_core::autogen::AutogenSkip) -> Value { @@ -428,6 +382,28 @@ fn autogen_skip_json(skip: &getter_core::autogen::AutogenSkip) -> Value { }) } +fn cleanup_preview_response( + target_alias: RepositoryId, + repo_path: PathBuf, + candidates: Vec, + diagnostics: Vec, +) -> Value { + json!({ + "operation": "cleanup.preview", + "target_repo_id": target_alias.as_str(), + "target_repo_path": repo_path, + "summary": { + "candidate_count": candidates.len(), + "skipped_count": 0, + "write_count": 0, + "delete_count": candidates.len(), + }, + "candidates": candidates, + "skipped": [], + "diagnostics": diagnostics, + }) +} + fn accepted_preview_candidates<'a>( preview: &'a Value, acceptance: &AutogenAcceptance, @@ -480,13 +456,111 @@ fn preview_relative_path(candidate: &Value) -> AutogenOperationResult { }) } +struct PreviewCandidatePayload { + record_content: String, + files: Vec, +} + +fn preview_candidate_payload( + candidate: &Value, + package_id: &PackageId, + relative_path: &Path, +) -> AutogenOperationResult { + let record_content = candidate + .get("autogen_record_content") + .or_else(|| candidate.get("content")) + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen( + "preview candidate missing autogen_record_content".to_owned(), + ) + })? + .to_owned(); + let expected_hash = candidate + .get("content_hash") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen("preview candidate missing content_hash".to_owned()) + })?; + if content_hash(&record_content) != expected_hash { + return Err(AutogenOperationError::Autogen(format!( + "preview autogen record hash mismatch for {package_id}" + ))); + } + let files = preview_generated_files(candidate)?; + let record = parse_record_content(&record_content)?; + validate_record(&record, package_id, relative_path, &files)?; + Ok(PreviewCandidatePayload { + record_content, + files, + }) +} + +fn preview_generated_files(candidate: &Value) -> AutogenOperationResult> { + let files = candidate + .get("files") + .and_then(Value::as_array) + .ok_or_else(|| { + AutogenOperationError::Autogen("preview candidate missing files".to_owned()) + })?; + files + .iter() + .map(|file| { + let relative_path = file + .get("relative_path") + .and_then(Value::as_str) + .map(PathBuf::from) + .ok_or_else(|| { + AutogenOperationError::Autogen( + "preview generated file missing relative_path".to_owned(), + ) + })?; + validate_relative_path(&relative_path)?; + let content = file + .get("content") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen( + "preview generated file missing content".to_owned(), + ) + })? + .to_owned(); + let expected_hash = file + .get("content_hash") + .and_then(Value::as_str) + .ok_or_else(|| { + AutogenOperationError::Autogen( + "preview generated file missing content_hash".to_owned(), + ) + })?; + let actual_hash = content_hash(&content); + if actual_hash != expected_hash { + return Err(AutogenOperationError::Autogen(format!( + "preview generated file '{}' hash mismatch", + relative_path.display() + ))); + } + Ok(GeneratedPackageFile { + relative_path, + content, + content_hash: actual_hash, + }) + }) + .collect() +} + fn ensure_generated_repository( repo_path: &Path, db: &MainDb, alias: &RepositoryId, priority: RepositoryPriority, ) -> AutogenOperationResult<()> { - ensure_repository_layout(repo_path, &generated_repo_toml(alias, priority))?; + fs::create_dir_all(repo_path).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create generated repository '{}': {source}", + repo_path.display() + )) + })?; db.upsert_repository( &RepositoryMetadata { id: alias.clone(), @@ -500,15 +574,6 @@ fn ensure_generated_repository( Ok(()) } -fn generated_repo_toml(alias: &RepositoryId, priority: RepositoryPriority) -> String { - format!( - "id = \"{}\"\nname = \"{}\"\npriority = {}\napi_version = \"{REPO_API_VERSION_V1}\"\n", - alias.as_str(), - generated_repository_name(alias), - priority.value() - ) -} - fn generated_repository_name(alias: &RepositoryId) -> String { if alias.as_str() == DEFAULT_AUTOGEN_REPOSITORY_ID { DEFAULT_AUTOGEN_REPOSITORY_NAME.to_owned() @@ -517,177 +582,248 @@ fn generated_repository_name(alias: &RepositoryId) -> String { } } -fn ensure_local_repository(data_dir: &Path, db: &MainDb) -> AutogenOperationResult { - let local_id = RepositoryId::new(LOCAL_REPOSITORY_ID).expect("valid id"); - if let Ok(existing) = find_repository(db, &local_id) { - let repo_path = repo_path(&existing)?; - ensure_repository_layout(&repo_path, &local_repo_toml())?; - return Ok(repo_path); - } - - let repo_path = GetterDataDirLayout::new(data_dir) - .repository_path(&RepositoryId::new(LOCAL_REPOSITORY_ID).expect("valid id")); - ensure_repository_layout(&repo_path, &local_repo_toml())?; - db.upsert_repository( - &RepositoryMetadata { - id: local_id, - name: LOCAL_REPOSITORY_NAME.to_owned(), - priority: RepositoryPriority::LOCAL, - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(&repo_path), - None, - )?; - Ok(repo_path) -} - -fn find_repository(db: &MainDb, id: &RepositoryId) -> AutogenOperationResult { - db.repositories()? - .into_iter() - .find(|repo| &repo.id == id) - .ok_or_else(|| { - AutogenOperationError::Repository(format!("repository '{id}' is not registered")) - }) -} - -fn repo_path(repo: &StoredRepository) -> AutogenOperationResult { - repo.path.as_ref().map(PathBuf::from).ok_or_else(|| { - AutogenOperationError::Repository(format!("repository '{}' has no path", repo.id)) - }) +struct LoadedAutogenRecord { + content_hash: String, } -fn ensure_repository_layout(repo_path: &Path, repo_toml: &str) -> AutogenOperationResult<()> { - fs::create_dir_all(repo_path.join("packages")).map_err(|source| { +fn read_owned_record( + package_dir: &Path, + package_id: &PackageId, + relative_path: &Path, +) -> AutogenOperationResult { + let record_path = package_dir.join(AUTOGEN_RECORD_FILE); + if !record_path.is_file() { + return Err(AutogenOperationError::Autogen(format!( + "generated package '{}' is missing {AUTOGEN_RECORD_FILE}", + package_dir.display() + ))); + } + let bytes = fs::read(&record_path).map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to create repository packages dir '{}': {source}", - repo_path.display() + "failed to read autogen record '{}': {source}", + record_path.display() )) })?; - fs::create_dir_all(repo_path.join("lib")).map_err(|source| { + let record: AutogenRecord = serde_json::from_reader(json_comments::StripComments::new( + bytes.as_slice(), + )) + .map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to create repository lib dir '{}': {source}", - repo_path.display() + "failed to parse autogen record '{}': {source}", + record_path.display() )) })?; - fs::create_dir_all(repo_path.join("templates")).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to create repository templates dir '{}': {source}", - repo_path.display() - )) - })?; - let repo_toml_path = repo_path.join("repo.toml"); - if !repo_toml_path.exists() { - fs::write(&repo_toml_path, repo_toml).map_err(|source| { + let files = read_recorded_files(package_dir, &record)?; + validate_record(&record, package_id, relative_path, &files)?; + Ok(LoadedAutogenRecord { + content_hash: content_hash_bytes(&bytes), + }) +} + +fn read_recorded_files( + package_dir: &Path, + record: &AutogenRecord, +) -> AutogenOperationResult> { + let mut files = Vec::new(); + for (relative, expected_hash) in &record.files { + let relative_path = PathBuf::from(relative); + validate_relative_path(&relative_path)?; + let path = package_dir.join(&relative_path); + if !path.is_file() { + return Err(AutogenOperationError::Autogen(format!( + "generated file '{}' listed in {AUTOGEN_RECORD_FILE} is missing", + path.display() + ))); + } + let bytes = fs::read(&path).map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to write repo.toml '{}': {source}", - repo_toml_path.display() + "failed to read generated file '{}': {source}", + path.display() )) })?; + let actual_hash = content_hash_bytes(&bytes); + if &actual_hash != expected_hash { + return Err(AutogenOperationError::Autogen(format!( + "generated file '{}' hash does not match {AUTOGEN_RECORD_FILE}", + path.display() + ))); + } + let content = String::from_utf8(bytes).map_err(|source| { + AutogenOperationError::Autogen(format!( + "generated file '{}' is not UTF-8: {source}", + path.display() + )) + })?; + files.push(GeneratedPackageFile { + relative_path, + content, + content_hash: actual_hash, + }); } - Ok(()) + Ok(files) } -fn preserve_autogen_file_in_local( - data_dir: &Path, - db: &MainDb, +fn parse_record_content(content: &str) -> AutogenOperationResult { + serde_json::from_str(content).map_err(|source| { + AutogenOperationError::Autogen(format!("failed to parse preview autogen record: {source}")) + }) +} + +fn validate_record( + record: &AutogenRecord, package_id: &PackageId, - content: &str, -) -> AutogenOperationResult { - let local_repo = ensure_local_repository(data_dir, db)?; - let primary_relative = getter_core::autogen::package_relative_path(package_id); - let primary_target = safe_join(&local_repo, &primary_relative)?; - let relative_path = if primary_target.exists() { - let backup = PathBuf::from("autogen-preserved") - .join(package_id.kind().as_str()) - .join(format!( - "{}.{}.lua", - package_id.name(), - content_hash(content).replace(':', "-") - )); - safe_join(&local_repo, &backup)?; - backup - } else { - primary_relative - }; - let target = safe_join(&local_repo, &relative_path)?; - if let Some(parent) = target.parent() { - fs::create_dir_all(parent).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to create local preservation directory '{}': {source}", - parent.display() - )) - })?; + relative_path: &Path, + files: &[GeneratedPackageFile], +) -> AutogenOperationResult<()> { + if record.version != AUTOGEN_RECORD_VERSION { + return Err(AutogenOperationError::Autogen(format!( + "unsupported autogen record version {}; expected {AUTOGEN_RECORD_VERSION}", + record.version + ))); } - fs::write(&target, content).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to preserve modified autogen file '{}': {source}", - target.display() - )) - })?; - Ok(json!({ - "package_id": package_id.to_string(), - "repository_id": LOCAL_REPOSITORY_ID, - "relative_path": relative_path, - })) + if record.generator != INSTALLED_AUTOGEN_GENERATOR { + return Err(AutogenOperationError::Autogen(format!( + "autogen record generator '{}' does not match '{INSTALLED_AUTOGEN_GENERATOR}'", + record.generator + ))); + } + if &record.package_id != package_id { + return Err(AutogenOperationError::Autogen(format!( + "autogen record package '{}' does not match '{package_id}'", + record.package_id + ))); + } + if record.output_relative_path != relative_path { + return Err(AutogenOperationError::Autogen(format!( + "autogen record output path '{}' does not match '{}'", + record.output_relative_path.display(), + relative_path.display() + ))); + } + let file_hashes: BTreeMap = files + .iter() + .map(|file| { + ( + record_file_key(&file.relative_path), + file.content_hash.clone(), + ) + }) + .collect(); + if record.files.contains_key(AUTOGEN_RECORD_FILE) { + return Err(AutogenOperationError::Autogen(format!( + "autogen record must not list {AUTOGEN_RECORD_FILE} in files" + ))); + } + if record.files != file_hashes { + return Err(AutogenOperationError::Autogen( + "autogen record files do not match generated files".to_owned(), + )); + } + Ok(()) } -fn read_autogen_manifest(repo_path: &Path) -> AutogenOperationResult> { - let path = repo_path.join(AUTOGEN_MANIFEST_FILE); - if !path.exists() { - return Ok(None); +fn write_generated_package( + package_dir: &Path, + files: &[GeneratedPackageFile], + record_content: &str, +) -> AutogenOperationResult<()> { + for file in files { + let target = safe_join(package_dir, &file.relative_path)?; + if let Some(parent) = target.parent() { + fs::create_dir_all(parent).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create generated package directory '{}': {source}", + parent.display() + )) + })?; + } + fs::write(&target, &file.content).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to write generated package file '{}': {source}", + target.display() + )) + })?; } - let bytes = fs::read(&path).map_err(|source| { + fs::write(package_dir.join(AUTOGEN_RECORD_FILE), record_content).map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to read autogen manifest '{}': {source}", - path.display() + "failed to write autogen record '{}': {source}", + package_dir.join(AUTOGEN_RECORD_FILE).display() )) - })?; - serde_json::from_slice(&bytes).map(Some).map_err(|source| { - AutogenOperationError::Autogen(format!("failed to parse autogen manifest: {source}")) }) } -fn write_autogen_manifest( - repo_path: &Path, - manifest: &AutogenManifest, -) -> AutogenOperationResult<()> { - fs::create_dir_all(repo_path).map_err(|source| { +fn clear_directory_contents(path: &Path) -> AutogenOperationResult<()> { + fs::create_dir_all(path).map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to create autogen repository '{}': {source}", - repo_path.display() + "failed to create generated package directory '{}': {source}", + path.display() )) })?; - let path = repo_path.join(AUTOGEN_MANIFEST_FILE); - let bytes = serde_json::to_vec_pretty(manifest).map_err(|source| { - AutogenOperationError::Autogen(format!("failed to serialize manifest: {source}")) - })?; - fs::write(&path, bytes).map_err(|source| { + for entry in fs::read_dir(path).map_err(|source| { AutogenOperationError::Autogen(format!( - "failed to write autogen manifest '{}': {source}", + "failed to read generated package directory '{}': {source}", path.display() )) - }) + })? { + let entry = entry.map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to read generated package directory '{}': {source}", + path.display() + )) + })?; + let child = entry.path(); + let file_type = entry.file_type().map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to inspect generated package entry '{}': {source}", + child.display() + )) + })?; + if file_type.is_dir() { + fs::remove_dir_all(&child).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to delete generated package directory '{}': {source}", + child.display() + )) + })?; + } else { + fs::remove_file(&child).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to delete generated package file '{}': {source}", + child.display() + )) + })?; + } + } + Ok(()) } -fn empty_autogen_manifest(repository_id: &RepositoryId) -> AutogenManifest { - AutogenManifest { - version: getter_core::autogen::AUTOGEN_MANIFEST_VERSION, - repository_id: repository_id.clone(), - packages: Vec::new(), - } +fn relative_package_path(root: &Path, package_dir: &Path) -> AutogenOperationResult { + package_dir + .strip_prefix(root) + .map(Path::to_path_buf) + .map_err(|_| { + AutogenOperationError::Autogen(format!( + "package directory '{}' is not under generated repository '{}'", + package_dir.display(), + root.display() + )) + }) } -fn upsert_manifest_entry(manifest: &mut AutogenManifest, entry: AutogenManifestEntry) { - manifest - .packages - .retain(|existing| existing.package_id != entry.package_id); - manifest.packages.push(entry); - manifest - .packages - .sort_by_key(|existing| existing.package_id.to_string()); +fn autogen_diagnostic(code: &str, message: String, package_id: Option) -> Value { + json!({ + "code": code, + "message": message, + "detail": package_id, + }) } fn safe_join(root: &Path, relative: &Path) -> AutogenOperationResult { + validate_relative_path(relative)?; + Ok(root.join(relative)) +} + +fn validate_relative_path(relative: &Path) -> AutogenOperationResult<()> { if relative.is_absolute() || relative.components().any(|component| { matches!( @@ -701,7 +837,7 @@ fn safe_join(root: &Path, relative: &Path) -> AutogenOperationResult { relative.display() ))); } - Ok(root.join(relative)) + Ok(()) } #[cfg(test)] @@ -734,6 +870,11 @@ mod tests { preview["target_repo_path"].as_str(), data_dir.join("repo/autogen").to_str() ); + assert_eq!( + preview["candidates"][0]["output_relative_path"], + "android/app/com.example.autogen" + ); + assert!(preview["candidates"][0]["files"].is_array()); assert!(!data_dir.join("repo/autogen").exists()); } @@ -750,15 +891,114 @@ mod tests { .unwrap(); assert_eq!(result["target_repo_id"], "autogen"); - assert!(data_dir.join("repo/autogen/repo.toml").is_file()); + assert!(data_dir.join("repo/autogen").is_dir()); + assert!(data_dir + .join("repo/autogen/android/app/com.example.autogen/metadata.jsonc") + .is_file()); assert!(data_dir - .join("repo/autogen/packages/android/com.example.autogen.lua") + .join("repo/autogen/android/app/com.example.autogen/9999.lua") .is_file()); + assert!(data_dir + .join("repo/autogen/android/app/com.example.autogen/.autogen.jsonc") + .is_file()); + assert!(!data_dir.join("repo/autogen/repo.toml").exists()); + assert!(!data_dir.join("repo/autogen/packages").exists()); let repos = db.repositories().unwrap(); assert_eq!(repos[0].id.as_str(), "autogen"); assert_eq!(repos[0].priority.value(), -1); } + #[test] + fn apply_rejects_existing_package_directory_without_ownership_record() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + fs::create_dir_all(data_dir.join("repo/autogen/android/app/com.example.autogen")).unwrap(); + fs::write( + data_dir.join("repo/autogen/android/app/com.example.autogen/metadata.jsonc"), + r#"{ "type": "android:app" }"#, + ) + .unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + + let error = apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("missing .autogen.jsonc")) + ); + } + + #[test] + fn apply_rejects_modified_generated_files_instead_of_preserving_to_local() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll).unwrap(); + fs::write( + data_dir.join("repo/autogen/android/app/com.example.autogen/9999.lua"), + "#!/bin/upa-lua v1\n-- user edited\nreturn {}\n", + ) + .unwrap(); + + let error = apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("hash does not match")) + ); + assert!(!data_dir.join("repo/local").exists()); + } + + #[test] + fn cleanup_clears_generated_package_directory_but_keeps_directory() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll).unwrap(); + let empty_inventory = InstalledInventory::new(Vec::new()); + let cleanup = cleanup_preview_json(data_dir, &db, &empty_inventory).unwrap(); + + assert_eq!( + cleanup["candidates"][0]["package_id"], + "android/app/com.example.autogen" + ); + apply_cleanup_preview(data_dir, &db, &cleanup, &AutogenAcceptance::AcceptAll).unwrap(); + + let package_dir = data_dir.join("repo/autogen/android/app/com.example.autogen"); + assert!(package_dir.is_dir()); + assert_eq!(fs::read_dir(package_dir).unwrap().count(), 0); + } + + #[test] + fn cleanup_rejects_stale_preview_when_autogen_record_changes() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll).unwrap(); + let empty_inventory = InstalledInventory::new(Vec::new()); + let cleanup = cleanup_preview_json(data_dir, &db, &empty_inventory).unwrap(); + fs::write( + data_dir.join("repo/autogen/android/app/com.example.autogen/.autogen.jsonc"), + r#"{ "version": 1, "generator": "installed-inventory", "package_id": "android/app/com.example.autogen", "output_relative_path": "android/app/com.example.autogen", "input": { "kind": "installed_android_package", "package_name": "com.example.autogen" }, "files": {} }"#, + ) + .unwrap(); + + let error = apply_cleanup_preview(data_dir, &db, &cleanup, &AutogenAcceptance::AcceptAll) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("does not match current autogen record") || detail.contains("files do not match")) + ); + } + #[test] fn custom_generated_repository_must_already_exist() { let temp = tempfile::tempdir().unwrap(); From 367fa9bc9c3afff4eaf834041cd38f6bc25f577b Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 01:49:07 +0800 Subject: [PATCH 31/71] feat(repository): evaluate package directories --- crates/getter-cli/src/lib.rs | 97 +++- crates/getter-cli/tests/bdd_cli.rs | 52 +- .../features/cli/autogen_installed.feature | 3 + .../cli/repository_package_eval.feature | 24 + crates/getter-core/src/diagnostics.rs | 123 ++++- crates/getter-core/src/lua.rs | 455 +++++++++++++++++- crates/getter-core/src/repository.rs | 206 ++++++-- crates/getter-operations/src/read_model.rs | 149 ++++-- crates/getter-operations/src/runtime.rs | 112 ++++- 9 files changed, 1105 insertions(+), 116 deletions(-) diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 9b03e76..0cb5e6f 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -6,7 +6,7 @@ use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; -use getter_core::lua::evaluate_package_file; +use getter_core::lua::{evaluate_package_directory_script, evaluate_package_file}; use getter_core::repository::{ default_repository_priority, GetterDataDirLayout, RepositoryLayout, RepositoryMetadata, RepositoryPackageDirectoryLayout, REPOSITORY_ROOT_METADATA_FILE, REPO_API_VERSION_V1, @@ -596,14 +596,10 @@ fn execute(invocation: CliInvocation) -> Result { CliCommand::RepoEval { id } => { let db = open_main_db(&invocation.data_dir)?; let repo = find_repository(&db, &id)?; - let path = repo_path(&repo)?; - let layout = load_repository_layout(&path)?; - let mut packages = Vec::new(); - for package_file in &layout.packages { - let package = evaluate_package_file(&layout, &package_file.path) - .map_err(|error| CliError::PackageEval(error.to_string()))?; - packages.push(package_json(package)?); - } + let packages = evaluate_repository_packages(&repo)? + .into_iter() + .map(package_json) + .collect::, _>>()?; Ok(json!({ "repository": repository_json(repo), "packages": packages, @@ -929,6 +925,13 @@ fn load_repository_layout(path: &Path) -> Result { RepositoryLayout::load(path).map_err(|source| CliError::Repository(source.to_string())) } +fn load_package_directory_layout( + path: &Path, +) -> Result { + RepositoryPackageDirectoryLayout::load(path) + .map_err(|source| CliError::Repository(source.to_string())) +} + fn load_repository_metadata( id: &RepositoryId, path: &Path, @@ -979,16 +982,12 @@ fn evaluate_package_from_repo( package_id: &PackageId, ) -> Result { let repo = find_repository(db, repo_id)?; - let path = repo_path(&repo)?; - let layout = load_repository_layout(&path)?; - let package_file = layout.package_file(package_id).ok_or_else(|| { + evaluate_package_in_repository(&repo, package_id)?.ok_or_else(|| { CliError::PackageEval(format!( "package '{}' was not found in repository '{}'", package_id, repo_id )) - })?; - evaluate_package_file(&layout, &package_file.path) - .map_err(|error| CliError::PackageEval(error.to_string())) + }) } fn evaluate_highest_priority_package( @@ -996,11 +995,8 @@ fn evaluate_highest_priority_package( package_id: &PackageId, ) -> Result { for repo in db.repositories()? { - let path = repo_path(&repo)?; - let layout = load_repository_layout(&path)?; - if let Some(package_file) = layout.package_file(package_id) { - return evaluate_package_file(&layout, &package_file.path) - .map_err(|error| CliError::PackageEval(error.to_string())); + if let Some(package) = evaluate_package_in_repository(&repo, package_id)? { + return Ok(package); } } Err(CliError::PackageEval(format!( @@ -1008,6 +1004,67 @@ fn evaluate_highest_priority_package( ))) } +fn evaluate_repository_packages( + repo: &StoredRepository, +) -> Result, CliError> { + let path = repo_path(repo)?; + if path.join("repo.toml").is_file() { + let layout = load_repository_layout(&path)?; + return layout + .packages + .iter() + .map(|package_file| { + evaluate_package_file(&layout, &package_file.path) + .map_err(|error| CliError::PackageEval(error.to_string())) + }) + .collect(); + } + + let layout = load_package_directory_layout(&path)?; + layout + .packages + .iter() + .map(|package| evaluate_package_directory(&repo.id, &layout, package)) + .collect() +} + +fn evaluate_package_in_repository( + repo: &StoredRepository, + package_id: &PackageId, +) -> Result, CliError> { + let path = repo_path(repo)?; + if path.join("repo.toml").is_file() { + let layout = load_repository_layout(&path)?; + let Some(package_file) = layout.package_file(package_id) else { + return Ok(None); + }; + return evaluate_package_file(&layout, &package_file.path) + .map(Some) + .map_err(|error| CliError::PackageEval(error.to_string())); + } + + let layout = load_package_directory_layout(&path)?; + let Some(package) = layout.package(package_id) else { + return Ok(None); + }; + evaluate_package_directory(&repo.id, &layout, package).map(Some) +} + +fn evaluate_package_directory( + repo_id: &RepositoryId, + layout: &RepositoryPackageDirectoryLayout, + package: &getter_core::repository::PackageDirectory, +) -> Result { + let metadata = layout + .package_metadata(package) + .map_err(|error| CliError::PackageEval(error.to_string()))?; + let script = layout + .unambiguous_version_script(package) + .map_err(|error| CliError::PackageEval(error.to_string()))?; + evaluate_package_directory_script(repo_id, package, &metadata, script) + .map_err(|error| CliError::PackageEval(error.to_string())) +} + fn read_installed_inventory(path: &Path) -> Result { let bytes = fs::read(path) .map_err(|source| CliError::Autogen(format!("failed to read inventory: {source}")))?; diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index e5e2787..35754d5 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -349,20 +349,45 @@ fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: Str #[given(expr = "a package-directory repository {string} with package {string}")] fn package_directory_repository(world: &mut CliWorld, repo_id: String, package_id: String) { + create_package_directory_repository(world, repo_id, package_id, 1); +} + +#[given(expr = "a package-directory repository {string} with multi-version package {string}")] +fn package_directory_repository_multi_version( + world: &mut CliWorld, + repo_id: String, + package_id: String, +) { + create_package_directory_repository(world, repo_id, package_id, 2); +} + +fn create_package_directory_repository( + world: &mut CliWorld, + repo_id: String, + package_id: String, + script_count: usize, +) { let temp = world.temp.as_ref().expect("tempdir exists"); let repo_path = temp.path().join(format!("repo-{repo_id}")); let package_dir = repo_path.join(package_id.replace('/', std::path::MAIN_SEPARATOR_STR)); fs::create_dir_all(&package_dir).expect("create package dir"); fs::write( package_dir.join("metadata.jsonc"), - r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + r#"{ "type": "android:app", "display_name": "F-Droid", "android": { "package_name": "org.fdroid.fdroid" } }"#, ) .expect("write package metadata"); fs::write( package_dir.join("1.20.0.lua"), - "#!/bin/upa-lua v1\nreturn {}", + "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } } }", ) .expect("write version Lua"); + if script_count > 1 { + fs::write( + package_dir.join("9999.lua"), + "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } } }", + ) + .expect("write live version Lua"); + } world.fixture_repo_id = Some(repo_id); world.fixture_repo_path = Some(repo_path); @@ -905,6 +930,21 @@ fn command_fails_with_autogen_error(world: &mut CliWorld) { world.json = Some(json); } +#[then("the command fails with a package eval error")] +fn command_fails_with_package_eval_error(world: &mut CliWorld) { + let output = world.output.as_ref().expect("command output exists"); + assert_eq!(output.status.code(), Some(1)); + let json = parse_stdout(output); + assert_eq!(json["ok"], false); + assert_eq!(json["command"], "package eval"); + assert_eq!(json["error"]["code"], "package.eval_error"); + assert!(json["error"]["detail"] + .as_str() + .unwrap_or_default() + .contains("explicit version selection is not implemented")); + world.json = Some(json); +} + #[then("the command fails with an update check error")] fn command_fails_with_update_check_error(world: &mut CliWorld) { let output = world.output.as_ref().expect("command output exists"); @@ -1224,6 +1264,14 @@ fn output_contains_named_package(world: &mut CliWorld, package_id: String, packa assert_eq!(json["data"]["package"]["name"], package_name); } +#[then(expr = "the package eval name is {string}")] +fn package_eval_name_is(world: &mut CliWorld, package_name: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "package eval"); + assert_eq!(json["data"]["package"]["name"], package_name); +} + #[then(expr = "the pinned package version is {string}")] fn pinned_package_version_is(world: &mut CliWorld, version: String) { let json = current_json(world); diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index 695dc78..6a0838d 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -21,6 +21,9 @@ Feature: Installed app autogen And the app list contains autogen tracked package "android/app/com.example.autogen" When I run getter repo validate for autogen Then the output reports a valid repository without network + When I run getter package eval for package "android/app/com.example.autogen" + Then the command succeeds + And the package eval name is "Example Autogen" Scenario: Higher-priority repositories suppress installed app autogen candidates Given an initialized getter data directory diff --git a/crates/getter-cli/tests/features/cli/repository_package_eval.feature b/crates/getter-cli/tests/features/cli/repository_package_eval.feature index 4edefc2..172e634 100644 --- a/crates/getter-cli/tests/features/cli/repository_package_eval.feature +++ b/crates/getter-cli/tests/features/cli/repository_package_eval.feature @@ -1,5 +1,29 @@ @getter-cli @repository Feature: Getter CLI repository and package evaluation + Scenario: User adds and evaluates a package-directory repository + Given an initialized getter data directory + And a package-directory repository "official" with package "android/app/org.fdroid.fdroid" + When I run getter repo add for that repository with priority 0 + Then the command succeeds + And the output is valid JSON + And the output contains the added repository + When I run getter repo eval for that repository + Then the command succeeds + And the output is valid JSON + And the output contains the evaluated fixture package + When I run getter package eval for that fixture package + Then the command succeeds + And the output is valid JSON + And the output contains the fixture package + + Scenario: Package-directory package eval requires an unambiguous version script + Given an initialized getter data directory + And a package-directory repository "official" with multi-version package "android/app/org.fdroid.fdroid" + When I run getter repo add for that repository with priority 0 + Then the command succeeds + When I run getter package eval for that fixture package + Then the command fails with a package eval error + Scenario: User adds and evaluates a fixture Lua repository Given an initialized getter data directory And a fixture Lua repository "official" with package "android/org.fdroid.fdroid" diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index 817b310..48f36a3 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -3,7 +3,7 @@ //! Diagnostics are getter-owned DTOs used by CLI and future app bridges. They //! describe what getter observed; Flutter should only render them. -use crate::lua::{evaluate_package_file, LuaPackageError}; +use crate::lua::{evaluate_package_directory_script, evaluate_package_file, LuaPackageError}; use crate::repository::{ package_cache_key, InvalidPackageDirectory, RepositoryLayout, RepositoryLoadError, RepositoryPackageDirectoryLayout, @@ -103,12 +103,42 @@ fn validate_package_directory_repository_path(root: &Path) -> RepositoryValidati return RepositoryValidationReport::new(0, vec![repository_load_diagnostic(error)]) } }; - let diagnostics = layout + let mut diagnostics: Vec<_> = layout .invalid_packages .iter() .map(invalid_package_directory_diagnostic) .collect(); - RepositoryValidationReport::new(layout.packages.len(), diagnostics) + let mut package_count = 0usize; + let repository_id = "validation".parse().expect("static repository id is valid"); + for package in &layout.packages { + let metadata = match layout.package_metadata(package) { + Ok(metadata) => metadata, + Err(error) => { + diagnostics.push(repository_load_diagnostic(error)); + continue; + } + }; + if package.version_scripts.is_empty() { + diagnostics.push(repository_load_diagnostic( + RepositoryLoadError::MissingPackageVersionScript { + package_id: package.id.clone(), + }, + )); + continue; + } + let diagnostic_count_before = diagnostics.len(); + for script in &package.version_scripts { + if let Err(error) = + evaluate_package_directory_script(&repository_id, package, &metadata, script) + { + diagnostics.push(lua_diagnostic(error, Some(package.id.clone()))); + } + } + if diagnostics.len() == diagnostic_count_before { + package_count += 1; + } + } + RepositoryValidationReport::new(package_count, diagnostics) } fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDiagnostic { @@ -185,6 +215,38 @@ fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDi path, format!("failed to hash package file: {source}"), ), + RepositoryLoadError::ReadPackageMetadata { path, source } => ( + "package.read_metadata", + path, + format!("failed to read package metadata: {source}"), + ), + RepositoryLoadError::ParsePackageMetadata { path, source } => ( + "package.parse_metadata", + path, + format!("failed to parse package metadata: {source}"), + ), + RepositoryLoadError::InvalidPackageMetadata { path, reason } => ( + "package.metadata", + path, + reason, + ), + RepositoryLoadError::MissingPackageVersionScript { package_id } => ( + "package.missing_version_script", + PathBuf::from(format!("{package_id}/metadata.jsonc")), + format!("package '{package_id}' has no enabled version scripts"), + ), + RepositoryLoadError::AmbiguousPackageVersionScript { package_id } => ( + "package.ambiguous_version_script", + PathBuf::from(format!("{package_id}/metadata.jsonc")), + format!( + "package '{package_id}' has multiple enabled version scripts; explicit version selection is not implemented" + ), + ), + RepositoryLoadError::MissingLuaApiShebang { path, expected } => ( + "package.lua_api_shebang", + path, + format!("Lua version script is missing required shebang '{expected}'"), + ), }; diagnostic(code, message, path, None, None) } @@ -245,6 +307,13 @@ fn lua_diagnostic( LuaPackageError::Domain { path, message } => { diagnostic("package.domain", message, path, None, fallback_package_id) } + LuaPackageError::Repository { path, source } => diagnostic( + "package.repository", + source.to_string(), + path, + None, + fallback_package_id, + ), } } @@ -325,7 +394,7 @@ api_version = "getter.repo.v1" .unwrap(); fs::write( package_dir.join("1.20.0.lua"), - "#!/bin/upa-lua v1\nreturn {}", + "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } } }", ) .unwrap(); @@ -337,6 +406,52 @@ api_version = "getter.repo.v1" assert!(!report.network_required); } + #[test] + fn package_directory_without_shebang_is_reported_as_invalid() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + ) + .unwrap(); + fs::write(package_dir.join("1.20.0.lua"), "return package_version {}").unwrap(); + + let report = validate_repository_path(temp.path()); + + assert!(!report.valid); + assert_eq!(report.diagnostics[0].code, "package.repository"); + assert!(report.diagnostics[0].message.contains("#!/bin/upa-lua v1")); + } + + #[test] + fn package_directory_validator_checks_each_version_script_without_selecting_one() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + ) + .unwrap(); + fs::write( + package_dir.join("1.20.0.lua"), + "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } } }", + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } } }", + ) + .unwrap(); + + let report = validate_repository_path(temp.path()); + + assert!(report.valid, "{report:?}"); + assert_eq!(report.package_count, 1); + } + #[test] fn package_directory_metadata_error_is_stable_package_diagnostic() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index d461539..4bea4d6 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -1,8 +1,12 @@ //! Minimal Lua package-file evaluation and Rust validation boundary. -use crate::repository::RepositoryLayout; +use crate::repository::{ + PackageDirectory, PackageDirectoryMetadata, PackageLuaPermission, PackageTypeMetadata, + PackageVersionScript, RepositoryLayout, RepositoryLoadError, LUA_API_SHEBANG_V1, + REPOSITORY_LUACLASS_DIR, +}; use crate::{ - InstalledTarget, PackageId, PackagePermissions, ResolvedPackage, UpdateArtifact, + InstalledTarget, PackageId, PackagePermissions, RepositoryId, ResolvedPackage, UpdateArtifact, UpdateCandidate, }; use mlua::{Lua, Table, Value}; @@ -36,6 +40,12 @@ pub enum LuaPackageError { Schema { path: PathBuf, message: String }, #[error("domain validation failed for {path}: {message}")] Domain { path: PathBuf, message: String }, + #[error("repository layout failed for {path}: {source}")] + Repository { + path: PathBuf, + #[source] + source: RepositoryLoadError, + }, } /// Evaluate and validate a Lua package file from a repository layout. @@ -59,8 +69,49 @@ pub fn evaluate_package_source( source: &str, ) -> Result { let path = path.as_ref().to_path_buf(); + let json = evaluate_package_source_to_json( + &LuaRepositoryEnvironment::Legacy(repository), + &path, + source, + )?; + validate_package_json(repository, &path, json) +} + +pub fn evaluate_package_directory_script( + repository_id: &RepositoryId, + package: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, +) -> Result { + let source = fs::read_to_string(&script.path).map_err(|source| LuaPackageError::ReadFile { + path: script.path.clone(), + source, + })?; + if source.lines().next() != Some(LUA_API_SHEBANG_V1) { + return Err(LuaPackageError::Repository { + path: script.path.clone(), + source: RepositoryLoadError::MissingLuaApiShebang { + path: script.path.clone(), + expected: LUA_API_SHEBANG_V1, + }, + }); + } + let json = evaluate_package_source_to_json( + &LuaRepositoryEnvironment::PackageDirectory { package }, + &script.path, + &source, + )?; + validate_package_directory_version_json(repository_id, package, metadata, script, json) +} + +fn evaluate_package_source_to_json( + environment: &LuaRepositoryEnvironment<'_>, + path: &Path, + source: &str, +) -> Result { + let path = path.to_path_buf(); let lua = Lua::new(); - configure_package_path(&lua, repository).map_err(|source| LuaPackageError::Runtime { + configure_package_path(&lua, environment).map_err(|source| LuaPackageError::Runtime { path: path.clone(), source, })?; @@ -85,26 +136,55 @@ pub fn evaluate_package_source( Value::Table(table) => table, _ => return Err(LuaPackageError::NotATable { path }), }; - let json = lua_table_to_json(&path, "$", table)?; - validate_package_json(repository, &path, json) + lua_table_to_json(&path, "$", table) +} + +enum LuaRepositoryEnvironment<'a> { + Legacy(&'a RepositoryLayout), + PackageDirectory { package: &'a PackageDirectory }, } -fn configure_package_path(lua: &Lua, repository: &RepositoryLayout) -> mlua::Result<()> { +fn configure_package_path( + lua: &Lua, + environment: &LuaRepositoryEnvironment<'_>, +) -> mlua::Result<()> { let package: Table = lua.globals().get("package")?; - let lib_pattern = repository.lib_dir.join("?.lua"); - let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); - let new_path = format!( - "{};{}", - lib_pattern.to_string_lossy(), - nested_lib_pattern.to_string_lossy() - ); - package.set("path", new_path)?; package.set("cpath", "")?; package.set("loadlib", Value::Nil)?; - install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone())?; + match environment { + LuaRepositoryEnvironment::Legacy(repository) => { + let lib_pattern = repository.lib_dir.join("?.lua"); + let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); + let new_path = format!( + "{};{}", + lib_pattern.to_string_lossy(), + nested_lib_pattern.to_string_lossy() + ); + package.set("path", new_path)?; + install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone())?; + } + LuaRepositoryEnvironment::PackageDirectory { + package: package_dir, + } => { + package.set("path", "")?; + install_luaclass_prefix_searcher( + lua, + &package, + package_directory_repository_root(package_dir).join(REPOSITORY_LUACLASS_DIR), + )?; + } + } disable_native_module_searchers(&package) } +fn package_directory_repository_root(package: &PackageDirectory) -> PathBuf { + let mut root = package.path.clone(); + for _ in package.id.to_string().split('/') { + root.pop(); + } + root +} + fn disable_native_module_searchers(package: &Table) -> mlua::Result<()> { let searchers = package_searchers(package)?; let len = searchers.raw_len(); @@ -128,24 +208,54 @@ fn remove_unsafe_globals(lua: &Lua) -> mlua::Result<()> { } fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> mlua::Result<()> { + install_prefixed_file_searcher( + lua, + package, + "lib.", + lib_dir, + "constrained repository lib searcher only handles lib.* modules", + ) +} + +fn install_luaclass_prefix_searcher( + lua: &Lua, + package: &Table, + luaclass_dir: PathBuf, +) -> mlua::Result<()> { + install_prefixed_file_searcher( + lua, + package, + "luaclass.", + luaclass_dir, + "constrained repository luaclass searcher only handles luaclass.* modules", + ) +} + +fn install_prefixed_file_searcher( + lua: &Lua, + package: &Table, + prefix: &'static str, + module_dir: PathBuf, + wrong_prefix_message: &'static str, +) -> mlua::Result<()> { let searchers = package_searchers(package)?; let searcher = lua.create_function(move |lua, module: String| { - let Some(module) = module.strip_prefix("lib.") else { + let Some(module) = module.strip_prefix(prefix) else { return lua - .create_string("\n\tconstrained repository lib searcher only handles lib.* modules") + .create_string(format!("\n\t{wrong_prefix_message}")) .map(Value::String); }; let Some(relative_module) = module_to_relative_path(module) else { return lua .create_string(format!( - "\n\tinvalid repository lib module name 'lib.{module}'" + "\n\tinvalid repository module name '{prefix}{module}'" )) .map(Value::String); }; - let module_path = lib_dir.join(&relative_module).with_extension("lua"); - let init_path = lib_dir.join(&relative_module).join("init.lua"); + let module_path = module_dir.join(&relative_module).with_extension("lua"); + let init_path = module_dir.join(&relative_module).join("init.lua"); for candidate in [&module_path, &init_path] { if candidate.is_file() { let source = fs::read_to_string(candidate).map_err(mlua::Error::external)?; @@ -157,8 +267,8 @@ fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> } lua.create_string(format!( - "\n\tno repository lib module 'lib.{module}' in {}", - lib_dir.display() + "\n\tno repository module '{prefix}{module}' in {}", + module_dir.display() )) .map(Value::String) })?; @@ -201,6 +311,7 @@ fn module_to_relative_path(module: &str) -> Option { fn install_helpers(lua: &Lua) -> mlua::Result<()> { let package_fn = lua.create_function(|_, table: Table| Ok(table))?; lua.globals().set("package_def", package_fn.clone())?; + lua.globals().set("package_version", package_fn.clone())?; lua.globals().set("android_app", package_fn.clone())?; lua.globals().set("magisk_module", package_fn.clone())?; lua.globals().set("generic_package", package_fn)?; @@ -337,15 +448,115 @@ fn validate_package_json( } let name = required_string(path, object, "name")?.to_owned(); + package_from_version_json( + repository.metadata.id.clone(), + id, + name, + PackagePermissions::default(), + path, + object, + ) +} + +fn validate_package_directory_version_json( + repository_id: &RepositoryId, + package: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, + value: JsonValue, +) -> Result { + let object = value.as_object().ok_or_else(|| LuaPackageError::Schema { + path: script.path.clone(), + message: "package value must be an object".to_owned(), + })?; + if object.contains_key("id") { + return Err(LuaPackageError::Schema { + path: script.path.clone(), + message: "field 'id' must not be declared by package version scripts; package id is derived from path".to_owned(), + }); + } + let name = object + .get("name") + .and_then(JsonValue::as_str) + .map(str::to_owned) + .unwrap_or_else(|| metadata.display_name_for(&package.id)); + let mut package = package_from_version_json( + repository_id.clone(), + package.id.clone(), + name, + metadata_permissions_for_script(metadata, &script.file_name), + &script.path, + object, + )?; + apply_metadata_installed_target(&mut package, metadata, &script.path)?; + Ok(package) +} + +fn apply_metadata_installed_target( + package: &mut ResolvedPackage, + metadata: &PackageDirectoryMetadata, + path: &Path, +) -> Result<(), LuaPackageError> { + let metadata_target = metadata_installed_target(metadata); + if package.installed.is_empty() { + package.installed.push(metadata_target); + return Ok(()); + } + if package.installed == [metadata_target] { + return Ok(()); + } + Err(LuaPackageError::Domain { + path: path.to_path_buf(), + message: "field 'installed' must match package metadata identity".to_owned(), + }) +} + +fn metadata_installed_target(metadata: &PackageDirectoryMetadata) -> InstalledTarget { + match &metadata.package { + PackageTypeMetadata::AndroidApp { android } => InstalledTarget::AndroidPackage { + package_name: android.package_name.clone(), + }, + PackageTypeMetadata::MagiskModule { magisk } => InstalledTarget::MagiskModule { + module_id: magisk.module_id.clone(), + }, + PackageTypeMetadata::Generic { generic } => InstalledTarget::Generic { + id: generic.id.clone(), + }, + } +} + +fn metadata_permissions_for_script( + metadata: &PackageDirectoryMetadata, + file_name: &str, +) -> PackagePermissions { + PackagePermissions { + free_network: metadata + .permissions_for(file_name) + .iter() + .any(|permission| *permission == PackageLuaPermission::AllowFreeNetwork), + } +} + +fn package_from_version_json( + repository: RepositoryId, + id: PackageId, + name: String, + default_permissions: PackagePermissions, + path: &Path, + object: &Map, +) -> Result { let installed = parse_installed_targets(path, object.get("installed"))?; - let permissions = parse_permissions(path, object.get("permissions"))?; + let permissions = match object.get("permissions") { + Some(value) => parse_permissions(path, Some(value))?, + None => default_permissions, + }; let source_priority = parse_string_array(path, "source_priority", object.get("source_priority"))?; let updates = parse_update_candidates(path, object.get("updates"))?; Ok(ResolvedPackage { id, - repository: repository.metadata.id.clone(), + repository, name, installed, permissions, @@ -516,7 +727,8 @@ fn parse_string_array( #[cfg(test)] mod tests { use super::*; - use crate::repository::RepositoryLayout; + use crate::repository::{RepositoryLayout, RepositoryPackageDirectoryLayout}; + use crate::RepositoryId; use std::fs; fn fixture_repo() -> (tempfile::TempDir, RepositoryLayout, PathBuf) { @@ -596,6 +808,199 @@ return package_def { ); } + #[test] + fn evaluates_package_directory_version_script_from_metadata_identity() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" }, + "lua": { "9999.lua": { "permission": ["allow_free_network"] } } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { + installed = { + { kind = "android_package", package_name = "com.example.autogen" }, + }, + updates = { + { + version = "1.2.0", + artifacts = { + { name = "app.apk", url = "https://example.invalid/app.apk", file_name = "app.apk" }, + }, + }, + }, +} +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let package = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap(); + + assert_eq!(package.id.to_string(), "android/app/com.example.autogen"); + assert_eq!(package.repository.as_str(), "autogen"); + assert_eq!(package.name, "Example Autogen"); + assert!(package.permissions.free_network); + assert_eq!(package.installed.len(), 1); + assert_eq!(package.updates[0].version, "1.2.0"); + } + + #[test] + fn rejects_package_directory_installed_target_mismatch() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { + installed = { + { kind = "android_package", package_name = "com.other.app" }, + }, +} +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let err = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap_err(); + + assert!(matches!(err, LuaPackageError::Domain { .. })); + assert!(err.to_string().contains("metadata identity")); + } + + #[test] + fn rejects_package_directory_version_script_id_field() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { id = "android/app/com.example.autogen" } +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let err = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap_err(); + + assert!(matches!(err, LuaPackageError::Schema { .. })); + assert!(err.to_string().contains("must not be declared")); + } + + #[test] + fn package_directory_can_load_luaclass_modules() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::create_dir_all(temp.path().join("luaclass")).unwrap(); + fs::write( + temp.path().join("luaclass/android.lua"), + r#" +return { + package_version = function(input) + return { + installed = input.installed, + updates = input.updates, + } + end +} +"#, + ) + .unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local android = require("luaclass.android") +return android.package_version { + installed = { + { kind = "android_package", package_name = "com.example.autogen" }, + }, +} +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let package = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap(); + + assert_eq!(package.name, "Example Autogen"); + assert_eq!(package.installed.len(), 1); + } + #[test] fn rejects_package_id_that_does_not_match_path() { let (_temp, layout, package_path) = fixture_repo(); diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index fd16ef5..f85c81a 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -2,6 +2,7 @@ use crate::{PackageId, PackageIdError, RepositoryId, RepositoryIdError, RepositoryPriority}; use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha512}; use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; @@ -16,7 +17,9 @@ pub const REPOSITORY_ROOT_METADATA_VERSION: u32 = 1; pub const REPOSITORY_SELF_METADATA_DIR: &str = ".metadata"; pub const REPOSITORY_LUACLASS_DIR: &str = "luaclass"; pub const PACKAGE_METADATA_FILE: &str = "metadata.jsonc"; +pub const PACKAGE_MANIFEST_FILE: &str = "Manifest"; pub const LUA_SCRIPT_EXTENSION: &str = "lua"; +pub const LUA_API_SHEBANG_V1: &str = "#!/bin/upa-lua v1"; pub const LOCAL_REPOSITORY_ALIAS: &str = "local"; pub const DEFAULT_GENERATED_REPOSITORY_ALIAS: &str = "autogen"; @@ -253,6 +256,54 @@ pub struct PackageDirectory { pub version_scripts: Vec, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PackageDirectoryMetadata { + #[serde(default)] + pub display_name: Option, + #[serde(default)] + pub lua: HashMap, + #[serde(flatten)] + pub package: PackageTypeMetadata, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum PackageTypeMetadata { + #[serde(rename = "android:app")] + AndroidApp { android: AndroidPackageMetadata }, + #[serde(rename = "magisk:module")] + MagiskModule { magisk: MagiskPackageMetadata }, + #[serde(rename = "generic")] + Generic { generic: GenericPackageMetadata }, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AndroidPackageMetadata { + pub package_name: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct MagiskPackageMetadata { + pub module_id: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GenericPackageMetadata { + pub id: String, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct PackageLuaMetadata { + #[serde(default)] + pub permission: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum PackageLuaPermission { + AllowFreeNetwork, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct PackageVersionScript { pub version: String, @@ -354,6 +405,29 @@ pub enum RepositoryLoadError { #[source] source: std::io::Error, }, + #[error("failed to read package metadata at {path}: {source}")] + ReadPackageMetadata { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("failed to parse package metadata at {path}: {source}")] + ParsePackageMetadata { + path: PathBuf, + #[source] + source: serde_json::Error, + }, + #[error("invalid package metadata at {path}: {reason}")] + InvalidPackageMetadata { path: PathBuf, reason: String }, + #[error("package '{package_id}' has no enabled version scripts")] + MissingPackageVersionScript { package_id: PackageId }, + #[error("package '{package_id}' has multiple enabled version scripts; explicit version selection is not implemented")] + AmbiguousPackageVersionScript { package_id: PackageId }, + #[error("Lua version script {path} is missing required shebang '{expected}'")] + MissingLuaApiShebang { + path: PathBuf, + expected: &'static str, + }, } impl RepositoryPackageDirectoryLayout { @@ -374,6 +448,43 @@ impl RepositoryPackageDirectoryLayout { pub fn package(&self, id: &PackageId) -> Option<&PackageDirectory> { self.packages.iter().find(|package| &package.id == id) } + + pub fn package_metadata( + &self, + package: &PackageDirectory, + ) -> Result { + load_package_metadata(&package.metadata_path) + } + + pub fn unambiguous_version_script<'a>( + &self, + package: &'a PackageDirectory, + ) -> Result<&'a PackageVersionScript, RepositoryLoadError> { + match package.version_scripts.as_slice() { + [script] => Ok(script), + [] => Err(RepositoryLoadError::MissingPackageVersionScript { + package_id: package.id.clone(), + }), + _ => Err(RepositoryLoadError::AmbiguousPackageVersionScript { + package_id: package.id.clone(), + }), + } + } +} + +impl PackageDirectoryMetadata { + pub fn display_name_for(&self, package_id: &PackageId) -> String { + self.display_name + .clone() + .unwrap_or_else(|| package_id.to_string()) + } + + pub fn permissions_for(&self, file_name: &str) -> &[PackageLuaPermission] { + self.lua + .get(file_name) + .map(|metadata| metadata.permission.as_slice()) + .unwrap_or(&[]) + } } impl RepositoryLayout { @@ -551,17 +662,37 @@ fn collect_package_boundary( } fn parse_package_metadata(metadata_path: &Path) -> Result<(), String> { - let bytes = fs::read(metadata_path) - .map_err(|source| format!("failed to read package metadata: {source}"))?; - let value = serde_json::from_reader::<_, serde_json::Value>(json_comments::StripComments::new( + load_package_metadata(metadata_path) + .map(|_| ()) + .map_err(|source| source.to_string()) +} + +pub fn load_package_metadata( + metadata_path: impl AsRef, +) -> Result { + let metadata_path = metadata_path.as_ref(); + let bytes = + fs::read(metadata_path).map_err(|source| RepositoryLoadError::ReadPackageMetadata { + path: metadata_path.to_path_buf(), + source, + })?; + let value: serde_json::Value = serde_json::from_reader(json_comments::StripComments::new( bytes.as_slice(), )) - .map_err(|source| format!("failed to parse package metadata: {source}"))?; - if value.is_object() { - Ok(()) - } else { - Err("package metadata must be a JSON object".to_owned()) + .map_err(|source| RepositoryLoadError::ParsePackageMetadata { + path: metadata_path.to_path_buf(), + source, + })?; + if !value.is_object() { + return Err(RepositoryLoadError::InvalidPackageMetadata { + path: metadata_path.to_path_buf(), + reason: "package metadata must be a JSON object".to_owned(), + }); } + serde_json::from_value(value).map_err(|source| RepositoryLoadError::ParsePackageMetadata { + path: metadata_path.to_path_buf(), + source, + }) } fn discover_version_scripts( @@ -704,22 +835,42 @@ pub fn package_cache_key( }) } +pub fn package_directory_cache_key( + repository_id: &RepositoryId, + package: &PackageDirectory, + script: &PackageVersionScript, +) -> Result { + let package_file_hash = format!( + "metadata={};script={};manifest={}", + package_file_content_hash(&package.metadata_path)?, + package_file_content_hash(&script.path)?, + optional_file_content_hash(&package.path.join(PACKAGE_MANIFEST_FILE))? + .unwrap_or_else(|| "missing".to_owned()) + ); + Ok(RepositoryPackageCacheKey { + repository_id: repository_id.clone(), + package_id: package.id.clone(), + api_version: REPO_API_VERSION_V1.to_owned(), + package_file_hash, + }) +} + +fn optional_file_content_hash(path: &Path) -> Result, RepositoryLoadError> { + if path.exists() { + package_file_content_hash(path).map(Some) + } else { + Ok(None) + } +} + pub fn package_file_content_hash(path: impl AsRef) -> Result { let path = path.as_ref(); let bytes = fs::read(path).map_err(|source| RepositoryLoadError::HashPackageFile { path: path.to_path_buf(), source, })?; - Ok(format!("{:016x}", fnv1a64(&bytes))) -} - -fn fnv1a64(bytes: &[u8]) -> u64 { - let mut hash = 0xcbf29ce484222325u64; - for byte in bytes { - hash ^= u64::from(*byte); - hash = hash.wrapping_mul(0x100000001b3); - } - hash + let hash = Sha512::digest(&bytes); + Ok(format!("sha512:{hash:x}")) } pub fn highest_priority(items: &[T], priority: F) -> Option<&T> @@ -906,7 +1057,7 @@ mod tests { fs::create_dir_all(package_dir.join("nested")).unwrap(); fs::write( package_dir.join(PACKAGE_METADATA_FILE), - r#"{ "type": "android:app" }"#, + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, ) .unwrap(); fs::write(package_dir.join("1.2.3.lua"), "return {}").unwrap(); @@ -944,7 +1095,7 @@ mod tests { fs::write(package_dir.join(PACKAGE_METADATA_FILE), "{not-json").unwrap(); fs::write( package_dir.join("nested/android/app/hidden/metadata.jsonc"), - r#"{ "type": "android:app" }"#, + r#"{ "type": "android:app", "android": { "package_name": "hidden" } }"#, ) .unwrap(); @@ -968,19 +1119,19 @@ mod tests { fs::create_dir_all(root.join(".metadata/android/app/hidden")).unwrap(); fs::write( root.join(".metadata/android/app/hidden/metadata.jsonc"), - r#"{ "type": "android:app" }"#, + r#"{ "type": "android:app", "android": { "package_name": "hidden" } }"#, ) .unwrap(); fs::create_dir_all(root.join("luaclass/android/app/hidden")).unwrap(); fs::write( root.join("luaclass/android/app/hidden/metadata.jsonc"), - r#"{ "type": "android:app" }"#, + r#"{ "type": "android:app", "android": { "package_name": "hidden" } }"#, ) .unwrap(); fs::create_dir_all(root.join("android/app/visible")).unwrap(); fs::write( root.join("android/app/visible/metadata.jsonc"), - r#"{ "type": "android:app" }"#, + r#"{ "type": "android:app", "android": { "package_name": "visible" } }"#, ) .unwrap(); @@ -1014,10 +1165,9 @@ mod tests { assert!(layout.packages.is_empty()); assert_eq!(layout.invalid_packages.len(), 1); - assert_eq!( - layout.invalid_packages[0].reason, - "package metadata must be a JSON object" - ); + assert!(layout.invalid_packages[0] + .reason + .contains("package metadata must be a JSON object")); } #[test] @@ -1027,7 +1177,7 @@ mod tests { fs::create_dir_all(root.join("invalidkind/app/example")).unwrap(); fs::write( root.join("invalidkind/app/example/metadata.jsonc"), - r#"{ "type": "android:app" }"#, + r#"{ "type": "android:app", "android": { "package_name": "example" } }"#, ) .unwrap(); diff --git a/crates/getter-operations/src/read_model.rs b/crates/getter-operations/src/read_model.rs index 898864b..79f5cad 100644 --- a/crates/getter-operations/src/read_model.rs +++ b/crates/getter-operations/src/read_model.rs @@ -6,9 +6,11 @@ //! to parse and render. #[cfg(feature = "lua")] -use getter_core::lua::evaluate_package_file; +use getter_core::lua::{evaluate_package_directory_script, evaluate_package_file}; #[cfg(feature = "lua")] -use getter_core::repository::{RepositoryLayout, RepositoryLoadError}; +use getter_core::repository::{ + RepositoryLayout, RepositoryLoadError, RepositoryPackageDirectoryLayout, +}; #[cfg(feature = "lua")] use getter_core::{PackageId, RepositoryId}; use getter_storage::{MainDb, StorageError, StoredRepository, StoredTrackedPackage}; @@ -140,16 +142,12 @@ fn evaluate_package_from_repo( package_id: &PackageId, ) -> Result { let repo = find_repository(db, repo_id)?; - let path = repo_path(&repo)?; - let layout = RepositoryLayout::load(&path)?; - let package_file = layout.package_file(package_id).ok_or_else(|| { + evaluate_package_in_repository(&repo, package_id)?.ok_or_else(|| { ReadModelOperationError::PackageEval(format!( "package '{}' was not found in repository '{}'", package_id, repo_id )) - })?; - evaluate_package_file(&layout, &package_file.path) - .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())) + }) } #[cfg(feature = "lua")] @@ -158,11 +156,8 @@ fn evaluate_highest_priority_package( package_id: &PackageId, ) -> Result { for repo in db.repositories()? { - let path = repo_path(&repo)?; - let layout = RepositoryLayout::load(&path)?; - if let Some(package_file) = layout.package_file(package_id) { - return evaluate_package_file(&layout, &package_file.path) - .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())); + if let Some(package) = evaluate_package_in_repository(&repo, package_id)? { + return Ok(package); } } Err(ReadModelOperationError::PackageEval(format!( @@ -170,6 +165,33 @@ fn evaluate_highest_priority_package( ))) } +#[cfg(feature = "lua")] +fn evaluate_package_in_repository( + repo: &StoredRepository, + package_id: &PackageId, +) -> Result, ReadModelOperationError> { + let path = repo_path(repo)?; + if path.join("repo.toml").is_file() { + let layout = RepositoryLayout::load(&path)?; + let Some(package_file) = layout.package_file(package_id) else { + return Ok(None); + }; + return evaluate_package_file(&layout, &package_file.path) + .map(Some) + .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())); + } + + let layout = RepositoryPackageDirectoryLayout::load(&path)?; + let Some(package) = layout.package(package_id) else { + return Ok(None); + }; + let metadata = layout.package_metadata(package)?; + let script = layout.unambiguous_version_script(package)?; + evaluate_package_directory_script(&repo.id, package, &metadata, script) + .map(Some) + .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())) +} + #[cfg(feature = "lua")] fn find_repository( db: &MainDb, @@ -274,6 +296,68 @@ mod tests { fn package_eval_reads_registered_lua_repository() { let temp = tempdir().unwrap(); let repo_path = temp.path().join("repo"); + write_legacy_lua_repo(&repo_path); + + let db = MainDb::open(temp.path().join(MAIN_DB_FILE)).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::new(10), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + ) + .unwrap(); + + let result = package_eval_json( + temp.path(), + r#"{"package_id":"android/org.fdroid.fdroid","repository_id":"official"}"#, + ) + .unwrap(); + assert_eq!(result["package"]["id"], "android/org.fdroid.fdroid"); + assert_eq!(result["package"]["name"], "F-Droid"); + assert_eq!(result["package"]["permissions"]["free_network"], true); + } + + #[cfg(feature = "lua")] + #[test] + fn package_eval_reads_registered_package_directory_repository() { + let temp = tempdir().unwrap(); + let repo_path = temp.path().join("repo"); + write_package_directory_repo(&repo_path); + + let db = MainDb::open(temp.path().join(MAIN_DB_FILE)).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: "autogen".parse().unwrap(), + name: "Autogen".to_owned(), + priority: RepositoryPriority::new(-1), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_path), + None, + ) + .unwrap(); + + let result = package_eval_json( + temp.path(), + r#"{"package_id":"android/app/com.example.autogen","repository_id":"autogen"}"#, + ) + .unwrap(); + + assert_eq!(result["package"]["id"], "android/app/com.example.autogen"); + assert_eq!(result["package"]["repository"], "autogen"); + assert_eq!(result["package"]["name"], "Example Autogen"); + assert_eq!( + result["package"]["installed"][0]["package_name"], + "com.example.autogen" + ); + } + + #[cfg(feature = "lua")] + fn write_legacy_lua_repo(repo_path: &Path) { fs::create_dir_all(repo_path.join("packages/android")).unwrap(); fs::create_dir_all(repo_path.join("lib")).unwrap(); fs::create_dir_all(repo_path.join("templates")).unwrap(); @@ -299,27 +383,32 @@ api_version = "getter.repo.v1" "#, ) .unwrap(); + } - let db = MainDb::open(temp.path().join(MAIN_DB_FILE)).unwrap(); - db.upsert_repository( - &RepositoryMetadata { - id: "official".parse().unwrap(), - name: "Official".to_owned(), - priority: RepositoryPriority::new(10), - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(&repo_path), - None, + #[cfg(feature = "lua")] + fn write_package_directory_repo(repo_path: &Path) { + let package_dir = repo_path.join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, ) .unwrap(); - - let result = package_eval_json( - temp.path(), - r#"{"package_id":"android/org.fdroid.fdroid","repository_id":"official"}"#, + fs::write(package_dir.join("Manifest"), "").unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { + installed = { + { kind = "android_package", package_name = "com.example.autogen" }, + }, +} +"#, ) .unwrap(); - assert_eq!(result["package"]["id"], "android/org.fdroid.fdroid"); - assert_eq!(result["package"]["name"], "F-Droid"); - assert_eq!(result["package"]["permissions"]["free_network"], true); } } diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 1392322..d9e53f1 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -8,8 +8,11 @@ #[cfg(feature = "lua")] use getter_core::{ - lua::evaluate_package_file, - repository::{package_cache_key, RepositoryLayout, RepositoryLoadError}, + lua::{evaluate_package_directory_script, evaluate_package_file}, + repository::{ + package_cache_key, package_directory_cache_key, RepositoryLayout, RepositoryLoadError, + RepositoryPackageDirectoryLayout, + }, }; use getter_core::{ runtime::{ @@ -383,13 +386,31 @@ fn evaluate_registered_package( continue; }; let root = PathBuf::from(root); - let layout = RepositoryLayout::load(&root)?; - let Some(package_file) = layout.package_file(&request.package_id) else { + if root.join("repo.toml").is_file() { + let layout = RepositoryLayout::load(&root)?; + let Some(package_file) = layout.package_file(&request.package_id) else { + continue; + }; + let package = evaluate_package_file(&layout, &package_file.path) + .map_err(|source| RuntimeOperationError::PackageEval(source.to_string()))?; + let cache_key = package_cache_key(&layout, package_file)?; + let dependency_digest = format!( + "repo:{}:package:{}:hash:{}", + cache_key.repository_id, cache_key.package_id, cache_key.package_file_hash + ); + return Ok((package, dependency_digest)); + } + + let layout = RepositoryPackageDirectoryLayout::load(&root)?; + let Some(package_directory) = layout.package(&request.package_id) else { continue; }; - let package = evaluate_package_file(&layout, &package_file.path) - .map_err(|source| RuntimeOperationError::PackageEval(source.to_string()))?; - let cache_key = package_cache_key(&layout, package_file)?; + let metadata = layout.package_metadata(package_directory)?; + let script = layout.unambiguous_version_script(package_directory)?; + let package = + evaluate_package_directory_script(&repository.id, package_directory, &metadata, script) + .map_err(|source| RuntimeOperationError::PackageEval(source.to_string()))?; + let cache_key = package_directory_cache_key(&repository.id, package_directory, script)?; let dependency_digest = format!( "repo:{}:package:{}:hash:{}", cache_key.repository_id, cache_key.package_id, cache_key.package_file_hash @@ -484,6 +505,47 @@ mod tests { assert_eq!(submitted["package_id"], "android/org.fdroid.fdroid"); } + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_issues_action_from_package_directory_static_updates() { + let temp = tempfile::tempdir().unwrap(); + let repo_root = temp.path().join("repo"); + write_package_directory_static_update_repo(&repo_root); + let db = MainDb::open_in_memory().unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: "autogen".parse().unwrap(), + name: "Autogen".to_owned(), + priority: RepositoryPriority::new(-1), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_root), + None, + ) + .unwrap(); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + &db, + &json!({ + "package_id": "android/app/com.example.autogen", + "installed_version": "1.0.0" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["package"]["repository"], "autogen"); + assert_eq!(issued["package"]["name"], "Example Autogen"); + assert_eq!(issued["update"]["status"], "update_available"); + let action_id = issued["action"]["action_id"].as_str().unwrap(); + let submitted = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap(); + assert_eq!(submitted["package_id"], "android/app/com.example.autogen"); + } + #[cfg(feature = "lua")] #[test] fn registered_package_update_check_without_update_does_not_issue_action() { @@ -682,6 +744,42 @@ return package_def { .unwrap(); } + #[cfg(feature = "lua")] + fn write_package_directory_static_update_repo(root: &std::path::Path) { + let package_dir = root.join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write(package_dir.join("Manifest"), "").unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { + updates = { + { + version = "1.2.0", + artifacts = { + { + name = "app.apk", + url = "https://example.invalid/app.apk", + file_name = "app.apk", + }, + }, + }, + }, +} +"#, + ) + .unwrap(); + } + fn update_fixture( package_id: &str, installed_version: Option<&str>, From 15788b42a6c786ce86de80af744e72662e1e3e9d Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 02:08:03 +0800 Subject: [PATCH 32/71] fix(core): satisfy clippy for Lua errors --- crates/getter-core/src/lua.rs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 4bea4d6..53cd121 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -26,7 +26,7 @@ pub enum LuaPackageError { Runtime { path: PathBuf, #[source] - source: mlua::Error, + source: Box, }, #[error("Lua package {path} did not return a table")] NotATable { path: PathBuf }, @@ -44,7 +44,7 @@ pub enum LuaPackageError { Repository { path: PathBuf, #[source] - source: RepositoryLoadError, + source: Box, }, } @@ -90,10 +90,10 @@ pub fn evaluate_package_directory_script( if source.lines().next() != Some(LUA_API_SHEBANG_V1) { return Err(LuaPackageError::Repository { path: script.path.clone(), - source: RepositoryLoadError::MissingLuaApiShebang { + source: Box::new(RepositoryLoadError::MissingLuaApiShebang { path: script.path.clone(), expected: LUA_API_SHEBANG_V1, - }, + }), }); } let json = evaluate_package_source_to_json( @@ -113,15 +113,15 @@ fn evaluate_package_source_to_json( let lua = Lua::new(); configure_package_path(&lua, environment).map_err(|source| LuaPackageError::Runtime { path: path.clone(), - source, + source: Box::new(source), })?; remove_unsafe_globals(&lua).map_err(|source| LuaPackageError::Runtime { path: path.clone(), - source, + source: Box::new(source), })?; install_helpers(&lua).map_err(|source| LuaPackageError::Runtime { path: path.clone(), - source, + source: Box::new(source), })?; let value = lua @@ -130,7 +130,7 @@ fn evaluate_package_source_to_json( .eval::() .map_err(|source| LuaPackageError::Runtime { path: path.clone(), - source, + source: Box::new(source), })?; let table = match value { Value::Table(table) => table, @@ -325,13 +325,13 @@ fn lua_table_to_json( ) -> Result { if is_array_table(&table).map_err(|source| LuaPackageError::Runtime { path: path.to_path_buf(), - source, + source: Box::new(source), })? { let mut array = Vec::new(); for pair in table.sequence_values::() { let value = pair.map_err(|source| LuaPackageError::Runtime { path: path.to_path_buf(), - source, + source: Box::new(source), })?; array.push(lua_value_to_json(path, &format!("{location}[]"), value)?); } @@ -341,14 +341,14 @@ fn lua_table_to_json( for pair in table.pairs::() { let (key, value) = pair.map_err(|source| LuaPackageError::Runtime { path: path.to_path_buf(), - source, + source: Box::new(source), })?; let key = match key { Value::String(value) => value .to_str() .map_err(|source| LuaPackageError::Runtime { path: path.to_path_buf(), - source, + source: Box::new(source), })? .to_owned(), Value::Integer(value) => value.to_string(), @@ -388,7 +388,7 @@ fn lua_value_to_json( .to_str() .map_err(|source| LuaPackageError::Runtime { path: path.to_path_buf(), - source, + source: Box::new(source), })? .to_owned(), )), @@ -532,8 +532,7 @@ fn metadata_permissions_for_script( PackagePermissions { free_network: metadata .permissions_for(file_name) - .iter() - .any(|permission| *permission == PackageLuaPermission::AllowFreeNetwork), + .contains(&PackageLuaPermission::AllowFreeNetwork), } } From 091bb9fde26187fa01da008a9ce1faf3b2411fcc Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 02:17:58 +0800 Subject: [PATCH 33/71] feat(lua): expose package-local file reads --- crates/getter-core/src/lua.rs | 173 +++++++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 2 deletions(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 53cd121..8d6f74e 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -119,7 +119,7 @@ fn evaluate_package_source_to_json( path: path.clone(), source: Box::new(source), })?; - install_helpers(&lua).map_err(|source| LuaPackageError::Runtime { + install_helpers(&lua, environment).map_err(|source| LuaPackageError::Runtime { path: path.clone(), source: Box::new(source), })?; @@ -308,16 +308,69 @@ fn module_to_relative_path(module: &str) -> Option { } } -fn install_helpers(lua: &Lua) -> mlua::Result<()> { +fn install_helpers(lua: &Lua, environment: &LuaRepositoryEnvironment<'_>) -> mlua::Result<()> { let package_fn = lua.create_function(|_, table: Table| Ok(table))?; lua.globals().set("package_def", package_fn.clone())?; lua.globals().set("package_version", package_fn.clone())?; lua.globals().set("android_app", package_fn.clone())?; lua.globals().set("magisk_module", package_fn.clone())?; lua.globals().set("generic_package", package_fn)?; + if let LuaRepositoryEnvironment::PackageDirectory { package } = environment { + install_package_file_helpers(lua, package)?; + } Ok(()) } +fn install_package_file_helpers(lua: &Lua, package: &PackageDirectory) -> mlua::Result<()> { + let files_root = package.path.join("files"); + let read_package_file = lua.create_function(move |lua, requested_path: String| { + let relative_path = package_file_relative_path(&requested_path)?; + let path = files_root.join(relative_path); + let metadata = fs::metadata(&path).map_err(mlua::Error::external)?; + if !metadata.is_file() { + return Err(mlua::Error::external(format!( + "read_package_file target {} is not a file", + path.display() + ))); + } + let bytes = fs::read(&path).map_err(mlua::Error::external)?; + lua.create_string(&bytes) + })?; + + let globals = lua.globals(); + let getter_builtin = match globals.get::("getter_builtin")? { + Value::Table(table) => table, + Value::Nil => { + let table = lua.create_table()?; + globals.set("getter_builtin", table.clone())?; + table + } + _ => return Err(mlua::Error::external("getter_builtin must be a table")), + }; + getter_builtin.set("read_package_file", read_package_file.clone())?; + globals.set("read_package_file", read_package_file) +} + +fn package_file_relative_path(path: &str) -> mlua::Result { + let mut relative = PathBuf::new(); + for component in Path::new(path).components() { + match component { + std::path::Component::Normal(part) => relative.push(part), + _ => { + return Err(mlua::Error::external( + "read_package_file path must be relative to files/ and must not contain special path components", + )) + } + } + } + if relative.as_os_str().is_empty() { + return Err(mlua::Error::external( + "read_package_file path must not be empty", + )); + } + Ok(relative) +} + fn lua_table_to_json( path: &Path, location: &str, @@ -1000,6 +1053,122 @@ return android.package_version { assert_eq!(package.installed.len(), 1); } + #[test] + fn package_directory_can_read_package_local_files() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(package_dir.join("files/nested")).unwrap(); + fs::write(package_dir.join("files/nested/data.txt"), b"hello\xff").unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local body = read_package_file("nested/data.txt") +return package_version { name = "bytes:" .. tostring(#body) } +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let package = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap(); + + assert_eq!(package.name, "bytes:6"); + } + + #[test] + fn read_package_file_rejects_paths_outside_package_files() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(package_dir.join("files")).unwrap(); + fs::write(package_dir.join("outside.txt"), "outside").unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local ok = pcall(read_package_file, "../outside.txt") +return package_version { name = ok and "leaked" or "blocked" } +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let package = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap(); + + assert_eq!(package.name, "blocked"); + } + + #[test] + fn getter_builtin_exposes_package_file_reader_to_package_directory_lua() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(package_dir.join("files")).unwrap(); + fs::write(package_dir.join("files/name.txt"), "from builtin").unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { name = getter_builtin.read_package_file("name.txt") } +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + + let package = evaluate_package_directory_script( + &RepositoryId::new("autogen").unwrap(), + package, + &metadata, + script, + ) + .unwrap(); + + assert_eq!(package.name, "from builtin"); + } + #[test] fn rejects_package_id_that_does_not_match_path() { let (_temp, layout, package_path) = fixture_repo(); From 139d1ff02b80ce64f753f1b24e8fbe7e4111a96d Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 02:24:59 +0800 Subject: [PATCH 34/71] fix(repository): hash package-local file inputs --- crates/getter-core/src/diagnostics.rs | 3 + crates/getter-core/src/repository.rs | 143 ++++++++++++++++++++++++-- 2 files changed, 139 insertions(+), 7 deletions(-) diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index 48f36a3..a78ff21 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -215,6 +215,9 @@ fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDi path, format!("failed to hash package file: {source}"), ), + RepositoryLoadError::InvalidPackageLocalFile { path, reason } => { + ("package.local_file", path, reason) + } RepositoryLoadError::ReadPackageMetadata { path, source } => ( "package.read_metadata", path, diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index f85c81a..f5f0e99 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -18,6 +18,7 @@ pub const REPOSITORY_SELF_METADATA_DIR: &str = ".metadata"; pub const REPOSITORY_LUACLASS_DIR: &str = "luaclass"; pub const PACKAGE_METADATA_FILE: &str = "metadata.jsonc"; pub const PACKAGE_MANIFEST_FILE: &str = "Manifest"; +pub const PACKAGE_LOCAL_FILES_DIR: &str = "files"; pub const LUA_SCRIPT_EXTENSION: &str = "lua"; pub const LUA_API_SHEBANG_V1: &str = "#!/bin/upa-lua v1"; pub const LOCAL_REPOSITORY_ALIAS: &str = "local"; @@ -405,6 +406,8 @@ pub enum RepositoryLoadError { #[source] source: std::io::Error, }, + #[error("invalid package-local file {path}: {reason}")] + InvalidPackageLocalFile { path: PathBuf, reason: String }, #[error("failed to read package metadata at {path}: {source}")] ReadPackageMetadata { path: PathBuf, @@ -840,13 +843,7 @@ pub fn package_directory_cache_key( package: &PackageDirectory, script: &PackageVersionScript, ) -> Result { - let package_file_hash = format!( - "metadata={};script={};manifest={}", - package_file_content_hash(&package.metadata_path)?, - package_file_content_hash(&script.path)?, - optional_file_content_hash(&package.path.join(PACKAGE_MANIFEST_FILE))? - .unwrap_or_else(|| "missing".to_owned()) - ); + let package_file_hash = package_directory_dependency_hash(package, script)?; Ok(RepositoryPackageCacheKey { repository_id: repository_id.clone(), package_id: package.id.clone(), @@ -855,6 +852,99 @@ pub fn package_directory_cache_key( }) } +fn package_directory_dependency_hash( + package: &PackageDirectory, + script: &PackageVersionScript, +) -> Result { + let mut entries = vec![ + format!( + "metadata.jsonc={}", + package_file_content_hash(&package.metadata_path)? + ), + format!( + "{}={}", + script.file_name, + package_file_content_hash(&script.path)? + ), + format!( + "Manifest={}", + optional_file_content_hash(&package.path.join(PACKAGE_MANIFEST_FILE))? + .unwrap_or_else(|| "missing".to_owned()) + ), + ]; + for file in package_local_files(&package.path)? { + let hash = package_file_content_hash(&file.path)?; + entries.push(format!("files/{}={hash}", file.relative_path)); + } + Ok(content_hash(entries.join("\n"))) +} + +struct PackageLocalFile { + relative_path: String, + path: PathBuf, +} + +fn package_local_files(package_dir: &Path) -> Result, RepositoryLoadError> { + let files_root = package_dir.join(PACKAGE_LOCAL_FILES_DIR); + if !files_root.exists() { + return Ok(Vec::new()); + } + if !files_root.is_dir() { + return Err(RepositoryLoadError::InvalidPackageLocalFile { + path: files_root, + reason: "package-local files entry must be a directory".to_owned(), + }); + } + let mut files = Vec::new(); + collect_package_local_files(&files_root, &files_root, &mut files)?; + files.sort_by(|left, right| left.relative_path.cmp(&right.relative_path)); + Ok(files) +} + +fn collect_package_local_files( + files_root: &Path, + current: &Path, + out: &mut Vec, +) -> Result<(), RepositoryLoadError> { + for entry in read_dir_entries(current)? { + let path = entry.path(); + let file_type = entry_file_type(&entry, current)?; + if file_type.is_dir() { + collect_package_local_files(files_root, &path, out)?; + } else if file_type.is_file() { + let relative = path.strip_prefix(files_root).map_err(|_| { + RepositoryLoadError::InvalidPackageLocalFile { + path: path.clone(), + reason: format!("path is not under {}", files_root.display()), + } + })?; + out.push(PackageLocalFile { + relative_path: logical_relative_path(&path, relative)?, + path, + }); + } + } + Ok(()) +} + +fn logical_relative_path(path: &Path, relative: &Path) -> Result { + let value = relative + .to_string_lossy() + .replace(std::path::MAIN_SEPARATOR, "/"); + if value.is_empty() { + return Err(RepositoryLoadError::InvalidPackageLocalFile { + path: path.to_path_buf(), + reason: "missing relative file path".to_owned(), + }); + } + Ok(value) +} + +fn content_hash(content: impl AsRef<[u8]>) -> String { + let hash = Sha512::digest(content.as_ref()); + format!("sha512:{hash:x}") +} + fn optional_file_content_hash(path: &Path) -> Result, RepositoryLoadError> { if path.exists() { package_file_content_hash(path).map(Some) @@ -1261,6 +1351,45 @@ api_version = "getter.repo.v1" assert_ne!(first.package_file_hash, second.package_file_hash); } + #[test] + fn package_directory_cache_key_changes_when_local_files_change() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + let package_dir = root.join("android/app/example"); + fs::create_dir_all(package_dir.join(PACKAGE_LOCAL_FILES_DIR)).unwrap(); + fs::write( + package_dir.join(PACKAGE_METADATA_FILE), + r#"{ "type": "android:app", "android": { "package_name": "example" } }"#, + ) + .unwrap(); + fs::write(package_dir.join("9999.lua"), "return {}").unwrap(); + fs::write( + package_dir.join(PACKAGE_LOCAL_FILES_DIR).join("data.txt"), + "one", + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + let package = &layout.packages[0]; + let script = layout.unambiguous_version_script(package).unwrap(); + let first = + package_directory_cache_key(&RepositoryId::new("repo").unwrap(), package, script) + .unwrap(); + + fs::write( + package_dir.join(PACKAGE_LOCAL_FILES_DIR).join("data.txt"), + "two", + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + let package = &layout.packages[0]; + let script = layout.unambiguous_version_script(package).unwrap(); + let second = + package_directory_cache_key(&RepositoryId::new("repo").unwrap(), package, script) + .unwrap(); + + assert_ne!(first.package_file_hash, second.package_file_hash); + } + #[test] fn highest_priority_selects_larger_number() { let priorities = [ From edbebceefc960caf20a03eb9a5a9e5210f111949 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 02:56:13 +0800 Subject: [PATCH 35/71] feat(cache): add provider refresh primitives --- crates/getter-operations/src/lib.rs | 1 + .../getter-operations/src/provider_cache.rs | 268 +++++++++++++++ crates/getter-storage/src/lib.rs | 312 ++++++++++++++++++ 3 files changed, 581 insertions(+) create mode 100644 crates/getter-operations/src/provider_cache.rs diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 4bd9337..a2e7afe 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -7,5 +7,6 @@ pub mod autogen; pub mod legacy_room; +pub mod provider_cache; pub mod read_model; pub mod runtime; diff --git a/crates/getter-operations/src/provider_cache.rs b/crates/getter-operations/src/provider_cache.rs new file mode 100644 index 0000000..771cd81 --- /dev/null +++ b/crates/getter-operations/src/provider_cache.rs @@ -0,0 +1,268 @@ +//! Shared provider/source cache refresh helper. +//! +//! Provider implementations can use this small helper to keep ADR-0010/0012 +//! forced-refresh semantics consistent: a successful refresh replaces the cache, +//! while a failed forced refresh may return old cache only with explicit stale +//! diagnostics. + +use getter_storage::{CacheDb, ProviderResponseUpsert, StorageError, StoredProviderResponse}; +use serde_json::Value; + +pub const CACHE_REFRESH_FAILED: &str = "cache.refresh_failed"; +pub const USED_STALE_CACHE: &str = "used_stale_cache"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ProviderCacheMode { + UseCached, + ForceRefresh, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ProviderCacheSource { + Cache, + Refreshed, + Stale, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ProviderCacheRequest<'a> { + pub cache_key: &'a str, + pub provider: &'a str, + pub mode: ProviderCacheMode, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProviderCacheDiagnostic { + pub code: String, + pub message: String, + pub cache_key: String, + pub provider: String, + pub stale_fetched_at_unix: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProviderCacheResult { + pub response: StoredProviderResponse, + pub source: ProviderCacheSource, + pub diagnostics: Vec, +} + +#[derive(Debug, thiserror::Error)] +pub enum ProviderCacheOperationError { + #[error("storage error: {0}")] + Storage(#[from] StorageError), + #[error("provider refresh failed: {0}")] + RefreshFailed(String), +} + +pub fn read_or_refresh_provider_response( + db: &CacheDb, + request: ProviderCacheRequest<'_>, + refresh: F, +) -> Result +where + F: FnOnce() -> Result, +{ + let cached = db.provider_response(request.cache_key)?; + if request.mode == ProviderCacheMode::UseCached { + if let Some(response) = cached { + return Ok(ProviderCacheResult { + response, + source: ProviderCacheSource::Cache, + diagnostics: Vec::new(), + }); + } + } + + match refresh() { + Ok(response_json) => { + let response = db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: request.cache_key.to_owned(), + provider: request.provider.to_owned(), + response_json, + })?; + Ok(ProviderCacheResult { + response, + source: ProviderCacheSource::Refreshed, + diagnostics: Vec::new(), + }) + } + Err(detail) => match cached { + Some(response) => { + let stale_fetched_at_unix = Some(response.fetched_at_unix); + Ok(ProviderCacheResult { + response, + source: ProviderCacheSource::Stale, + diagnostics: vec![ + ProviderCacheDiagnostic { + code: CACHE_REFRESH_FAILED.to_owned(), + message: detail, + cache_key: request.cache_key.to_owned(), + provider: request.provider.to_owned(), + stale_fetched_at_unix, + }, + ProviderCacheDiagnostic { + code: USED_STALE_CACHE.to_owned(), + message: "using stale provider cache after refresh failure".to_owned(), + cache_key: request.cache_key.to_owned(), + provider: request.provider.to_owned(), + stale_fetched_at_unix, + }, + ], + }) + } + None => Err(ProviderCacheOperationError::RefreshFailed(detail)), + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + use std::cell::Cell; + + #[test] + fn uses_cached_provider_response_without_refreshing() { + let db = CacheDb::open_in_memory().unwrap(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: "fdroid:official:index".to_owned(), + provider: "fdroid".to_owned(), + response_json: json!({ "revision": "cached" }), + }) + .unwrap(); + let refreshed = Cell::new(false); + + let result = read_or_refresh_provider_response( + &db, + ProviderCacheRequest { + cache_key: "fdroid:official:index", + provider: "fdroid", + mode: ProviderCacheMode::UseCached, + }, + || { + refreshed.set(true); + Ok(json!({ "revision": "fresh" })) + }, + ) + .unwrap(); + + assert!(!refreshed.get()); + assert_eq!(result.source, ProviderCacheSource::Cache); + assert_eq!(result.response.response_json["revision"], "cached"); + assert!(result.diagnostics.is_empty()); + } + + #[test] + fn cache_miss_refreshes_and_stores_provider_response() { + let db = CacheDb::open_in_memory().unwrap(); + + let result = read_or_refresh_provider_response( + &db, + ProviderCacheRequest { + cache_key: "github:f-droid/fdroidclient:releases", + provider: "github", + mode: ProviderCacheMode::UseCached, + }, + || Ok(json!({ "etag": "fresh" })), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!(result.response.response_json["etag"], "fresh"); + assert_eq!( + db.provider_response("github:f-droid/fdroidclient:releases") + .unwrap() + .unwrap() + .response_json["etag"], + "fresh" + ); + } + + #[test] + fn forced_refresh_replaces_cached_provider_response() { + let db = CacheDb::open_in_memory().unwrap(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: "fdroid:official:index".to_owned(), + provider: "fdroid".to_owned(), + response_json: json!({ "revision": "old" }), + }) + .unwrap(); + + let result = read_or_refresh_provider_response( + &db, + ProviderCacheRequest { + cache_key: "fdroid:official:index", + provider: "fdroid", + mode: ProviderCacheMode::ForceRefresh, + }, + || Ok(json!({ "revision": "new" })), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!(result.response.response_json["revision"], "new"); + assert_eq!( + db.provider_response("fdroid:official:index") + .unwrap() + .unwrap() + .response_json["revision"], + "new" + ); + } + + #[test] + fn forced_refresh_failure_returns_stale_cache_with_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: "github:f-droid/fdroidclient:releases".to_owned(), + provider: "github".to_owned(), + response_json: json!({ "etag": "old" }), + }) + .unwrap(); + + let result = read_or_refresh_provider_response( + &db, + ProviderCacheRequest { + cache_key: "github:f-droid/fdroidclient:releases", + provider: "github", + mode: ProviderCacheMode::ForceRefresh, + }, + || Err("HTTP 503".to_owned()), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Stale); + assert_eq!(result.response.response_json["etag"], "old"); + let codes: Vec<_> = result + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect(); + assert_eq!(codes, vec![CACHE_REFRESH_FAILED, USED_STALE_CACHE]); + assert!(result + .diagnostics + .iter() + .all(|diagnostic| diagnostic.stale_fetched_at_unix.is_some())); + } + + #[test] + fn refresh_failure_without_stale_cache_is_an_error() { + let db = CacheDb::open_in_memory().unwrap(); + let error = read_or_refresh_provider_response( + &db, + ProviderCacheRequest { + cache_key: "fdroid:official:index", + provider: "fdroid", + mode: ProviderCacheMode::ForceRefresh, + }, + || Err("network unavailable".to_owned()), + ) + .unwrap_err(); + + assert!(matches!( + error, + ProviderCacheOperationError::RefreshFailed(_) + )); + } +} diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index e8d7b02..2af994e 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -10,6 +10,7 @@ use getter_core::task::{ }; use getter_core::{PackageId, RepositoryId, RepositoryPriority, UpdateAction}; use rusqlite::{params, Connection, OptionalExtension, Params, Transaction}; +use serde_json::Value; use std::path::Path; use std::str::FromStr; @@ -884,6 +885,161 @@ CREATE TABLE IF NOT EXISTS provider_responses ( )?; Ok(()) } + + pub fn upsert_provider_response( + &self, + response: &ProviderResponseUpsert, + ) -> Result { + let response_json = serde_json::to_string(&response.response_json)?; + self.conn.execute( + r#" +INSERT INTO provider_responses(cache_key, provider, response_json) +VALUES (?1, ?2, ?3) +ON CONFLICT(cache_key) DO UPDATE SET + provider = excluded.provider, + response_json = excluded.response_json, + fetched_at_unix = unixepoch() +"#, + params![response.cache_key, response.provider, response_json], + )?; + self.provider_response(&response.cache_key)?.ok_or_else(|| { + StorageError::Invariant(format!( + "provider response '{}' was not readable after upsert", + response.cache_key + )) + }) + } + + pub fn provider_response( + &self, + cache_key: &str, + ) -> Result, StorageError> { + let mut stmt = self.conn.prepare( + r#" +SELECT cache_key, provider, response_json, fetched_at_unix +FROM provider_responses +WHERE cache_key = ?1 +"#, + )?; + let row = stmt + .query_row(params![cache_key], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + row.get::<_, i64>(3)?, + )) + }) + .optional()?; + row.map( + |(cache_key, provider, response_json, fetched_at_unix)| -> Result<_, StorageError> { + Ok(StoredProviderResponse { + cache_key, + provider, + response_json: serde_json::from_str(&response_json)?, + fetched_at_unix, + }) + }, + ) + .transpose() + } + + pub fn upsert_evaluated_package( + &self, + package: &EvaluatedPackageUpsert, + ) -> Result { + let evaluated_json = serde_json::to_string(&package.evaluated_json)?; + self.conn.execute( + r#" +INSERT INTO evaluated_packages( + cache_key, + repository_id, + package_id, + package_file_hash, + schema_version, + evaluated_json +) +VALUES (?1, ?2, ?3, ?4, ?5, ?6) +ON CONFLICT(cache_key) DO UPDATE SET + repository_id = excluded.repository_id, + package_id = excluded.package_id, + package_file_hash = excluded.package_file_hash, + schema_version = excluded.schema_version, + evaluated_json = excluded.evaluated_json, + evaluated_at_unix = unixepoch() +"#, + params![ + package.cache_key, + package.repository_id.as_str(), + package.package_id.to_string(), + package.package_file_hash, + package.schema_version, + evaluated_json, + ], + )?; + self.evaluated_package(&package.cache_key)?.ok_or_else(|| { + StorageError::Invariant(format!( + "evaluated package '{}' was not readable after upsert", + package.cache_key + )) + }) + } + + pub fn evaluated_package( + &self, + cache_key: &str, + ) -> Result, StorageError> { + let mut stmt = self.conn.prepare( + r#" +SELECT + cache_key, + repository_id, + package_id, + package_file_hash, + schema_version, + evaluated_json, + evaluated_at_unix +FROM evaluated_packages +WHERE cache_key = ?1 +"#, + )?; + let row = stmt + .query_row(params![cache_key], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + row.get::<_, String>(3)?, + row.get::<_, String>(4)?, + row.get::<_, String>(5)?, + row.get::<_, i64>(6)?, + )) + }) + .optional()?; + row.map( + |( + cache_key, + repository_id, + package_id, + package_file_hash, + schema_version, + evaluated_json, + evaluated_at_unix, + )| + -> Result<_, StorageError> { + Ok(StoredEvaluatedPackage { + cache_key, + repository_id: RepositoryId::new(repository_id)?, + package_id: package_id.parse()?, + package_file_hash, + schema_version, + evaluated_json: serde_json::from_str(&evaluated_json)?, + evaluated_at_unix, + }) + }, + ) + .transpose() + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -896,6 +1052,42 @@ pub struct StoredRepository { pub revision: Option, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProviderResponseUpsert { + pub cache_key: String, + pub provider: String, + pub response_json: Value, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StoredProviderResponse { + pub cache_key: String, + pub provider: String, + pub response_json: Value, + pub fetched_at_unix: i64, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EvaluatedPackageUpsert { + pub cache_key: String, + pub repository_id: RepositoryId, + pub package_id: PackageId, + pub package_file_hash: String, + pub schema_version: String, + pub evaluated_json: Value, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StoredEvaluatedPackage { + pub cache_key: String, + pub repository_id: RepositoryId, + pub package_id: PackageId, + pub package_file_hash: String, + pub schema_version: String, + pub evaluated_json: Value, + pub evaluated_at_unix: i64, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct TrackedPackageUpsert { pub package_id: PackageId, @@ -1498,4 +1690,124 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); fn cache_db_migrates_schema() { let _db = CacheDb::open_in_memory().unwrap(); } + + #[test] + fn cache_db_round_trips_provider_response_cache_entries() { + let db = CacheDb::open_in_memory().unwrap(); + + let stored = db + .upsert_provider_response(&ProviderResponseUpsert { + cache_key: "fdroid:official:index:v1".to_owned(), + provider: "fdroid".to_owned(), + response_json: serde_json::json!({ + "endpoint": "official", + "packages": ["org.fdroid.fdroid"] + }), + }) + .unwrap(); + + assert_eq!(stored.cache_key, "fdroid:official:index:v1"); + assert_eq!(stored.provider, "fdroid"); + assert_eq!(stored.response_json["endpoint"], "official"); + assert_eq!( + db.provider_response("fdroid:official:index:v1") + .unwrap() + .unwrap(), + stored + ); + } + + #[test] + fn cache_db_replaces_provider_response_for_same_cache_key() { + let db = CacheDb::open_in_memory().unwrap(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: "github:f-droid/fdroidclient:releases".to_owned(), + provider: "github".to_owned(), + response_json: serde_json::json!({ "etag": "old" }), + }) + .unwrap(); + + let stored = db + .upsert_provider_response(&ProviderResponseUpsert { + cache_key: "github:f-droid/fdroidclient:releases".to_owned(), + provider: "github".to_owned(), + response_json: serde_json::json!({ "etag": "new" }), + }) + .unwrap(); + + assert_eq!(stored.response_json["etag"], "new"); + assert_eq!( + db.provider_response("github:f-droid/fdroidclient:releases") + .unwrap() + .unwrap() + .response_json["etag"], + "new" + ); + } + + #[test] + fn cache_db_round_trips_evaluated_package_cache_entries() { + let db = CacheDb::open_in_memory().unwrap(); + let stored = db + .upsert_evaluated_package(&EvaluatedPackageUpsert { + cache_key: "official:android/app/org.fdroid.fdroid:sha512:123".to_owned(), + repository_id: RepositoryId::new("official").unwrap(), + package_id: "android/app/org.fdroid.fdroid".parse().unwrap(), + package_file_hash: "sha512:123".to_owned(), + schema_version: "getter.package.v1".to_owned(), + evaluated_json: serde_json::json!({ + "name": "F-Droid", + "updates": [] + }), + }) + .unwrap(); + + assert_eq!(stored.repository_id.as_str(), "official"); + assert_eq!( + stored.package_id.to_string(), + "android/app/org.fdroid.fdroid" + ); + assert_eq!(stored.evaluated_json["name"], "F-Droid"); + assert_eq!( + db.evaluated_package("official:android/app/org.fdroid.fdroid:sha512:123") + .unwrap() + .unwrap(), + stored + ); + } + + #[test] + fn cache_db_replaces_evaluated_package_for_same_cache_key() { + let db = CacheDb::open_in_memory().unwrap(); + let cache_key = "official:android/app/org.fdroid.fdroid:sha512:123"; + db.upsert_evaluated_package(&EvaluatedPackageUpsert { + cache_key: cache_key.to_owned(), + repository_id: RepositoryId::new("official").unwrap(), + package_id: "android/app/org.fdroid.fdroid".parse().unwrap(), + package_file_hash: "sha512:old".to_owned(), + schema_version: "getter.package.v1".to_owned(), + evaluated_json: serde_json::json!({ "name": "Old" }), + }) + .unwrap(); + + let stored = db + .upsert_evaluated_package(&EvaluatedPackageUpsert { + cache_key: cache_key.to_owned(), + repository_id: RepositoryId::new("official").unwrap(), + package_id: "android/app/org.fdroid.fdroid".parse().unwrap(), + package_file_hash: "sha512:new".to_owned(), + schema_version: "getter.package.v1".to_owned(), + evaluated_json: serde_json::json!({ "name": "New" }), + }) + .unwrap(); + + assert_eq!(stored.package_file_hash, "sha512:new"); + assert_eq!( + db.evaluated_package(cache_key) + .unwrap() + .unwrap() + .evaluated_json["name"], + "New" + ); + } } From bc94040e4facb44cd3d5a2a9cd65b83902959b6d Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 03:33:44 +0800 Subject: [PATCH 36/71] feat(providers): parse F-Droid catalog fixtures --- Cargo.lock | 11 + crates/getter-core/src/lib.rs | 6 + crates/getter-core/src/lua.rs | 44 ++++ crates/getter-core/src/update.rs | 4 + crates/getter-operations/src/runtime.rs | 3 + crates/getter-providers/Cargo.toml | 2 + crates/getter-providers/src/lib.rs | 299 +++++++++++++++++++++++- 7 files changed, 364 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 34622f9..c43d98e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -636,6 +636,8 @@ name = "getter-providers" version = "0.1.0" dependencies = [ "getter-core", + "roxmltree", + "thiserror 1.0.69", ] [[package]] @@ -1178,6 +1180,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "roxmltree" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb" +dependencies = [ + "memchr", +] + [[package]] name = "rusqlite" version = "0.32.1" diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index f02c4b0..957386c 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -281,6 +281,8 @@ pub struct PackagePermissions { pub struct UpdateCandidate { pub version: String, #[serde(default)] + pub version_code: Option, + #[serde(default)] pub channel: Option, #[serde(default)] pub source: Option, @@ -294,6 +296,10 @@ pub struct UpdateArtifact { pub url: String, #[serde(default)] pub file_name: Option, + #[serde(default)] + pub sha256: Option, + #[serde(default)] + pub size: Option, } /// Candidate selected for update after package/user-state policy. diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 8d6f74e..8e33718 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -690,6 +690,7 @@ fn parse_update_candidates( let artifacts = parse_update_artifacts(path, object.get("artifacts"))?; Ok(UpdateCandidate { version: required_string(path, object, "version")?.to_owned(), + version_code: optional_i64(path, object, "version_code")?, channel: optional_string(object, "channel"), source: optional_string(object, "source"), artifacts, @@ -720,6 +721,8 @@ fn parse_update_artifacts( name: required_string(path, object, "name")?.to_owned(), url: required_string(path, object, "url")?.to_owned(), file_name: optional_string(object, "file_name"), + sha256: optional_string(object, "sha256"), + size: optional_u64(path, object, "size")?, }) }) .collect() @@ -732,6 +735,38 @@ fn optional_string(object: &Map, field: &str) -> Option, + field: &str, +) -> Result, LuaPackageError> { + object + .get(field) + .map(|value| { + value.as_i64().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("field '{field}' must be an integer"), + }) + }) + .transpose() +} + +fn optional_u64( + path: &Path, + object: &Map, + field: &str, +) -> Result, LuaPackageError> { + object + .get(field) + .map(|value| { + value.as_u64().ok_or_else(|| LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("field '{field}' must be an unsigned integer"), + }) + }) + .transpose() +} + fn parse_permissions( path: &Path, value: Option<&JsonValue>, @@ -822,6 +857,7 @@ return package_def { updates = { { version = "1.2.0", + version_code = 120, channel = "stable", source = "fixture", artifacts = { @@ -829,6 +865,8 @@ return package_def { name = "app.apk", url = "https://example.invalid/app.apk", file_name = "fdroid.apk", + sha256 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + size = 12345, }, }, }, @@ -852,12 +890,18 @@ return package_def { assert_eq!(package.source_priority, vec!["github", "fdroid"]); assert_eq!(package.updates.len(), 1); assert_eq!(package.updates[0].version, "1.2.0"); + assert_eq!(package.updates[0].version_code, Some(120)); assert_eq!(package.updates[0].channel.as_deref(), Some("stable")); assert_eq!(package.updates[0].source.as_deref(), Some("fixture")); assert_eq!( package.updates[0].artifacts[0].file_name.as_deref(), Some("fdroid.apk") ); + assert_eq!( + package.updates[0].artifacts[0].sha256.as_deref(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ); + assert_eq!(package.updates[0].artifacts[0].size, Some(12345)); } #[test] diff --git a/crates/getter-core/src/update.rs b/crates/getter-core/src/update.rs index 09952ef..6e9260c 100644 --- a/crates/getter-core/src/update.rs +++ b/crates/getter-core/src/update.rs @@ -504,6 +504,7 @@ mod tests { Some("1.0.0".to_owned()), vec![UpdateCandidate { version: "1.2.0".to_owned(), + version_code: None, channel: None, source: None, artifacts: Vec::new(), @@ -539,12 +540,15 @@ mod tests { fn candidate(version: &str) -> UpdateCandidate { UpdateCandidate { version: version.to_owned(), + version_code: None, channel: None, source: None, artifacts: vec![UpdateArtifact { name: "APK".to_owned(), url: format!("https://example.invalid/{version}.apk"), file_name: Some("app.apk".to_owned()), + sha256: None, + size: None, }], } } diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index d9e53f1..3721eed 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -795,12 +795,15 @@ return package_version { .into_iter() .map(|version| UpdateCandidate { version: version.to_owned(), + version_code: None, channel: None, source: None, artifacts: vec![UpdateArtifact { name: "app.apk".to_owned(), url: "https://example.invalid/app.apk".to_owned(), file_name: Some("app.apk".to_owned()), + sha256: None, + size: None, }], }) .collect(), diff --git a/crates/getter-providers/Cargo.toml b/crates/getter-providers/Cargo.toml index f5ae23c..9424784 100644 --- a/crates/getter-providers/Cargo.toml +++ b/crates/getter-providers/Cargo.toml @@ -5,3 +5,5 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core", default-features = false } +roxmltree = "0.21" +thiserror = "1" diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index ee714b9..4d3d02a 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -1,13 +1,13 @@ //! Provider executor scaffolding for the UpgradeAll getter rewrite. //! -//! Real network/provider execution is intentionally deferred to a later ADR. -//! The first Phase D bridge uses package-declared static update candidates as a -//! mock provider so the rest of the runtime can exercise getter-owned update -//! selection and opaque action issuance without direct network side effects. +//! Real network/provider execution is intentionally deferred to later slices. +//! The current provider code is fixture-backed and keeps parsing/normalization in +//! Rust getter so Flutter and Android adapter glue do not learn provider formats. pub use getter_core as core; -use getter_core::{ResolvedPackage, UpdateCandidate}; +use getter_core::{ResolvedPackage, UpdateArtifact, UpdateCandidate}; +use roxmltree::{Document, Node}; /// Mock provider that returns the static `updates` candidates materialized from /// a resolved Lua package table. @@ -24,11 +24,242 @@ impl StaticPackageUpdatesProvider { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FdroidCatalog { + pub endpoint: FdroidEndpoint, + pub apps: Vec, +} + +impl FdroidCatalog { + pub fn app(&self, package_name: &str) -> Option<&FdroidApp> { + self.apps + .iter() + .find(|app| app.package_name == package_name) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FdroidEndpoint { + pub name: Option, + pub url: Option, + pub timestamp: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FdroidApp { + pub package_name: String, + pub name: Option, + pub summary: Option, + pub packages: Vec, +} + +impl FdroidApp { + pub fn update_candidates(&self, endpoint: &FdroidEndpoint) -> Vec { + self.packages + .iter() + .map(|release| release.update_candidate(endpoint)) + .collect() + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FdroidRelease { + pub version: String, + pub version_code: Option, + pub apk_name: String, + pub sha256: Option, + pub size: Option, +} + +impl FdroidRelease { + fn update_candidate(&self, endpoint: &FdroidEndpoint) -> UpdateCandidate { + UpdateCandidate { + version: self.version.clone(), + version_code: self.version_code, + channel: None, + source: Some("fdroid".to_owned()), + artifacts: vec![UpdateArtifact { + name: self.apk_name.clone(), + url: endpoint + .url + .as_deref() + .map(|base| fdroid_artifact_url(base, &self.apk_name)) + .unwrap_or_else(|| self.apk_name.clone()), + file_name: Some(self.apk_name.clone()), + sha256: self.sha256.clone(), + size: self.size, + }], + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum FdroidCatalogError { + #[error("failed to parse F-Droid catalog XML: {0}")] + Xml(#[from] roxmltree::Error), + #[error("F-Droid catalog is missing required field {field} for {context}")] + MissingField { + context: String, + field: &'static str, + }, + #[error("F-Droid catalog field {field} for {context} is invalid: {value}")] + InvalidField { + context: String, + field: &'static str, + value: String, + }, +} + +pub fn parse_fdroid_index_xml(xml: &str) -> Result { + let document = Document::parse(xml)?; + let root = document.root_element(); + let repo = child_element(root, "repo"); + let endpoint = FdroidEndpoint { + name: repo + .and_then(|node| node.attribute("name")) + .map(str::to_owned), + url: repo + .and_then(|node| node.attribute("url")) + .map(str::to_owned), + timestamp: repo + .and_then(|node| node.attribute("timestamp")) + .map(str::to_owned), + }; + let apps = root + .children() + .filter(|node| node.has_tag_name("application")) + .map(parse_fdroid_app) + .collect::>()?; + + Ok(FdroidCatalog { endpoint, apps }) +} + +fn parse_fdroid_app(app: Node<'_, '_>) -> Result { + let package_name = app + .attribute("id") + .map(str::to_owned) + .or_else(|| child_text(app, "id")) + .filter(|id| !id.trim().is_empty()) + .ok_or_else(|| FdroidCatalogError::MissingField { + context: "application".to_owned(), + field: "id", + })?; + let packages = app + .children() + .filter(|node| node.has_tag_name("package")) + .map(|package| parse_fdroid_release(&package_name, package)) + .collect::>()?; + + Ok(FdroidApp { + package_name, + name: child_text(app, "name"), + summary: child_text(app, "summary"), + packages, + }) +} + +fn parse_fdroid_release( + package_name: &str, + package: Node<'_, '_>, +) -> Result { + let version = required_child_text(package, package_name, "version")?; + let apk_name = required_child_text(package, package_name, "apkname")?; + let version_code = child_text(package, "versioncode") + .map(|value| parse_i64(package_name, "versioncode", &value)) + .transpose()?; + let size = child_text(package, "size") + .map(|value| parse_u64(package_name, "size", &value)) + .transpose()?; + let sha256 = package + .children() + .find(|node| node.has_tag_name("hash") && node.attribute("type") == Some("sha256")) + .and_then(|node| node.text()) + .map(str::trim) + .filter(|value| !value.is_empty()) + .map(str::to_owned); + + Ok(FdroidRelease { + version, + version_code, + apk_name, + sha256, + size, + }) +} + +fn required_child_text( + parent: Node<'_, '_>, + context: &str, + field: &'static str, +) -> Result { + child_text(parent, field).ok_or_else(|| FdroidCatalogError::MissingField { + context: context.to_owned(), + field, + }) +} + +fn child_text(parent: Node<'_, '_>, tag: &str) -> Option { + child_element(parent, tag) + .and_then(|node| node.text()) + .map(str::trim) + .filter(|value| !value.is_empty()) + .map(str::to_owned) +} + +fn child_element<'a>(parent: Node<'a, 'a>, tag: &str) -> Option> { + parent.children().find(|node| node.has_tag_name(tag)) +} + +fn parse_i64(context: &str, field: &'static str, value: &str) -> Result { + value.parse().map_err(|_| FdroidCatalogError::InvalidField { + context: context.to_owned(), + field, + value: value.to_owned(), + }) +} + +fn parse_u64(context: &str, field: &'static str, value: &str) -> Result { + value.parse().map_err(|_| FdroidCatalogError::InvalidField { + context: context.to_owned(), + field, + value: value.to_owned(), + }) +} + +fn fdroid_artifact_url(base: &str, apk_name: &str) -> String { + format!("{}/{}", base.trim_end_matches('/'), apk_name) +} + #[cfg(test)] mod tests { use super::*; use getter_core::PackagePermissions; + const FDROID_FIXTURE: &str = r#" + + + + org.fdroid.fdroid + F-Droid + App repository client + + 1.20.0 + 1020000 + org.fdroid.fdroid_1020000.apk + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 1234567 + + + 1.19.0 + 1019000 + org.fdroid.fdroid_1019000.apk + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + 1111111 + + + +"#; + #[test] fn static_provider_returns_package_declared_update_candidates() { let package = ResolvedPackage { @@ -40,6 +271,7 @@ mod tests { source_priority: Vec::new(), updates: vec![UpdateCandidate { version: "1.2.0".to_owned(), + version_code: None, channel: Some("stable".to_owned()), source: Some("fixture".to_owned()), artifacts: Vec::new(), @@ -52,4 +284,61 @@ mod tests { assert_eq!(candidates[0].version, "1.2.0"); assert_eq!(candidates[0].source.as_deref(), Some("fixture")); } + + #[test] + fn parses_fdroid_index_xml_into_catalog_facts() { + let catalog = parse_fdroid_index_xml(FDROID_FIXTURE).unwrap(); + + assert_eq!(catalog.endpoint.name.as_deref(), Some("F-Droid")); + assert_eq!( + catalog.endpoint.url.as_deref(), + Some("https://f-droid.org/repo") + ); + let app = catalog.app("org.fdroid.fdroid").unwrap(); + assert_eq!(app.name.as_deref(), Some("F-Droid")); + assert_eq!(app.summary.as_deref(), Some("App repository client")); + assert_eq!(app.packages.len(), 2); + assert_eq!(app.packages[0].version, "1.20.0"); + assert_eq!(app.packages[0].version_code, Some(1020000)); + assert_eq!( + app.packages[0].sha256.as_deref(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ); + assert_eq!(app.packages[0].size, Some(1234567)); + } + + #[test] + fn normalizes_fdroid_releases_to_update_candidates() { + let catalog = parse_fdroid_index_xml(FDROID_FIXTURE).unwrap(); + let app = catalog.app("org.fdroid.fdroid").unwrap(); + + let candidates = app.update_candidates(&catalog.endpoint); + + assert_eq!(candidates.len(), 2); + assert_eq!(candidates[0].version, "1.20.0"); + assert_eq!(candidates[0].version_code, Some(1020000)); + assert_eq!(candidates[0].source.as_deref(), Some("fdroid")); + let artifact = &candidates[0].artifacts[0]; + assert_eq!(artifact.name, "org.fdroid.fdroid_1020000.apk"); + assert_eq!( + artifact.url, + "https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk" + ); + assert_eq!( + artifact.file_name.as_deref(), + Some("org.fdroid.fdroid_1020000.apk") + ); + assert_eq!( + artifact.sha256.as_deref(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ); + assert_eq!(artifact.size, Some(1234567)); + } + + #[test] + fn malformed_fdroid_index_reports_parse_error() { + let error = parse_fdroid_index_xml("").unwrap_err(); + + assert!(matches!(error, FdroidCatalogError::Xml(_))); + } } From d60cc75ff804edab69a1335a9f52bb205d5896f1 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 04:04:22 +0800 Subject: [PATCH 37/71] feat(operations): cache F-Droid catalog fixtures --- Cargo.lock | 2 + crates/getter-operations/Cargo.toml | 1 + .../getter-operations/src/fdroid_catalog.rs | 336 ++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + crates/getter-providers/Cargo.toml | 1 + crates/getter-providers/src/lib.rs | 9 +- 6 files changed, 346 insertions(+), 4 deletions(-) create mode 100644 crates/getter-operations/src/fdroid_catalog.rs diff --git a/Cargo.lock b/Cargo.lock index c43d98e..039770c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -620,6 +620,7 @@ dependencies = [ "json_comments", "serde", "serde_json", + "sha2", "tempfile", "thiserror 1.0.69", ] @@ -637,6 +638,7 @@ version = "0.1.0" dependencies = [ "getter-core", "roxmltree", + "serde", "thiserror 1.0.69", ] diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index 26863cb..ed85e8c 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -14,6 +14,7 @@ getter-providers = { path = "../getter-providers" } getter-storage = { path = "../getter-storage" } serde = { version = "1", features = ["derive"] } serde_json = "1" +sha2 = "0.10" thiserror = "1" [dev-dependencies] diff --git a/crates/getter-operations/src/fdroid_catalog.rs b/crates/getter-operations/src/fdroid_catalog.rs new file mode 100644 index 0000000..3a301f5 --- /dev/null +++ b/crates/getter-operations/src/fdroid_catalog.rs @@ -0,0 +1,336 @@ +//! Fixture-backed F-Droid catalog cache/query operations. +//! +//! This is the first getter-owned F-Droid operation slice after the provider +//! parser. It wires parsed catalog facts through `cache.db` refresh semantics +//! without introducing live HTTP, Flutter-owned provider parsing, or downloader +//! task state. + +use crate::provider_cache::{ + read_or_refresh_provider_response, ProviderCacheDiagnostic, ProviderCacheMode, + ProviderCacheOperationError, ProviderCacheRequest, ProviderCacheSource, +}; +use getter_providers::{parse_fdroid_index_xml, FdroidApp, FdroidCatalog, FdroidCatalogError}; +use getter_storage::{CacheDb, StorageError}; +use serde::Deserialize; +use serde_json::{json, Value}; +use sha2::{Digest, Sha512}; + +pub const FDROID_PROVIDER_ID: &str = "fdroid"; +pub const FDROID_CATALOG_CACHE_VERSION: &str = "fdroid-index-v1"; +pub const DEFAULT_FDROID_ENDPOINT_ID: &str = "official"; +pub const DEFAULT_FDROID_ENDPOINT_URL: &str = "https://f-droid.org/repo"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FdroidEndpointConfig { + pub endpoint_id: String, + pub endpoint_url: String, +} + +impl Default for FdroidEndpointConfig { + fn default() -> Self { + Self { + endpoint_id: DEFAULT_FDROID_ENDPOINT_ID.to_owned(), + endpoint_url: DEFAULT_FDROID_ENDPOINT_URL.to_owned(), + } + } +} + +impl FdroidEndpointConfig { + pub fn cache_key(&self) -> String { + format!( + "{FDROID_PROVIDER_ID}:{FDROID_CATALOG_CACHE_VERSION}:{}:{}", + self.endpoint_id, + digest_hex(&self.endpoint_url), + ) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FdroidCatalogResult { + pub endpoint: FdroidEndpointConfig, + pub cache_key: String, + pub catalog: FdroidCatalog, + pub source: ProviderCacheSource, + pub diagnostics: Vec, +} + +impl FdroidCatalogResult { + pub fn app(&self, package_name: &str) -> Option<&FdroidApp> { + self.catalog.app(package_name) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum FdroidCatalogOperationError { + #[error("provider cache operation failed: {0}")] + Cache(#[from] ProviderCacheOperationError), + #[error("storage error: {0}")] + Storage(#[from] StorageError), + #[error("F-Droid catalog parse failed: {0}")] + Catalog(#[from] FdroidCatalogError), + #[error("F-Droid catalog serialization failed: {0}")] + Serialization(#[from] serde_json::Error), + #[error("invalid F-Droid catalog request: {0}")] + InvalidRequest(String), +} + +pub fn read_or_refresh_fdroid_catalog( + db: &CacheDb, + endpoint: FdroidEndpointConfig, + mode: ProviderCacheMode, + refresh_xml: F, +) -> Result +where + F: FnOnce() -> Result, +{ + let cache_key = endpoint.cache_key(); + let cache_result = read_or_refresh_provider_response( + db, + ProviderCacheRequest { + cache_key: &cache_key, + provider: FDROID_PROVIDER_ID, + mode, + }, + || { + let xml = refresh_xml()?; + let catalog = parse_fdroid_index_xml(&xml).map_err(|source| source.to_string())?; + serde_json::to_value(catalog).map_err(|source| source.to_string()) + }, + )?; + let catalog = serde_json::from_value(cache_result.response.response_json)?; + + Ok(FdroidCatalogResult { + endpoint, + cache_key, + catalog, + source: cache_result.source, + diagnostics: cache_result.diagnostics, + }) +} + +pub fn fdroid_catalog_json( + db: &CacheDb, + request_json: &str, +) -> Result { + let request: FdroidCatalogJsonRequest = serde_json::from_str(request_json)?; + let endpoint = FdroidEndpointConfig { + endpoint_id: request + .endpoint_id + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_ID.to_owned()), + endpoint_url: request + .endpoint_url + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), + }; + let mode = match request.mode.as_deref() { + Some("force_refresh") => ProviderCacheMode::ForceRefresh, + Some("use_cached") | None => ProviderCacheMode::UseCached, + Some(other) => { + return Err(FdroidCatalogOperationError::InvalidRequest(format!( + "unknown mode '{other}'" + ))) + } + }; + let result = read_or_refresh_fdroid_catalog(db, endpoint, mode, || { + request + .index_xml + .ok_or_else(|| "fixture-backed F-Droid catalog refresh requires index_xml".to_owned()) + })?; + let package_matches: Vec = request + .package_names + .iter() + .filter_map(|package_name| result.app(package_name)) + .map(serde_json::to_value) + .collect::>()?; + + Ok(json!({ + "operation": "fdroid.catalog", + "provider": FDROID_PROVIDER_ID, + "endpoint_id": result.endpoint.endpoint_id, + "endpoint_url": result.endpoint.endpoint_url, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "catalog": result.catalog, + "matches": package_matches, + "diagnostics": result.diagnostics.iter().map(diagnostic_json).collect::>(), + })) +} + +#[derive(Debug, Deserialize)] +struct FdroidCatalogJsonRequest { + #[serde(default)] + endpoint_id: Option, + #[serde(default)] + endpoint_url: Option, + #[serde(default)] + mode: Option, + #[serde(default)] + index_xml: Option, + #[serde(default)] + package_names: Vec, +} + +fn provider_source_json(source: ProviderCacheSource) -> &'static str { + match source { + ProviderCacheSource::Cache => "cache", + ProviderCacheSource::Refreshed => "refreshed", + ProviderCacheSource::Stale => "stale", + } +} + +fn diagnostic_json(diagnostic: &ProviderCacheDiagnostic) -> Value { + json!({ + "code": diagnostic.code, + "message": diagnostic.message, + "cache_key": diagnostic.cache_key, + "provider": diagnostic.provider, + "stale_fetched_at_unix": diagnostic.stale_fetched_at_unix, + }) +} + +fn digest_hex(value: &str) -> String { + let mut hasher = Sha512::new(); + hasher.update(value.as_bytes()); + format!("{:x}", hasher.finalize()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::provider_cache::{CACHE_REFRESH_FAILED, USED_STALE_CACHE}; + use serde_json::json; + use std::cell::Cell; + + const FDROID_FIXTURE: &str = r#" + + + + F-Droid + App repository client + + 1.20.0 + 1020000 + org.fdroid.fdroid_1020000.apk + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 1234567 + + + +"#; + + fn endpoint() -> FdroidEndpointConfig { + FdroidEndpointConfig::default() + } + + #[test] + fn cache_miss_parses_and_stores_fixture_catalog() { + let db = CacheDb::open_in_memory().unwrap(); + let endpoint = endpoint(); + let cache_key = endpoint.cache_key(); + + let result = + read_or_refresh_fdroid_catalog(&db, endpoint, ProviderCacheMode::UseCached, || { + Ok(FDROID_FIXTURE.to_owned()) + }) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!( + result + .catalog + .app("org.fdroid.fdroid") + .unwrap() + .packages + .len(), + 1 + ); + let cached = db.provider_response(&cache_key).unwrap().unwrap(); + assert_eq!(cached.provider, FDROID_PROVIDER_ID); + assert_eq!( + cached.response_json["apps"][0]["package_name"], + "org.fdroid.fdroid" + ); + } + + #[test] + fn cache_hit_avoids_refreshing_fixture_catalog() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_fdroid_catalog(&db, endpoint(), ProviderCacheMode::UseCached, || { + Ok(FDROID_FIXTURE.to_owned()) + }) + .unwrap(); + let refreshed = Cell::new(false); + + let result = + read_or_refresh_fdroid_catalog(&db, endpoint(), ProviderCacheMode::UseCached, || { + refreshed.set(true); + Err("should not refresh".to_owned()) + }) + .unwrap(); + + assert!(!refreshed.get()); + assert_eq!(result.source, ProviderCacheSource::Cache); + assert_eq!(result.catalog.endpoint.name.as_deref(), Some("F-Droid")); + } + + #[test] + fn forced_refresh_failure_returns_stale_catalog_with_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_fdroid_catalog(&db, endpoint(), ProviderCacheMode::UseCached, || { + Ok(FDROID_FIXTURE.to_owned()) + }) + .unwrap(); + + let result = read_or_refresh_fdroid_catalog( + &db, + endpoint(), + ProviderCacheMode::ForceRefresh, + || Err("HTTP 503".to_owned()), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Stale); + assert_eq!( + result + .catalog + .app("org.fdroid.fdroid") + .unwrap() + .name + .as_deref(), + Some("F-Droid") + ); + let codes: Vec<_> = result + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect(); + assert_eq!(codes, vec![CACHE_REFRESH_FAILED, USED_STALE_CACHE]); + } + + #[test] + fn json_operation_returns_package_name_matches_from_cached_catalog() { + let db = CacheDb::open_in_memory().unwrap(); + let first = fdroid_catalog_json( + &db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid", "missing.package"] + }) + .to_string(), + ) + .unwrap(); + assert_eq!(first["source"], "refreshed"); + + let second = fdroid_catalog_json( + &db, + &json!({ + "package_names": ["org.fdroid.fdroid"] + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(second["source"], "cache"); + assert_eq!(second["matches"].as_array().unwrap().len(), 1); + assert_eq!(second["matches"][0]["package_name"], "org.fdroid.fdroid"); + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index a2e7afe..ffe59eb 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -6,6 +6,7 @@ //! generated package files, manifests, and tracked state live here. pub mod autogen; +pub mod fdroid_catalog; pub mod legacy_room; pub mod provider_cache; pub mod read_model; diff --git a/crates/getter-providers/Cargo.toml b/crates/getter-providers/Cargo.toml index 9424784..31d2fc4 100644 --- a/crates/getter-providers/Cargo.toml +++ b/crates/getter-providers/Cargo.toml @@ -6,4 +6,5 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core", default-features = false } roxmltree = "0.21" +serde = { version = "1", features = ["derive"] } thiserror = "1" diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index 4d3d02a..518d5ba 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -8,6 +8,7 @@ pub use getter_core as core; use getter_core::{ResolvedPackage, UpdateArtifact, UpdateCandidate}; use roxmltree::{Document, Node}; +use serde::{Deserialize, Serialize}; /// Mock provider that returns the static `updates` candidates materialized from /// a resolved Lua package table. @@ -24,7 +25,7 @@ impl StaticPackageUpdatesProvider { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FdroidCatalog { pub endpoint: FdroidEndpoint, pub apps: Vec, @@ -38,14 +39,14 @@ impl FdroidCatalog { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FdroidEndpoint { pub name: Option, pub url: Option, pub timestamp: Option, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FdroidApp { pub package_name: String, pub name: Option, @@ -62,7 +63,7 @@ impl FdroidApp { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FdroidRelease { pub version: String, pub version_code: Option, From de63a6b515aa8292eeee957db8531b08c4cc467a Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 10:24:47 +0800 Subject: [PATCH 38/71] feat(autogen): generate F-Droid package directories --- crates/getter-cli/src/lib.rs | 116 ++- crates/getter-cli/tests/bdd_cli.rs | 134 +++- .../features/cli/autogen_installed.feature | 35 + crates/getter-core/src/autogen.rs | 14 +- crates/getter-operations/src/autogen.rs | 71 +- .../getter-operations/src/fdroid_autogen.rs | 661 ++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + 7 files changed, 1017 insertions(+), 15 deletions(-) create mode 100644 crates/getter-operations/src/fdroid_autogen.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 0cb5e6f..1f3d406 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -22,6 +22,7 @@ use getter_downloader::{ cancel_download_task, record_install_result, run_fake_download_task, submit_fake_download_task, }; use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; +use getter_operations::fdroid_autogen; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; use getter_operations::runtime as runtime_operations; use getter_storage::legacy_room::{ @@ -126,6 +127,14 @@ pub enum CliCommand { preview: PathBuf, acceptance: AutogenAcceptance, }, + AutogenFdroidPreview { + index: PathBuf, + package_names: Vec, + }, + AutogenFdroidApply { + preview: PathBuf, + acceptance: AutogenAcceptance, + }, LegacyImportRoomBundle { bundle: PathBuf, }, @@ -526,6 +535,26 @@ where inventory: PathBuf::from(inventory), } } + [domain, subject, action, rest @ ..] + if domain == "autogen" && subject == "fdroid" && action == "preview" => + { + let (index, package_names) = parse_fdroid_autogen_preview_args(rest)?; + CliCommand::AutogenFdroidPreview { + index, + package_names, + } + } + [domain, subject, action, flag, preview, rest @ ..] + if domain == "autogen" + && subject == "fdroid" + && action == "apply" + && flag == "--preview" => + { + CliCommand::AutogenFdroidApply { + preview: PathBuf::from(preview), + acceptance: parse_autogen_acceptance(rest)?, + } + } [domain, subject, action, flag, preview, rest @ ..] if domain == "autogen" && subject == "cleanup" @@ -727,6 +756,39 @@ fn execute(invocation: CliInvocation) -> Result { autogen::apply_cleanup_preview(&invocation.data_dir, &db, &preview, &acceptance) .map_err(CliError::from) } + CliCommand::AutogenFdroidPreview { + index, + package_names, + } => { + let db = open_main_db(&invocation.data_dir)?; + let cache_db = open_cache_db(&invocation.data_dir)?; + let index_xml = read_fdroid_index(&index)?; + let request = json!({ + "index_xml": index_xml, + "package_names": package_names, + }); + fdroid_autogen::preview_fdroid_packages_json( + &invocation.data_dir, + &db, + &cache_db, + &request.to_string(), + ) + .map_err(CliError::from) + } + CliCommand::AutogenFdroidApply { + preview, + acceptance, + } => { + let db = open_main_db(&invocation.data_dir)?; + let preview = read_autogen_preview(&preview, "fdroid.autogen.preview")?; + fdroid_autogen::apply_fdroid_preview_json( + &invocation.data_dir, + &db, + &preview, + &acceptance, + ) + .map_err(CliError::from) + } CliCommand::LegacyImportRoomBundle { bundle } => { let db = open_main_db(&invocation.data_dir)?; if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { @@ -845,6 +907,11 @@ fn open_main_db(data_dir: &Path) -> Result { Ok(MainDb::open(main_db_path(data_dir))?) } +fn open_cache_db(data_dir: &Path) -> Result { + initialize_storage(data_dir)?; + Ok(CacheDb::open(cache_db_path(data_dir))?) +} + fn parse_repository_id(value: &str) -> Result { RepositoryId::new(value).map_err(|source| CliError::Usage(source.to_string())) } @@ -894,6 +961,46 @@ fn parse_priority(value: &str) -> Result { .map_err(|source| CliError::Usage(format!("invalid repository priority: {source}"))) } +fn parse_fdroid_autogen_preview_args(args: &[String]) -> Result<(PathBuf, Vec), CliError> { + let mut index = None; + let mut package_names = Vec::new(); + let mut position = 0; + while position < args.len() { + match args[position].as_str() { + "--index" => { + let path = args.get(position + 1).ok_or_else(|| { + CliError::Usage("autogen fdroid preview --index requires a path".to_owned()) + })?; + index = Some(PathBuf::from(path)); + position += 2; + } + "--package" => { + let package_name = args.get(position + 1).ok_or_else(|| { + CliError::Usage( + "autogen fdroid preview --package requires a package name".to_owned(), + ) + })?; + package_names.push(package_name.clone()); + position += 2; + } + other => { + return Err(CliError::Usage(format!( + "unsupported autogen fdroid preview argument '{other}'" + ))) + } + } + } + let index = index.ok_or_else(|| { + CliError::Usage("autogen fdroid preview requires --index ".to_owned()) + })?; + if package_names.is_empty() { + return Err(CliError::Usage( + "autogen fdroid preview requires at least one --package ".to_owned(), + )); + } + Ok((index, package_names)) +} + fn parse_autogen_acceptance(args: &[String]) -> Result { match args { [flag] if flag == "--accept-all" => Ok(AutogenAcceptance::AcceptAll), @@ -1065,6 +1172,11 @@ fn evaluate_package_directory( .map_err(|error| CliError::PackageEval(error.to_string())) } +fn read_fdroid_index(path: &Path) -> Result { + fs::read_to_string(path) + .map_err(|source| CliError::Autogen(format!("failed to read F-Droid index: {source}"))) +} + fn read_installed_inventory(path: &Path) -> Result { let bytes = fs::read(path) .map_err(|source| CliError::Autogen(format!("failed to read inventory: {source}")))?; @@ -1594,7 +1706,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index --package [--package ...]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1661,6 +1773,8 @@ impl CliCommand { Self::AutogenInstalledApply { .. } => "autogen installed apply", Self::AutogenCleanupPreview { .. } => "autogen cleanup preview", Self::AutogenCleanupApply { .. } => "autogen cleanup apply", + Self::AutogenFdroidPreview { .. } => "autogen fdroid preview", + Self::AutogenFdroidApply { .. } => "autogen fdroid apply", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", Self::LegacyImportRoomDb { .. } => "legacy import-room-db", Self::LegacyReportList => "legacy report-list", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 35754d5..5fc72ac 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -15,6 +15,7 @@ struct CliWorld { inventory: Option, autogen_preview: Option, update_fixture: Option, + fdroid_index: Option, task_request: Option, runtime_script: Option, remembered_task_id: Option, @@ -142,6 +143,35 @@ fn installed_inventory_with_android_app(world: &mut CliWorld, package_name: Stri world.inventory = Some(inventory); } +#[given(expr = "a fixture F-Droid catalog index with package {string}")] +fn fixture_fdroid_catalog_index_with_package(world: &mut CliWorld, package_name: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let index = temp.path().join("fdroid-index.xml"); + fs::write( + &index, + format!( + r#" + + + + F-Droid + App repository client + + 1.20.0 + 1020000 + {package_name}_1020000.apk + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 1234567 + + + +"# + ), + ) + .expect("write F-Droid fixture index"); + world.fdroid_index = Some(index); +} + #[given("an empty installed inventory")] fn empty_installed_inventory(world: &mut CliWorld) { let temp = world.temp.as_ref().expect("tempdir exists"); @@ -824,6 +854,46 @@ fn run_getter_autogen_installed_apply_accept_all(world: &mut CliWorld) { world.json = None; } +#[when(expr = "I run getter autogen fdroid preview for package {string}")] +fn run_getter_autogen_fdroid_preview(world: &mut CliWorld, package_name: String) { + let index = world.fdroid_index.as_ref().expect("F-Droid index exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "fdroid".to_owned(), + "preview".to_owned(), + "--index".to_owned(), + index.to_string_lossy().to_string(), + "--package".to_owned(), + package_name, + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter autogen fdroid apply for that preview with accept-all")] +fn run_getter_autogen_fdroid_apply_accept_all(world: &mut CliWorld) { + let preview = world + .autogen_preview + .as_ref() + .expect("autogen preview exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "fdroid".to_owned(), + "apply".to_owned(), + "--preview".to_owned(), + preview.to_string_lossy().to_string(), + "--accept-all".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen cleanup preview for that inventory")] fn run_getter_autogen_cleanup_preview(world: &mut CliWorld) { let inventory = world.inventory.as_ref().expect("inventory exists"); @@ -924,7 +994,7 @@ fn command_fails_with_autogen_error(world: &mut CliWorld) { assert_eq!(json["ok"], false); assert!(matches!( json["command"].as_str(), - Some("autogen cleanup apply" | "autogen installed apply") + Some("autogen cleanup apply" | "autogen installed apply" | "autogen fdroid apply") )); assert_eq!(json["error"]["code"], "autogen.error"); world.json = Some(json); @@ -1272,6 +1342,22 @@ fn package_eval_name_is(world: &mut CliWorld, package_name: String) { assert_eq!(json["data"]["package"]["name"], package_name); } +#[then(expr = "the package eval contains update version_code {int}")] +fn package_eval_contains_update_version_code(world: &mut CliWorld, version_code: i64) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "package eval"); + let updates = json["data"]["package"]["updates"] + .as_array() + .expect("updates array"); + assert!( + updates + .iter() + .any(|update| update["version_code"].as_i64() == Some(version_code)), + "package eval should contain version_code {version_code}: {updates:?}" + ); +} + #[then(expr = "the pinned package version is {string}")] fn pinned_package_version_is(world: &mut CliWorld, version: String) { let json = current_json(world); @@ -1322,6 +1408,23 @@ fn update_check_has_no_selected_update(world: &mut CliWorld) { assert_eq!(json["data"]["actions"], Value::Array(Vec::new())); } +#[then(expr = "the F-Droid autogen preview contains candidate {string}")] +fn fdroid_autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "autogen fdroid preview"); + assert_eq!(json["data"]["operation"], "fdroid.autogen.preview"); + let candidates = json["data"]["candidates"] + .as_array() + .expect("candidates array"); + assert!( + candidates + .iter() + .any(|candidate| candidate["package_id"].as_str() == Some(package_id.as_str())), + "preview should contain {package_id}: {candidates:?}" + ); +} + #[then(expr = "the autogen preview contains candidate {string}")] fn autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) { let json = current_json(world); @@ -1363,6 +1466,15 @@ fn save_autogen_preview_to_file(world: &mut CliWorld) { world.autogen_preview = Some(preview); } +#[then(expr = "the autogen repository contains generated F-Droid package {string}")] +fn autogen_repository_contains_generated_fdroid_package(world: &mut CliWorld, package_id: String) { + autogen_repository_contains_generated_package(world, package_id.clone()); + let path = autogen_repo_path(world).join(package_relative_path(&package_id)); + let content = fs::read_to_string(path.join("9999.lua")).expect("generated package readable"); + assert!(content.contains("return fdroid.package")); + assert!(content.contains("version_code = 1020000")); +} + #[then(expr = "the autogen repository contains generated package {string}")] fn autogen_repository_contains_generated_package(world: &mut CliWorld, package_id: String) { let path = autogen_repo_path(world).join(package_relative_path(&package_id)); @@ -1393,6 +1505,26 @@ fn app_list_contains_autogen_tracked_package(world: &mut CliWorld, package_id: S assert_eq!(app["package_resolution"], "generate_local_package"); } +#[then( + expr = "the F-Droid autogen preview skips package {string} because repository {string} covers it" +)] +fn fdroid_autogen_preview_skips_package_because_repository_covers_it( + world: &mut CliWorld, + package_id: String, + repository_id: String, +) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "autogen fdroid preview"); + let skipped = json["data"]["skipped"].as_array().expect("skipped array"); + let skip = skipped + .iter() + .find(|skip| skip["package_id"].as_str() == Some(package_id.as_str())) + .unwrap_or_else(|| panic!("skipped should contain {package_id}: {skipped:?}")); + assert_eq!(skip["reason"], "covered_by_higher_priority_repo"); + assert_eq!(skip["covering_repo_id"], repository_id); +} + #[then(expr = "the autogen preview skips package {string} because repository {string} covers it")] fn autogen_preview_skips_package_because_repository_covers_it( world: &mut CliWorld, diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index 6a0838d..50f6b6a 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -1,5 +1,40 @@ @getter-cli @autogen Feature: Installed app autogen + Scenario: User previews explicit F-Droid package generation without writing files + Given an initialized getter data directory + And a fixture F-Droid catalog index with package "org.fdroid.fdroid" + When I run getter autogen fdroid preview for package "org.fdroid.fdroid" + Then the command succeeds + And the output is valid JSON + And the F-Droid autogen preview contains candidate "android/f-droid/app/org.fdroid.fdroid" + And the autogen repository has not been written + + Scenario: User applies explicit F-Droid autogen and validates the generated package directory + Given an initialized getter data directory + And a fixture F-Droid catalog index with package "org.fdroid.fdroid" + When I run getter autogen fdroid preview for package "org.fdroid.fdroid" + Then the command succeeds + And I save the autogen preview to a file + When I run getter autogen fdroid apply for that preview with accept-all + Then the command succeeds + And the autogen repository contains generated F-Droid package "android/f-droid/app/org.fdroid.fdroid" + And the app list contains autogen tracked package "android/f-droid/app/org.fdroid.fdroid" + When I run getter repo validate for autogen + Then the output reports a valid repository without network + When I run getter package eval for package "android/f-droid/app/org.fdroid.fdroid" + Then the command succeeds + And the package eval contains update version_code 1020000 + + Scenario: Higher-priority repositories suppress explicit F-Droid autogen candidates + Given an initialized getter data directory + And a package-directory repository "official" with package "android/f-droid/app/org.fdroid.fdroid" + And a fixture F-Droid catalog index with package "org.fdroid.fdroid" + When I run getter repo add for that repository with priority 0 + Then the command succeeds + When I run getter autogen fdroid preview for package "org.fdroid.fdroid" + Then the command succeeds + And the F-Droid autogen preview skips package "android/f-droid/app/org.fdroid.fdroid" because repository "official" covers it + Scenario: User previews installed app fallback generation without writing files Given an initialized getter data directory And an installed inventory with Android app "com.example.autogen" labeled "Example Autogen" diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs index 8a30220..6a97c50 100644 --- a/crates/getter-core/src/autogen.rs +++ b/crates/getter-core/src/autogen.rs @@ -15,6 +15,7 @@ pub const INSTALLED_INVENTORY_VERSION: u32 = 1; pub const AUTOGEN_RECORD_FILE: &str = ".autogen.jsonc"; pub const AUTOGEN_RECORD_VERSION: u32 = 1; pub const INSTALLED_AUTOGEN_GENERATOR: &str = "installed-inventory"; +pub const FDROID_AUTOGEN_GENERATOR: &str = "fdroid-catalog"; pub const DEFAULT_AUTOGEN_REPOSITORY_ID: &str = "autogen"; pub const DEFAULT_AUTOGEN_REPOSITORY_NAME: &str = "UpgradeAll Autogen"; pub const GENERATED_MARKER: &str = "@generated by UpgradeAll getter autogen"; @@ -105,8 +106,17 @@ pub struct AutogenRecord { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "kind", rename_all = "snake_case")] pub enum AutogenRecordInput { - InstalledAndroidPackage { package_name: String }, - InstalledMagiskModule { module_id: String }, + InstalledAndroidPackage { + package_name: String, + }, + InstalledMagiskModule { + module_id: String, + }, + FdroidPackage { + endpoint_id: String, + endpoint_url: String, + package_name: String, + }, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/crates/getter-operations/src/autogen.rs b/crates/getter-operations/src/autogen.rs index 0c0b5f0..0b597f4 100644 --- a/crates/getter-operations/src/autogen.rs +++ b/crates/getter-operations/src/autogen.rs @@ -128,7 +128,12 @@ pub fn cleanup_preview_json( let mut diagnostics = Vec::new(); for package in layout.packages { let relative_path = relative_package_path(&repo_path, &package.path)?; - let ownership = match read_owned_record(&package.path, &package.id, &relative_path) { + let ownership = match read_owned_record( + &package.path, + &package.id, + &relative_path, + INSTALLED_AUTOGEN_GENERATOR, + ) { Ok(ownership) => ownership, Err(error) => { diagnostics.push(autogen_diagnostic( @@ -173,10 +178,33 @@ pub fn apply_installed_preview( preview: &Value, acceptance: &AutogenAcceptance, ) -> AutogenOperationResult { + apply_preview( + data_dir, + db, + preview, + acceptance, + "installed.preview", + INSTALLED_AUTOGEN_GENERATOR, + ) +} + +pub(crate) fn apply_preview( + data_dir: &Path, + db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, + expected_operation: &str, + expected_generator: &str, +) -> AutogenOperationResult { + if preview.get("operation").and_then(Value::as_str) != Some(expected_operation) { + return Err(AutogenOperationError::Autogen(format!( + "autogen preview operation must be '{expected_operation}'" + ))); + } let (target_alias, repo_path, target_priority) = generated_repository_config(data_dir)?; if preview.get("target_repo_id").and_then(Value::as_str) != Some(target_alias.as_str()) { return Err(AutogenOperationError::Autogen(format!( - "installed preview target_repo_id must be '{}'", + "{expected_operation} target_repo_id must be '{}'", target_alias.as_str() ))); } @@ -187,10 +215,11 @@ pub fn apply_installed_preview( for candidate in accepted { let package_id = preview_package_id(candidate)?; let relative_path = preview_relative_path(candidate)?; - let payload = preview_candidate_payload(candidate, &package_id, &relative_path)?; + let payload = + preview_candidate_payload(candidate, &package_id, &relative_path, expected_generator)?; let target_dir = safe_join(&repo_path, &relative_path)?; if target_dir.exists() { - read_owned_record(&target_dir, &package_id, &relative_path)?; + read_owned_record(&target_dir, &package_id, &relative_path, expected_generator)?; clear_directory_contents(&target_dir)?; } else { fs::create_dir_all(&target_dir).map_err(|source| { @@ -243,7 +272,12 @@ pub fn apply_cleanup_preview( ) })?; let target_dir = safe_join(&repo_path, &relative_path)?; - let ownership = read_owned_record(&target_dir, &package_id, &relative_path)?; + let ownership = read_owned_record( + &target_dir, + &package_id, + &relative_path, + INSTALLED_AUTOGEN_GENERATOR, + )?; if ownership.content_hash != expected_hash { return Err(AutogenOperationError::Autogen(format!( "cleanup preview candidate {package_id} does not match current autogen record" @@ -286,7 +320,7 @@ pub fn default_autogen_repo_path(data_dir: &Path) -> PathBuf { .repository_path(&RepositoryId::new(DEFAULT_AUTOGEN_REPOSITORY_ID).expect("valid id")) } -fn generated_repository_config( +pub(crate) fn generated_repository_config( data_dir: &Path, ) -> AutogenOperationResult<(RepositoryId, PathBuf, RepositoryPriority)> { let layout = GetterDataDirLayout::new(data_dir); @@ -300,7 +334,7 @@ fn generated_repository_config( Ok((alias, path, priority)) } -fn higher_priority_package_coverage( +pub(crate) fn higher_priority_package_coverage( db: &MainDb, target_alias: &RepositoryId, target_priority: RepositoryPriority, @@ -465,6 +499,7 @@ fn preview_candidate_payload( candidate: &Value, package_id: &PackageId, relative_path: &Path, + expected_generator: &str, ) -> AutogenOperationResult { let record_content = candidate .get("autogen_record_content") @@ -489,7 +524,13 @@ fn preview_candidate_payload( } let files = preview_generated_files(candidate)?; let record = parse_record_content(&record_content)?; - validate_record(&record, package_id, relative_path, &files)?; + validate_record( + &record, + package_id, + relative_path, + &files, + expected_generator, + )?; Ok(PreviewCandidatePayload { record_content, files, @@ -590,6 +631,7 @@ fn read_owned_record( package_dir: &Path, package_id: &PackageId, relative_path: &Path, + expected_generator: &str, ) -> AutogenOperationResult { let record_path = package_dir.join(AUTOGEN_RECORD_FILE); if !record_path.is_file() { @@ -614,7 +656,13 @@ fn read_owned_record( )) })?; let files = read_recorded_files(package_dir, &record)?; - validate_record(&record, package_id, relative_path, &files)?; + validate_record( + &record, + package_id, + relative_path, + &files, + expected_generator, + )?; Ok(LoadedAutogenRecord { content_hash: content_hash_bytes(&bytes), }) @@ -674,6 +722,7 @@ fn validate_record( package_id: &PackageId, relative_path: &Path, files: &[GeneratedPackageFile], + expected_generator: &str, ) -> AutogenOperationResult<()> { if record.version != AUTOGEN_RECORD_VERSION { return Err(AutogenOperationError::Autogen(format!( @@ -681,9 +730,9 @@ fn validate_record( record.version ))); } - if record.generator != INSTALLED_AUTOGEN_GENERATOR { + if record.generator != expected_generator { return Err(AutogenOperationError::Autogen(format!( - "autogen record generator '{}' does not match '{INSTALLED_AUTOGEN_GENERATOR}'", + "autogen record generator '{}' does not match '{expected_generator}'", record.generator ))); } diff --git a/crates/getter-operations/src/fdroid_autogen.rs b/crates/getter-operations/src/fdroid_autogen.rs new file mode 100644 index 0000000..2968d1f --- /dev/null +++ b/crates/getter-operations/src/fdroid_autogen.rs @@ -0,0 +1,661 @@ +//! Fixture-backed F-Droid explicit autogen preview/apply operations. +//! +//! This consumes getter-owned cached F-Droid catalog facts and writes ordinary +//! generated package directories into the configured generated repository. It is +//! intentionally still fixture/offline friendly: live HTTP, downloader state, +//! installer semantics, and Flutter/Kotlin provider logic remain outside this +//! slice. + +use crate::autogen::{self, AutogenAcceptance, AutogenOperationError, AutogenOperationResult}; +use crate::fdroid_catalog::{ + read_or_refresh_fdroid_catalog, FdroidEndpointConfig, DEFAULT_FDROID_ENDPOINT_ID, + DEFAULT_FDROID_ENDPOINT_URL, +}; +use crate::provider_cache::ProviderCacheMode; +use getter_core::autogen::{ + content_hash, package_relative_path, record_file_key, render_autogen_record, AutogenRecord, + AutogenRecordInput, GeneratedPackageFile, AUTOGEN_RECORD_VERSION, FDROID_AUTOGEN_GENERATOR, +}; +use getter_core::{InstalledTarget, PackageId, PackageKind}; +use getter_providers::{FdroidApp, FdroidRelease}; +use getter_storage::{CacheDb, MainDb}; +use serde::Deserialize; +use serde_json::{json, Value}; +use std::path::{Path, PathBuf}; + +pub fn preview_fdroid_packages_json( + data_dir: &Path, + main_db: &MainDb, + cache_db: &CacheDb, + request_json: &str, +) -> AutogenOperationResult { + let request: FdroidAutogenPreviewRequest = + serde_json::from_str(request_json).map_err(|source| { + AutogenOperationError::Autogen(format!( + "invalid F-Droid autogen preview request: {source}" + )) + })?; + let endpoint = request.endpoint_config(); + let catalog = read_or_refresh_fdroid_catalog(cache_db, endpoint, request.cache_mode()?, || { + request + .index_xml + .ok_or_else(|| "fixture-backed F-Droid autogen refresh requires index_xml".to_owned()) + }) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + let (target_alias, target_path, target_priority) = + autogen::generated_repository_config(data_dir)?; + let covered = + autogen::higher_priority_package_coverage(main_db, &target_alias, target_priority)?; + let mut candidates = Vec::new(); + let mut skipped = Vec::new(); + let mut diagnostics: Vec = catalog + .diagnostics + .iter() + .map(|diagnostic| { + json!({ + "code": diagnostic.code, + "message": diagnostic.message, + "cache_key": diagnostic.cache_key, + "provider": diagnostic.provider, + "stale_fetched_at_unix": diagnostic.stale_fetched_at_unix, + }) + }) + .collect(); + + for package_name in unique_requested_packages(&request.package_names) { + let package_id = fdroid_package_id(&package_name)?; + if let Some(repository_id) = covered.get(&package_id) { + skipped.push(json!({ + "package_id": package_id.to_string(), + "reason": "covered_by_higher_priority_repo", + "covering_repo_id": repository_id.as_str(), + })); + continue; + } + let Some(app) = catalog.app(&package_name) else { + diagnostics.push(json!({ + "code": "provider.fdroid.package_not_found", + "message": format!("F-Droid package '{package_name}' was not found in endpoint '{}'", catalog.endpoint.endpoint_id), + "package_id": package_id.to_string(), + "provider": "fdroid", + "endpoint_id": catalog.endpoint.endpoint_id, + })); + skipped.push(json!({ + "package_id": package_id.to_string(), + "reason": "provider_package_not_found", + })); + continue; + }; + let candidate = fdroid_candidate_json( + &catalog.endpoint.endpoint_id, + &catalog.endpoint.endpoint_url, + &package_id, + app, + )?; + candidates.push(candidate); + } + + Ok(json!({ + "operation": "fdroid.autogen.preview", + "provider": "fdroid", + "endpoint_id": catalog.endpoint.endpoint_id, + "endpoint_url": catalog.endpoint.endpoint_url, + "cache_key": catalog.cache_key, + "source": match catalog.source { + crate::provider_cache::ProviderCacheSource::Cache => "cache", + crate::provider_cache::ProviderCacheSource::Refreshed => "refreshed", + crate::provider_cache::ProviderCacheSource::Stale => "stale", + }, + "target_repo_id": target_alias.as_str(), + "target_repo_path": target_path, + "summary": { + "candidate_count": candidates.len(), + "skipped_count": skipped.len(), + "write_count": candidates.len(), + "delete_count": 0, + }, + "candidates": candidates, + "skipped": skipped, + "diagnostics": diagnostics, + })) +} + +pub fn apply_fdroid_preview_json( + data_dir: &Path, + main_db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, +) -> AutogenOperationResult { + autogen::apply_preview( + data_dir, + main_db, + preview, + acceptance, + "fdroid.autogen.preview", + FDROID_AUTOGEN_GENERATOR, + ) +} + +#[derive(Debug, Deserialize)] +struct FdroidAutogenPreviewRequest { + #[serde(default)] + endpoint_id: Option, + #[serde(default)] + endpoint_url: Option, + #[serde(default)] + mode: Option, + #[serde(default)] + index_xml: Option, + #[serde(default)] + package_names: Vec, +} + +impl FdroidAutogenPreviewRequest { + fn endpoint_config(&self) -> FdroidEndpointConfig { + FdroidEndpointConfig { + endpoint_id: self + .endpoint_id + .clone() + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_ID.to_owned()), + endpoint_url: self + .endpoint_url + .clone() + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), + } + } + + fn cache_mode(&self) -> AutogenOperationResult { + match self.mode.as_deref() { + Some("force_refresh") => Ok(ProviderCacheMode::ForceRefresh), + Some("use_cached") | None => Ok(ProviderCacheMode::UseCached), + Some(other) => Err(AutogenOperationError::Autogen(format!( + "unknown F-Droid autogen preview mode '{other}'" + ))), + } + } +} + +fn unique_requested_packages(package_names: &[String]) -> Vec { + let mut packages = package_names + .iter() + .map(|package| package.trim()) + .filter(|package| !package.is_empty()) + .map(str::to_owned) + .collect::>(); + packages.sort(); + packages.dedup(); + packages +} + +fn fdroid_package_id(package_name: &str) -> AutogenOperationResult { + PackageId::new(PackageKind::Android, format!("f-droid/app/{package_name}")) + .map_err(|source| AutogenOperationError::Autogen(source.to_string())) +} + +fn fdroid_candidate_json( + endpoint_id: &str, + endpoint_url: &str, + package_id: &PackageId, + app: &FdroidApp, +) -> AutogenOperationResult { + let relative_path = package_relative_path(package_id); + let display_name = app + .name + .as_deref() + .filter(|name| !name.trim().is_empty()) + .unwrap_or(&app.package_name); + let files = fdroid_generated_files(endpoint_url, app)?; + let record = AutogenRecord { + version: AUTOGEN_RECORD_VERSION, + generator: FDROID_AUTOGEN_GENERATOR.to_owned(), + package_id: package_id.clone(), + output_relative_path: relative_path.clone(), + input: AutogenRecordInput::FdroidPackage { + endpoint_id: endpoint_id.to_owned(), + endpoint_url: endpoint_url.to_owned(), + package_name: app.package_name.clone(), + }, + files: files + .iter() + .map(|file| { + ( + record_file_key(&file.relative_path), + file.content_hash.clone(), + ) + }) + .collect(), + }; + let record_content = render_autogen_record(&record) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + let content_hash = content_hash(&record_content); + let file_json: Vec = files + .iter() + .map(|file| { + json!({ + "relative_path": file.relative_path, + "content_hash": file.content_hash, + "content": file.content, + }) + }) + .collect(); + + Ok(json!({ + "package_id": package_id.to_string(), + "kind": package_id.kind().as_str(), + "display_name": display_name, + "installed_target": InstalledTarget::AndroidPackage { package_name: app.package_name.clone() }, + "action": "create", + "output_relative_path": relative_path, + "content_hash": content_hash, + "content": record_content, + "autogen_record_content": record_content, + "files": file_json, + "provider": "fdroid", + "endpoint_id": endpoint_id, + "upstream_id": app.package_name, + })) +} + +fn fdroid_generated_files( + endpoint_url: &str, + app: &FdroidApp, +) -> AutogenOperationResult> { + let metadata = render_pretty_json(json!({ + "type": "android:app", + "android": { "package_name": app.package_name }, + }))?; + let manifest = String::new(); + let version_lua = fdroid_version_lua(endpoint_url, app); + Ok(vec![ + generated_file("metadata.jsonc", metadata), + generated_file("Manifest", manifest), + generated_file("9999.lua", version_lua), + ]) +} + +fn fdroid_version_lua(endpoint_url: &str, app: &FdroidApp) -> String { + let mut releases = app.packages.clone(); + releases.sort_by(|left, right| { + right + .version_code + .cmp(&left.version_code) + .then_with(|| right.version.cmp(&left.version)) + .then_with(|| left.apk_name.cmp(&right.apk_name)) + }); + let updates = releases + .iter() + .map(|release| fdroid_update_lua(endpoint_url, release)) + .collect::>() + .join(",\n"); + format!( + "#!/bin/upa-lua v1\n-- @generated by UpgradeAll getter autogen (F-Droid catalog fixture)\nlocal catalog_package = {{\n package_name = {},\n updates = {{{updates}\n }},\n}}\n\nlocal fdroid = {{}}\nfunction fdroid.package(spec)\n if spec.package_name ~= catalog_package.package_name then\n error(\"F-Droid generated package_name mismatch\")\n end\n return package_version {{ updates = catalog_package.updates }}\nend\n\nreturn fdroid.package {{\n package_name = {},\n}}\n", + lua_string(&app.package_name), + lua_string(&app.package_name), + ) +} + +fn fdroid_update_lua(endpoint_url: &str, release: &FdroidRelease) -> String { + let version_code = release + .version_code + .map(|value| format!("\n version_code = {value},")) + .unwrap_or_default(); + let sha256 = release + .sha256 + .as_deref() + .map(|value| format!("\n sha256 = {},", lua_string(value))) + .unwrap_or_default(); + let size = release + .size + .map(|value| format!("\n size = {value},")) + .unwrap_or_default(); + format!( + "\n {{\n version = {},{version_code}\n source = \"fdroid\",\n artifacts = {{\n {{\n name = {},\n url = {},\n file_name = {},{sha256}{size}\n }},\n }},\n }}", + lua_string(&release.version), + lua_string(&release.apk_name), + lua_string(&fdroid_artifact_url(endpoint_url, &release.apk_name)), + lua_string(&release.apk_name), + ) +} + +fn fdroid_artifact_url(endpoint_url: &str, apk_name: &str) -> String { + format!("{}/{}", endpoint_url.trim_end_matches('/'), apk_name) +} + +fn render_pretty_json(value: Value) -> AutogenOperationResult { + serde_json::to_string_pretty(&value) + .map(|mut content| { + content.push('\n'); + content + }) + .map_err(|source| AutogenOperationError::Autogen(source.to_string())) +} + +fn generated_file(relative_path: &str, content: String) -> GeneratedPackageFile { + GeneratedPackageFile { + relative_path: PathBuf::from(relative_path), + content_hash: content_hash(&content), + content, + } +} + +fn lua_string(value: &str) -> String { + let mut escaped = String::from("\""); + for ch in value.chars() { + match ch { + '\\' => escaped.push_str("\\\\"), + '"' => escaped.push_str("\\\""), + '\n' => escaped.push_str("\\n"), + '\r' => escaped.push_str("\\r"), + '\t' => escaped.push_str("\\t"), + other => escaped.push(other), + } + } + escaped.push('"'); + escaped +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::lua::evaluate_package_directory_script; + use getter_core::repository::{ + RepositoryMetadata, RepositoryPackageDirectoryLayout, REPO_API_VERSION_V1, + }; + use getter_core::RepositoryPriority; + + const FDROID_FIXTURE: &str = r#" + + + + F-Droid + App repository client + + 1.20.0 + 1020000 + org.fdroid.fdroid_1020000.apk + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 1234567 + + + 1.19.0 + 1019000 + org.fdroid.fdroid_1019000.apk + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + 1111111 + + + + Other + + 2.0 + org.example.other_2.apk + + + +"#; + + #[test] + fn preview_generates_fdroid_package_directory_from_cached_catalog() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid", "missing.package"] + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(preview["operation"], "fdroid.autogen.preview"); + assert_eq!(preview["target_repo_id"], "autogen"); + assert_eq!(preview["source"], "refreshed"); + assert_eq!(preview["candidates"].as_array().unwrap().len(), 1); + let candidate = &preview["candidates"][0]; + assert_eq!( + candidate["package_id"], + "android/f-droid/app/org.fdroid.fdroid" + ); + assert_eq!( + candidate["output_relative_path"], + "android/f-droid/app/org.fdroid.fdroid" + ); + let record: Value = + serde_json::from_str(candidate["autogen_record_content"].as_str().unwrap()).unwrap(); + assert_eq!(record["generator"], "fdroid-catalog"); + assert_eq!(record["input"]["kind"], "fdroid_package"); + assert_eq!(record["input"]["endpoint_id"], "official"); + assert_eq!(record["input"]["endpoint_url"], "https://f-droid.org/repo"); + assert_eq!(record["input"]["package_name"], "org.fdroid.fdroid"); + let files = candidate["files"].as_array().unwrap(); + assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" + && file["content"] + .as_str() + .unwrap() + .contains("version_code = 1020000"))); + assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" + && file["content"] + .as_str() + .unwrap() + .contains("return fdroid.package"))); + assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" + && file["content"] + .as_str() + .unwrap() + .contains("package_name = \"org.fdroid.fdroid\""))); + assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" + && file["content"] + .as_str() + .unwrap() + .contains("https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk"))); + assert!(files.iter().any(|file| file["relative_path"] == "Manifest" + && file["content"].as_str().unwrap().is_empty())); + assert_eq!( + preview["skipped"][0]["reason"], + "provider_package_not_found" + ); + assert_eq!( + preview["diagnostics"][0]["code"], + "provider.fdroid.package_not_found" + ); + assert!(!temp.path().join("repo/autogen").exists()); + } + + #[test] + fn preview_skips_fdroid_package_covered_by_higher_priority_repo() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let official_root = temp.path().join("repo/official"); + let package_dir = official_root.join("android/f-droid/app/org.fdroid.fdroid"); + std::fs::create_dir_all(&package_dir).unwrap(); + std::fs::write( + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + ) + .unwrap(); + main_db + .upsert_repository( + &RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::DEFAULT, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&official_root), + None, + ) + .unwrap(); + + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid"] + }) + .to_string(), + ) + .unwrap(); + + assert!(preview["candidates"].as_array().unwrap().is_empty()); + assert_eq!( + preview["skipped"][0]["package_id"], + "android/f-droid/app/org.fdroid.fdroid" + ); + assert_eq!( + preview["skipped"][0]["reason"], + "covered_by_higher_priority_repo" + ); + assert_eq!(preview["skipped"][0]["covering_repo_id"], "official"); + } + + #[test] + fn apply_writes_valid_fdroid_package_directory() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid"] + }) + .to_string(), + ) + .unwrap(); + + let result = apply_fdroid_preview_json( + temp.path(), + &main_db, + &preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap(); + + assert_eq!(result["applied_count"], 1); + let repo_root = temp.path().join("repo/autogen"); + let package_dir = repo_root.join("android/f-droid/app/org.fdroid.fdroid"); + assert!(package_dir.join("metadata.jsonc").is_file()); + assert!(package_dir.join("Manifest").is_file()); + assert!(package_dir.join("9999.lua").is_file()); + assert!(package_dir.join(".autogen.jsonc").is_file()); + let layout = RepositoryPackageDirectoryLayout::load(&repo_root).unwrap(); + let package_dir = layout + .package(&"android/f-droid/app/org.fdroid.fdroid".parse().unwrap()) + .unwrap(); + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + let package = evaluate_package_directory_script( + &"autogen".parse().unwrap(), + package_dir, + &metadata, + script, + ) + .unwrap(); + assert_eq!(package.name, "android/f-droid/app/org.fdroid.fdroid"); + assert_eq!(package.installed.len(), 1); + assert_eq!(package.updates.len(), 2); + assert_eq!(package.updates[0].version_code, Some(1020000)); + assert_eq!( + package.updates[0].artifacts[0].sha256.as_deref(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ); + } + + #[test] + fn apply_rejects_existing_fdroid_directory_owned_by_another_generator() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid"] + }) + .to_string(), + ) + .unwrap(); + apply_fdroid_preview_json( + temp.path(), + &main_db, + &preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap(); + let record_path = temp + .path() + .join("repo/autogen/android/f-droid/app/org.fdroid.fdroid/.autogen.jsonc"); + let mut record: Value = + serde_json::from_str(&std::fs::read_to_string(&record_path).unwrap()) + .expect("record JSON parses"); + record["generator"] = Value::String("installed-inventory".to_owned()); + std::fs::write( + &record_path, + serde_json::to_string_pretty(&record).unwrap() + "\n", + ) + .unwrap(); + + let error = apply_fdroid_preview_json( + temp.path(), + &main_db, + &preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("does not match 'fdroid-catalog'")) + ); + } + + #[test] + fn apply_rejects_existing_fdroid_directory_without_ownership() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let existing = temp + .path() + .join("repo/autogen/android/f-droid/app/org.fdroid.fdroid"); + std::fs::create_dir_all(&existing).unwrap(); + std::fs::write( + existing.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, + ) + .unwrap(); + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid"] + }) + .to_string(), + ) + .unwrap(); + + let error = apply_fdroid_preview_json( + temp.path(), + &main_db, + &preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("missing .autogen.jsonc")) + ); + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index ffe59eb..ba9281b 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -6,6 +6,7 @@ //! generated package files, manifests, and tracked state live here. pub mod autogen; +pub mod fdroid_autogen; pub mod fdroid_catalog; pub mod legacy_room; pub mod provider_cache; From 6d91ce50f3c5ae41a70633f7a1798cbf64c564e3 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 11:25:32 +0800 Subject: [PATCH 39/71] feat(autogen): match F-Droid installed inventory --- crates/getter-cli/src/lib.rs | 33 +++++-- crates/getter-cli/tests/bdd_cli.rs | 20 +++++ .../features/cli/autogen_installed.feature | 10 +++ .../getter-operations/src/fdroid_autogen.rs | 88 +++++++++++++++++-- 4 files changed, 138 insertions(+), 13 deletions(-) diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 1f3d406..730767b 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -129,6 +129,7 @@ pub enum CliCommand { }, AutogenFdroidPreview { index: PathBuf, + inventory: Option, package_names: Vec, }, AutogenFdroidApply { @@ -538,9 +539,10 @@ where [domain, subject, action, rest @ ..] if domain == "autogen" && subject == "fdroid" && action == "preview" => { - let (index, package_names) = parse_fdroid_autogen_preview_args(rest)?; + let (index, inventory, package_names) = parse_fdroid_autogen_preview_args(rest)?; CliCommand::AutogenFdroidPreview { index, + inventory, package_names, } } @@ -758,14 +760,20 @@ fn execute(invocation: CliInvocation) -> Result { } CliCommand::AutogenFdroidPreview { index, + inventory, package_names, } => { let db = open_main_db(&invocation.data_dir)?; let cache_db = open_cache_db(&invocation.data_dir)?; let index_xml = read_fdroid_index(&index)?; + let installed_inventory = inventory + .as_deref() + .map(read_installed_inventory) + .transpose()?; let request = json!({ "index_xml": index_xml, "package_names": package_names, + "installed_inventory": installed_inventory, }); fdroid_autogen::preview_fdroid_packages_json( &invocation.data_dir, @@ -961,8 +969,11 @@ fn parse_priority(value: &str) -> Result { .map_err(|source| CliError::Usage(format!("invalid repository priority: {source}"))) } -fn parse_fdroid_autogen_preview_args(args: &[String]) -> Result<(PathBuf, Vec), CliError> { +fn parse_fdroid_autogen_preview_args( + args: &[String], +) -> Result<(PathBuf, Option, Vec), CliError> { let mut index = None; + let mut inventory = None; let mut package_names = Vec::new(); let mut position = 0; while position < args.len() { @@ -974,6 +985,15 @@ fn parse_fdroid_autogen_preview_args(args: &[String]) -> Result<(PathBuf, Vec { + let path = args.get(position + 1).ok_or_else(|| { + CliError::Usage( + "autogen fdroid preview --inventory requires an inventory path".to_owned(), + ) + })?; + inventory = Some(PathBuf::from(path)); + position += 2; + } "--package" => { let package_name = args.get(position + 1).ok_or_else(|| { CliError::Usage( @@ -993,12 +1013,13 @@ fn parse_fdroid_autogen_preview_args(args: &[String]) -> Result<(PathBuf, Vec".to_owned()) })?; - if package_names.is_empty() { + if package_names.is_empty() && inventory.is_none() { return Err(CliError::Usage( - "autogen fdroid preview requires at least one --package ".to_owned(), + "autogen fdroid preview requires --package or --inventory " + .to_owned(), )); } - Ok((index, package_names)) + Ok((index, inventory, package_names)) } fn parse_autogen_acceptance(args: &[String]) -> Result { @@ -1706,7 +1727,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index --package [--package ...]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 5fc72ac..6e82d68 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -873,6 +873,26 @@ fn run_getter_autogen_fdroid_preview(world: &mut CliWorld, package_name: String) world.json = None; } +#[when("I run getter autogen fdroid preview for that inventory")] +fn run_getter_autogen_fdroid_preview_for_inventory(world: &mut CliWorld) { + let index = world.fdroid_index.as_ref().expect("F-Droid index exists"); + let inventory = world.inventory.as_ref().expect("inventory exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "fdroid".to_owned(), + "preview".to_owned(), + "--index".to_owned(), + index.to_string_lossy().to_string(), + "--inventory".to_owned(), + inventory.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen fdroid apply for that preview with accept-all")] fn run_getter_autogen_fdroid_apply_accept_all(world: &mut CliWorld) { let preview = world diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index 50f6b6a..26ce9c3 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -25,6 +25,16 @@ Feature: Installed app autogen Then the command succeeds And the package eval contains update version_code 1020000 + Scenario: User previews F-Droid autogen from installed inventory + Given an initialized getter data directory + And a fixture F-Droid catalog index with package "org.fdroid.fdroid" + And an installed inventory with Android app "org.fdroid.fdroid" labeled "F-Droid" + When I run getter autogen fdroid preview for that inventory + Then the command succeeds + And the output is valid JSON + And the F-Droid autogen preview contains candidate "android/f-droid/app/org.fdroid.fdroid" + And the autogen repository has not been written + Scenario: Higher-priority repositories suppress explicit F-Droid autogen candidates Given an initialized getter data directory And a package-directory repository "official" with package "android/f-droid/app/org.fdroid.fdroid" diff --git a/crates/getter-operations/src/fdroid_autogen.rs b/crates/getter-operations/src/fdroid_autogen.rs index 2968d1f..3026de4 100644 --- a/crates/getter-operations/src/fdroid_autogen.rs +++ b/crates/getter-operations/src/fdroid_autogen.rs @@ -13,8 +13,9 @@ use crate::fdroid_catalog::{ }; use crate::provider_cache::ProviderCacheMode; use getter_core::autogen::{ - content_hash, package_relative_path, record_file_key, render_autogen_record, AutogenRecord, - AutogenRecordInput, GeneratedPackageFile, AUTOGEN_RECORD_VERSION, FDROID_AUTOGEN_GENERATOR, + content_hash, package_relative_path, record_file_key, render_autogen_record, + validate_installed_inventory, AutogenRecord, AutogenRecordInput, GeneratedPackageFile, + InstalledInventory, InstalledInventoryItem, AUTOGEN_RECORD_VERSION, FDROID_AUTOGEN_GENERATOR, }; use getter_core::{InstalledTarget, PackageId, PackageKind}; use getter_providers::{FdroidApp, FdroidRelease}; @@ -36,7 +37,9 @@ pub fn preview_fdroid_packages_json( )) })?; let endpoint = request.endpoint_config(); - let catalog = read_or_refresh_fdroid_catalog(cache_db, endpoint, request.cache_mode()?, || { + let mode = request.cache_mode()?; + let requested_package_names = requested_fdroid_package_names(&request)?; + let catalog = read_or_refresh_fdroid_catalog(cache_db, endpoint, mode, || { request .index_xml .ok_or_else(|| "fixture-backed F-Droid autogen refresh requires index_xml".to_owned()) @@ -62,7 +65,7 @@ pub fn preview_fdroid_packages_json( }) .collect(); - for package_name in unique_requested_packages(&request.package_names) { + for package_name in requested_package_names { let package_id = fdroid_package_id(&package_name)?; if let Some(repository_id) = covered.get(&package_id) { skipped.push(json!({ @@ -148,6 +151,8 @@ struct FdroidAutogenPreviewRequest { index_xml: Option, #[serde(default)] package_names: Vec, + #[serde(default)] + installed_inventory: Option, } impl FdroidAutogenPreviewRequest { @@ -175,16 +180,32 @@ impl FdroidAutogenPreviewRequest { } } -fn unique_requested_packages(package_names: &[String]) -> Vec { - let mut packages = package_names +fn requested_fdroid_package_names( + request: &FdroidAutogenPreviewRequest, +) -> AutogenOperationResult> { + if let Some(inventory) = &request.installed_inventory { + validate_installed_inventory(inventory) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + } + let mut packages = request + .package_names .iter() .map(|package| package.trim()) .filter(|package| !package.is_empty()) .map(str::to_owned) .collect::>(); + if let Some(inventory) = &request.installed_inventory { + packages.extend(inventory.items.iter().filter_map(|item| match item { + InstalledInventoryItem::AndroidPackage { package_name, .. } => { + let package_name = package_name.trim(); + (!package_name.is_empty()).then(|| package_name.to_owned()) + } + InstalledInventoryItem::MagiskModule { .. } => None, + })); + } packages.sort(); packages.dedup(); - packages + Ok(packages) } fn fdroid_package_id(package_name: &str) -> AutogenOperationResult { @@ -658,4 +679,57 @@ mod tests { matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("missing .autogen.jsonc")) ); } + + #[test] + fn preview_matches_installed_android_inventory_against_fdroid_catalog() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "installed_inventory": { + "format": "upgradeall-installed-inventory", + "version": 1, + "items": [ + { + "kind": "android", + "package_name": "org.fdroid.fdroid", + "label": "F-Droid" + }, + { + "kind": "android", + "package_name": "missing.package", + "label": "Missing" + }, + { + "kind": "magisk", + "module_id": "zygisk-next" + } + ] + } + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(preview["operation"], "fdroid.autogen.preview"); + assert_eq!(preview["candidates"].as_array().unwrap().len(), 1); + assert_eq!( + preview["candidates"][0]["package_id"], + "android/f-droid/app/org.fdroid.fdroid" + ); + assert_eq!( + preview["skipped"][0]["package_id"], + "android/f-droid/app/missing.package" + ); + assert_eq!( + preview["skipped"][0]["reason"], + "provider_package_not_found" + ); + } } From 9001bb93b23245c7b2347a722157fffd18f64ca6 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 12:31:03 +0800 Subject: [PATCH 40/71] feat(providers): add GitHub release fixtures --- Cargo.lock | 2 + crates/getter-cli/src/lib.rs | 189 +++++++- crates/getter-cli/tests/bdd_cli.rs | 123 +++++ .../cli/provider_github_releases.feature | 10 + .../getter-operations/src/github_releases.rs | 438 ++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + crates/getter-providers/Cargo.toml | 2 + crates/getter-providers/src/lib.rs | 309 ++++++++++++ 8 files changed, 1071 insertions(+), 3 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/provider_github_releases.feature create mode 100644 crates/getter-operations/src/github_releases.rs diff --git a/Cargo.lock b/Cargo.lock index 039770c..c072fcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -637,8 +637,10 @@ name = "getter-providers" version = "0.1.0" dependencies = [ "getter-core", + "regex", "roxmltree", "serde", + "serde_json", "thiserror 1.0.69", ] diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 730767b..8306d7c 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -23,6 +23,7 @@ use getter_downloader::{ }; use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; use getter_operations::fdroid_autogen; +use getter_operations::github_releases::{self, GithubReleaseOperationError}; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; use getter_operations::runtime as runtime_operations; use getter_storage::legacy_room::{ @@ -136,6 +137,15 @@ pub enum CliCommand { preview: PathBuf, acceptance: AutogenAcceptance, }, + ProviderGithubReleases { + owner: String, + repo: String, + releases: PathBuf, + asset_include: Option, + asset_exclude: Option, + include_prereleases: bool, + refresh: bool, + }, LegacyImportRoomBundle { bundle: PathBuf, }, @@ -152,6 +162,7 @@ pub enum ExitCode { Usage = 2, Storage = 10, Migration = 20, + Provider = 30, Download = 40, } @@ -179,6 +190,8 @@ pub enum CliError { Runtime(String), #[error("autogen error: {0}")] Autogen(String), + #[error("provider error: {0}")] + Provider(String), #[error("Legacy Room export bundle is invalid")] InvalidLegacyBundle { report_path: PathBuf }, #[error("Legacy Room bundle import is not implemented yet")] @@ -199,6 +212,7 @@ impl CliError { | Self::Update(_) | Self::Runtime(_) | Self::Autogen(_) => ExitCode::GenericFailure, + Self::Provider(_) => ExitCode::Provider, Self::Download(_) => ExitCode::Download, Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } @@ -217,6 +231,7 @@ impl CliError { Self::Download(_) => "download.task_error", Self::Runtime(_) => "runtime.error", Self::Autogen(_) => "autogen.error", + Self::Provider(_) => "provider.error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", Self::InvalidLegacyDb { .. } => "migration.invalid_db", @@ -234,6 +249,7 @@ impl CliError { Self::Download(_) => "Getter download task operation failed", Self::Runtime(_) => "Getter runtime operation failed", Self::Autogen(_) => "Getter autogen operation failed", + Self::Provider(_) => "Getter provider operation failed", Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", Self::UnsupportedLegacyBundle { .. } => { "Legacy Room bundle import is not implemented yet" @@ -252,7 +268,8 @@ impl CliError { | Self::Update(detail) | Self::Download(detail) | Self::Runtime(detail) - | Self::Autogen(detail) => Some(detail.as_str()), + | Self::Autogen(detail) + | Self::Provider(detail) => Some(detail.as_str()), Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } | Self::InvalidLegacyDb { .. } @@ -273,7 +290,8 @@ impl CliError { | Self::Update(_) | Self::Download(_) | Self::Runtime(_) - | Self::Autogen(_) => None, + | Self::Autogen(_) + | Self::Provider(_) => None, } } } @@ -311,6 +329,16 @@ impl From for CliError { } } +impl From for CliError { + fn from(value: GithubReleaseOperationError) -> Self { + match value { + GithubReleaseOperationError::Storage(source) => Self::Storage(source.to_string()), + GithubReleaseOperationError::InvalidRequest(detail) => Self::Usage(detail), + other => Self::Provider(other.to_string()), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliOutput { pub exit_code: ExitCode, @@ -557,6 +585,20 @@ where acceptance: parse_autogen_acceptance(rest)?, } } + [domain, provider, action, rest @ ..] + if domain == "provider" && provider == "github" && action == "releases" => + { + let args = parse_provider_github_releases_args(rest)?; + CliCommand::ProviderGithubReleases { + owner: args.owner, + repo: args.repo, + releases: args.releases, + asset_include: args.asset_include, + asset_exclude: args.asset_exclude, + include_prereleases: args.include_prereleases, + refresh: args.refresh, + } + } [domain, subject, action, flag, preview, rest @ ..] if domain == "autogen" && subject == "cleanup" @@ -797,6 +839,30 @@ fn execute(invocation: CliInvocation) -> Result { ) .map_err(CliError::from) } + CliCommand::ProviderGithubReleases { + owner, + repo, + releases, + asset_include, + asset_exclude, + include_prereleases, + refresh, + } => { + let db = open_cache_db(&invocation.data_dir)?; + let releases_json = read_github_releases_fixture(&releases)?; + let request = json!({ + "owner": owner, + "repo": repo, + "mode": if refresh { "force_refresh" } else { "use_cached" }, + "releases_json": releases_json, + "include_prereleases": include_prereleases, + "asset": { + "include": asset_include, + "exclude": asset_exclude, + }, + }); + github_releases::github_releases_json(&db, &request.to_string()).map_err(CliError::from) + } CliCommand::LegacyImportRoomBundle { bundle } => { let db = open_main_db(&invocation.data_dir)?; if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { @@ -1022,6 +1088,116 @@ fn parse_fdroid_autogen_preview_args( Ok((index, inventory, package_names)) } +#[derive(Debug, Default, PartialEq, Eq)] +struct ProviderGithubReleasesArgs { + owner: String, + repo: String, + releases: PathBuf, + asset_include: Option, + asset_exclude: Option, + include_prereleases: bool, + refresh: bool, +} + +fn parse_provider_github_releases_args( + args: &[String], +) -> Result { + let mut parsed = ProviderGithubReleasesArgs::default(); + let mut position = 0; + while position < args.len() { + match args[position].as_str() { + "--owner" => { + parsed.owner = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github releases --owner requires an owner".to_owned(), + ) + })? + .clone(); + position += 2; + } + "--repo" => { + parsed.repo = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github releases --repo requires a repo".to_owned(), + ) + })? + .clone(); + position += 2; + } + "--releases" => { + let path = args.get(position + 1).ok_or_else(|| { + CliError::Usage( + "provider github releases --releases requires a fixture path".to_owned(), + ) + })?; + parsed.releases = PathBuf::from(path); + position += 2; + } + "--asset-include" => { + parsed.asset_include = Some( + args.get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github releases --asset-include requires a regex" + .to_owned(), + ) + })? + .clone(), + ); + position += 2; + } + "--asset-exclude" => { + parsed.asset_exclude = Some( + args.get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github releases --asset-exclude requires a regex" + .to_owned(), + ) + })? + .clone(), + ); + position += 2; + } + "--include-prereleases" => { + parsed.include_prereleases = true; + position += 1; + } + "--refresh" => { + parsed.refresh = true; + position += 1; + } + other => { + return Err(CliError::Usage(format!( + "unsupported provider github releases argument '{other}'" + ))) + } + } + } + + if parsed.owner.trim().is_empty() { + return Err(CliError::Usage( + "provider github releases requires --owner ".to_owned(), + )); + } + if parsed.repo.trim().is_empty() { + return Err(CliError::Usage( + "provider github releases requires --repo ".to_owned(), + )); + } + if parsed.releases.as_os_str().is_empty() { + return Err(CliError::Usage( + "provider github releases requires --releases ".to_owned(), + )); + } + + Ok(parsed) +} + fn parse_autogen_acceptance(args: &[String]) -> Result { match args { [flag] if flag == "--accept-all" => Ok(AutogenAcceptance::AcceptAll), @@ -1198,6 +1374,12 @@ fn read_fdroid_index(path: &Path) -> Result { .map_err(|source| CliError::Autogen(format!("failed to read F-Droid index: {source}"))) } +fn read_github_releases_fixture(path: &Path) -> Result { + fs::read_to_string(path).map_err(|source| { + CliError::Provider(format!("failed to read GitHub releases fixture: {source}")) + }) +} + fn read_installed_inventory(path: &Path) -> Result { let bytes = fs::read(path) .map_err(|source| CliError::Autogen(format!("failed to read inventory: {source}")))?; @@ -1727,7 +1909,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo --releases [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1796,6 +1978,7 @@ impl CliCommand { Self::AutogenCleanupApply { .. } => "autogen cleanup apply", Self::AutogenFdroidPreview { .. } => "autogen fdroid preview", Self::AutogenFdroidApply { .. } => "autogen fdroid apply", + Self::ProviderGithubReleases { .. } => "provider github releases", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", Self::LegacyImportRoomDb { .. } => "legacy import-room-db", Self::LegacyReportList => "legacy report-list", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 6e82d68..54b584a 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -16,6 +16,7 @@ struct CliWorld { autogen_preview: Option, update_fixture: Option, fdroid_index: Option, + github_releases: Option, task_request: Option, runtime_script: Option, remembered_task_id: Option, @@ -172,6 +173,73 @@ fn fixture_fdroid_catalog_index_with_package(world: &mut CliWorld, package_name: world.fdroid_index = Some(index); } +#[given(expr = "a fixture GitHub releases response for {string}")] +fn fixture_github_releases_response(world: &mut CliWorld, project: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let releases = temp.path().join("github-releases.json"); + fs::write( + &releases, + serde_json::to_vec_pretty(&serde_json::json!([ + { + "tag_name": "v1.2.0", + "name": format!("{project} 1.2.0"), + "body": "Release notes", + "draft": false, + "prerelease": false, + "published_at": "2026-06-01T00:00:00Z", + "assets": [ + { + "name": "app-release.apk", + "content_type": "application/vnd.android.package-archive", + "size": 1234, + "browser_download_url": "https://github.com/DUpdateSystem/UpgradeAll/releases/download/v1.2.0/app-release.apk", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "name": "app-debug.apk", + "content_type": "application/vnd.android.package-archive", + "size": 2345, + "browser_download_url": "https://github.com/DUpdateSystem/UpgradeAll/releases/download/v1.2.0/app-debug.apk" + }, + { + "name": "notes.txt", + "content_type": "text/plain", + "size": 345, + "browser_download_url": "https://github.com/DUpdateSystem/UpgradeAll/releases/download/v1.2.0/notes.txt" + } + ] + }, + { + "tag_name": "v1.3.0-beta1", + "draft": false, + "prerelease": true, + "assets": [ + { + "name": "app-beta.apk", + "size": 456, + "browser_download_url": "https://github.com/DUpdateSystem/UpgradeAll/releases/download/v1.3.0-beta1/app-beta.apk" + } + ] + }, + { + "tag_name": "v1.4.0-draft", + "draft": true, + "prerelease": false, + "assets": [ + { + "name": "app-draft.apk", + "size": 567, + "browser_download_url": "https://github.com/DUpdateSystem/UpgradeAll/releases/download/v1.4.0-draft/app-draft.apk" + } + ] + } + ])) + .expect("GitHub releases serializes"), + ) + .expect("write GitHub releases fixture"); + world.github_releases = Some(releases); +} + #[given("an empty installed inventory")] fn empty_installed_inventory(world: &mut CliWorld) { let temp = world.temp.as_ref().expect("tempdir exists"); @@ -893,6 +961,34 @@ fn run_getter_autogen_fdroid_preview_for_inventory(world: &mut CliWorld) { world.json = None; } +#[when(expr = "I run getter provider github releases for owner {string} repo {string}")] +fn run_getter_provider_github_releases(world: &mut CliWorld, owner: String, repo: String) { + let releases = world + .github_releases + .as_ref() + .expect("GitHub releases fixture exists"); + let output = run_getter( + world, + [ + "provider".to_owned(), + "github".to_owned(), + "releases".to_owned(), + "--owner".to_owned(), + owner, + "--repo".to_owned(), + repo, + "--releases".to_owned(), + releases.to_string_lossy().to_string(), + "--asset-include".to_owned(), + r"\.apk$".to_owned(), + "--asset-exclude".to_owned(), + "debug".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen fdroid apply for that preview with accept-all")] fn run_getter_autogen_fdroid_apply_accept_all(world: &mut CliWorld) { let preview = world @@ -1486,6 +1582,33 @@ fn save_autogen_preview_to_file(world: &mut CliWorld) { world.autogen_preview = Some(preview); } +#[then(expr = "the GitHub release provider returns candidate {string} with artifact {string}")] +fn github_release_provider_returns_candidate( + world: &mut CliWorld, + version: String, + artifact_name: String, +) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "provider github releases"); + assert_eq!(json["data"]["operation"], "github.releases"); + assert_eq!(json["data"]["provider"], "github"); + assert_eq!(json["data"]["source"], "refreshed"); + let candidates = json["data"]["candidates"] + .as_array() + .expect("candidates array"); + assert_eq!(candidates.len(), 1); + assert_eq!(candidates[0]["version"], version); + assert_eq!(candidates[0]["source"], "github"); + assert_eq!(candidates[0]["artifacts"][0]["name"], artifact_name); + assert_eq!(candidates[0]["artifacts"][0]["file_name"], artifact_name); + assert_eq!( + candidates[0]["artifacts"][0]["url"], + "https://github.com/DUpdateSystem/UpgradeAll/releases/download/v1.2.0/app-release.apk" + ); + assert!(json["data"]["diagnostics"].as_array().unwrap().is_empty()); +} + #[then(expr = "the autogen repository contains generated F-Droid package {string}")] fn autogen_repository_contains_generated_fdroid_package(world: &mut CliWorld, package_id: String) { autogen_repository_contains_generated_package(world, package_id.clone()); diff --git a/crates/getter-cli/tests/features/cli/provider_github_releases.feature b/crates/getter-cli/tests/features/cli/provider_github_releases.feature new file mode 100644 index 0000000..c0e6a81 --- /dev/null +++ b/crates/getter-cli/tests/features/cli/provider_github_releases.feature @@ -0,0 +1,10 @@ +@getter-cli @provider @github +Feature: GitHub release provider fixtures + Scenario: User queries fixture-backed GitHub releases without writing generated packages + Given an initialized getter data directory + And a fixture GitHub releases response for "DUpdateSystem/UpgradeAll" + When I run getter provider github releases for owner "DUpdateSystem" repo "UpgradeAll" + Then the command succeeds + And the output is valid JSON + And the GitHub release provider returns candidate "v1.2.0" with artifact "app-release.apk" + And the autogen repository has not been written diff --git a/crates/getter-operations/src/github_releases.rs b/crates/getter-operations/src/github_releases.rs new file mode 100644 index 0000000..9f151c2 --- /dev/null +++ b/crates/getter-operations/src/github_releases.rs @@ -0,0 +1,438 @@ +//! Fixture-backed GitHub release cache/query operations. +//! +//! This is the first getter-owned GitHub provider slice. It parses controlled +//! GitHub REST release fixtures, runs them through the shared provider cache, +//! and normalizes release assets into getter update candidates without adding +//! live HTTP, Flutter/Kotlin provider parsing, downloader, or installer logic. + +use crate::provider_cache::{ + read_or_refresh_provider_response, ProviderCacheDiagnostic, ProviderCacheMode, + ProviderCacheOperationError, ProviderCacheRequest, ProviderCacheSource, +}; +use getter_providers::{ + github_release_update_candidates, parse_github_releases_json, GithubAssetFilter, + GithubProviderError, GithubRelease, GithubReleaseCandidateOptions, +}; +use getter_storage::{CacheDb, StorageError}; +use serde::Deserialize; +use serde_json::{json, Value}; +use sha2::{Digest, Sha512}; + +pub const GITHUB_PROVIDER_ID: &str = "github"; +pub const GITHUB_RELEASES_CACHE_VERSION: &str = "github-releases-v1"; +pub const DEFAULT_GITHUB_API_BASE_URL: &str = "https://api.github.com"; +pub const GITHUB_ASSET_NOT_FOUND: &str = "provider.github.asset_not_found"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GithubReleaseConfig { + pub api_base_url: String, + pub owner: String, + pub repo: String, +} + +impl GithubReleaseConfig { + pub fn cache_key(&self) -> String { + format!( + "{GITHUB_PROVIDER_ID}:{GITHUB_RELEASES_CACHE_VERSION}:releases:{}:{}/{}", + digest_hex(&self.api_base_url), + self.owner, + self.repo, + ) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GithubReleaseResult { + pub config: GithubReleaseConfig, + pub cache_key: String, + pub releases: Vec, + pub source: ProviderCacheSource, + pub diagnostics: Vec, +} + +#[derive(Debug, thiserror::Error)] +pub enum GithubReleaseOperationError { + #[error("provider cache operation failed: {0}")] + Cache(#[from] ProviderCacheOperationError), + #[error("storage error: {0}")] + Storage(#[from] StorageError), + #[error("GitHub provider parse failed: {0}")] + Provider(#[from] GithubProviderError), + #[error("GitHub provider serialization failed: {0}")] + Serialization(#[from] serde_json::Error), + #[error("invalid GitHub release request: {0}")] + InvalidRequest(String), +} + +pub fn read_or_refresh_github_releases( + db: &CacheDb, + config: GithubReleaseConfig, + mode: ProviderCacheMode, + refresh_json: F, +) -> Result +where + F: FnOnce() -> Result, +{ + let cache_key = config.cache_key(); + let cache_result = read_or_refresh_provider_response( + db, + ProviderCacheRequest { + cache_key: &cache_key, + provider: GITHUB_PROVIDER_ID, + mode, + }, + || { + let json = refresh_json()?; + let releases = + parse_github_releases_json(&json).map_err(|source| source.to_string())?; + serde_json::to_value(releases).map_err(|source| source.to_string()) + }, + )?; + let releases = serde_json::from_value(cache_result.response.response_json)?; + + Ok(GithubReleaseResult { + config, + cache_key, + releases, + source: cache_result.source, + diagnostics: cache_result.diagnostics, + }) +} + +pub fn github_releases_json( + db: &CacheDb, + request_json: &str, +) -> Result { + let request: GithubReleasesJsonRequest = serde_json::from_str(request_json)?; + let owner = required_non_empty(request.owner, "owner")?; + let repo = required_non_empty(request.repo, "repo")?; + let config = GithubReleaseConfig { + api_base_url: request + .api_base_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()), + owner, + repo, + }; + let mode = match request.mode.as_deref() { + Some("force_refresh") => ProviderCacheMode::ForceRefresh, + Some("use_cached") | None => ProviderCacheMode::UseCached, + Some(other) => { + return Err(GithubReleaseOperationError::InvalidRequest(format!( + "unknown mode '{other}'" + ))) + } + }; + let asset_filter = request.asset.unwrap_or_default(); + let options = GithubReleaseCandidateOptions { + include_prereleases: request.include_prereleases, + asset_filter: GithubAssetFilter { + include: asset_filter.include, + exclude: asset_filter.exclude, + }, + }; + let result = read_or_refresh_github_releases(db, config, mode, || { + request.releases_json.ok_or_else(|| { + "fixture-backed GitHub release refresh requires releases_json".to_owned() + }) + })?; + let candidates = github_release_update_candidates(&result.releases, &options)?; + let mut diagnostics = result + .diagnostics + .iter() + .map(diagnostic_json) + .collect::>(); + if candidates.is_empty() && github_eligible_release_count(&result.releases, &options) > 0 { + diagnostics.push(json!({ + "code": GITHUB_ASSET_NOT_FOUND, + "message": "no GitHub release assets matched the requested filters", + "provider": GITHUB_PROVIDER_ID, + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + })); + } + + Ok(json!({ + "operation": "github.releases", + "provider": GITHUB_PROVIDER_ID, + "api_base_url": result.config.api_base_url, + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "releases": result.releases, + "candidates": candidates, + "diagnostics": diagnostics, + })) +} + +#[derive(Debug, Deserialize)] +struct GithubReleasesJsonRequest { + #[serde(default)] + api_base_url: Option, + #[serde(default)] + owner: Option, + #[serde(default)] + repo: Option, + #[serde(default)] + mode: Option, + #[serde(default)] + releases_json: Option, + #[serde(default)] + include_prereleases: bool, + #[serde(default)] + asset: Option, +} + +#[derive(Debug, Default, Deserialize)] +struct GithubAssetFilterRequest { + #[serde(default)] + include: Option, + #[serde(default)] + exclude: Option, +} + +fn required_non_empty( + value: Option, + field: &'static str, +) -> Result { + value + .filter(|value| !value.trim().is_empty()) + .ok_or_else(|| GithubReleaseOperationError::InvalidRequest(format!("missing {field}"))) +} + +fn github_eligible_release_count( + releases: &[GithubRelease], + options: &GithubReleaseCandidateOptions, +) -> usize { + releases + .iter() + .filter(|release| !release.draft) + .filter(|release| options.include_prereleases || !release.prerelease) + .count() +} + +fn provider_source_json(source: ProviderCacheSource) -> &'static str { + match source { + ProviderCacheSource::Cache => "cache", + ProviderCacheSource::Refreshed => "refreshed", + ProviderCacheSource::Stale => "stale", + } +} + +fn diagnostic_json(diagnostic: &ProviderCacheDiagnostic) -> Value { + json!({ + "code": diagnostic.code, + "message": diagnostic.message, + "cache_key": diagnostic.cache_key, + "provider": diagnostic.provider, + "stale_fetched_at_unix": diagnostic.stale_fetched_at_unix, + }) +} + +fn digest_hex(value: &str) -> String { + let mut hasher = Sha512::new(); + hasher.update(value.as_bytes()); + format!("{:x}", hasher.finalize()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::provider_cache::{CACHE_REFRESH_FAILED, USED_STALE_CACHE}; + use getter_storage::ProviderResponseUpsert; + use serde_json::json; + use std::cell::Cell; + + const GITHUB_RELEASES_FIXTURE: &str = r#"[ + { + "tag_name": "v1.2.0", + "name": "Release 1.2.0", + "body": "Release notes", + "draft": false, + "prerelease": false, + "published_at": "2026-06-01T00:00:00Z", + "assets": [ + { + "name": "app-release.apk", + "content_type": "application/vnd.android.package-archive", + "size": 1234, + "browser_download_url": "https://github.com/example/app/releases/download/v1.2.0/app-release.apk", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "name": "app-debug.apk", + "content_type": "application/vnd.android.package-archive", + "size": 2345, + "browser_download_url": "https://github.com/example/app/releases/download/v1.2.0/app-debug.apk" + } + ] + } +]"#; + + const UPDATED_RELEASES_FIXTURE: &str = r#"[ + { + "tag_name": "v1.3.0", + "draft": false, + "prerelease": false, + "assets": [ + { + "name": "app-release.apk", + "size": 3456, + "browser_download_url": "https://github.com/example/app/releases/download/v1.3.0/app-release.apk" + } + ] + } +]"#; + + fn config() -> GithubReleaseConfig { + GithubReleaseConfig { + api_base_url: DEFAULT_GITHUB_API_BASE_URL.to_owned(), + owner: "example".to_owned(), + repo: "app".to_owned(), + } + } + + #[test] + fn cache_miss_parses_and_stores_fixture_releases() { + let db = CacheDb::open_in_memory().unwrap(); + let config = config(); + let cache_key = config.cache_key(); + + let result = + read_or_refresh_github_releases(&db, config, ProviderCacheMode::UseCached, || { + Ok(GITHUB_RELEASES_FIXTURE.to_owned()) + }) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + let cached = db.provider_response(&cache_key).unwrap().unwrap(); + assert_eq!(cached.provider, GITHUB_PROVIDER_ID); + assert_eq!(cached.response_json[0]["tag_name"], "v1.2.0"); + } + + #[test] + fn cache_hit_avoids_refreshing_fixture_releases() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_github_releases(&db, config(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_RELEASES_FIXTURE.to_owned()) + }) + .unwrap(); + let refreshed = Cell::new(false); + + let result = + read_or_refresh_github_releases(&db, config(), ProviderCacheMode::UseCached, || { + refreshed.set(true); + Ok(UPDATED_RELEASES_FIXTURE.to_owned()) + }) + .unwrap(); + + assert!(!refreshed.get()); + assert_eq!(result.source, ProviderCacheSource::Cache); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + } + + #[test] + fn forced_refresh_replaces_cached_fixture_releases() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_github_releases(&db, config(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_RELEASES_FIXTURE.to_owned()) + }) + .unwrap(); + + let result = + read_or_refresh_github_releases(&db, config(), ProviderCacheMode::ForceRefresh, || { + Ok(UPDATED_RELEASES_FIXTURE.to_owned()) + }) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!(result.releases[0].tag_name, "v1.3.0"); + } + + #[test] + fn forced_refresh_failure_returns_stale_cache_with_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + let config = config(); + let cache_key = config.cache_key(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key, + provider: GITHUB_PROVIDER_ID.to_owned(), + response_json: serde_json::from_str(GITHUB_RELEASES_FIXTURE).unwrap(), + }) + .unwrap(); + + let result = + read_or_refresh_github_releases(&db, config, ProviderCacheMode::ForceRefresh, || { + Err("HTTP 503".to_owned()) + }) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Stale); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + let codes: Vec<_> = result + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect(); + assert_eq!(codes, vec![CACHE_REFRESH_FAILED, USED_STALE_CACHE]); + } + + #[test] + fn json_operation_returns_candidates_and_asset_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + let request = json!({ + "owner": "example", + "repo": "app", + "releases_json": GITHUB_RELEASES_FIXTURE, + "asset": { "include": "\\.apk$", "exclude": "debug" } + }); + + let response = github_releases_json(&db, &request.to_string()).unwrap(); + + assert_eq!(response["operation"], "github.releases"); + assert_eq!(response["provider"], "github"); + assert_eq!(response["source"], "refreshed"); + assert_eq!(response["candidates"][0]["version"], "v1.2.0"); + assert_eq!( + response["candidates"][0]["artifacts"][0]["name"], + "app-release.apk" + ); + assert!(response["diagnostics"].as_array().unwrap().is_empty()); + } + + #[test] + fn json_operation_reports_asset_not_found_diagnostic() { + let db = CacheDb::open_in_memory().unwrap(); + let request = json!({ + "owner": "example", + "repo": "app", + "releases_json": GITHUB_RELEASES_FIXTURE, + "asset": { "include": "\\.aab$" } + }); + + let response = github_releases_json(&db, &request.to_string()).unwrap(); + + assert!(response["candidates"].as_array().unwrap().is_empty()); + assert_eq!(response["diagnostics"][0]["code"], GITHUB_ASSET_NOT_FOUND); + } + + #[test] + fn malformed_fixture_json_reports_provider_error() { + let db = CacheDb::open_in_memory().unwrap(); + let request = json!({ + "owner": "example", + "repo": "app", + "releases_json": "not-json" + }); + + let error = github_releases_json(&db, &request.to_string()).unwrap_err(); + + assert!(matches!( + error, + GithubReleaseOperationError::Cache(ProviderCacheOperationError::RefreshFailed(_)) + )); + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index ba9281b..46511ea 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -8,6 +8,7 @@ pub mod autogen; pub mod fdroid_autogen; pub mod fdroid_catalog; +pub mod github_releases; pub mod legacy_room; pub mod provider_cache; pub mod read_model; diff --git a/crates/getter-providers/Cargo.toml b/crates/getter-providers/Cargo.toml index 31d2fc4..c8be0da 100644 --- a/crates/getter-providers/Cargo.toml +++ b/crates/getter-providers/Cargo.toml @@ -5,6 +5,8 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core", default-features = false } +regex = "1" roxmltree = "0.21" serde = { version = "1", features = ["derive"] } +serde_json = "1" thiserror = "1" diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index 518d5ba..6a9ebfb 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -231,6 +231,160 @@ fn fdroid_artifact_url(base: &str, apk_name: &str) -> String { format!("{}/{}", base.trim_end_matches('/'), apk_name) } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GithubRelease { + pub tag_name: String, + #[serde(default)] + pub name: Option, + #[serde(default)] + pub body: Option, + #[serde(default)] + pub draft: bool, + #[serde(default)] + pub prerelease: bool, + #[serde(default)] + pub published_at: Option, + #[serde(default)] + pub assets: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GithubReleaseAsset { + pub name: String, + pub browser_download_url: String, + #[serde(default)] + pub label: Option, + #[serde(default)] + pub content_type: Option, + #[serde(default)] + pub size: Option, + #[serde(default)] + pub digest: Option, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct GithubReleaseCandidateOptions { + pub include_prereleases: bool, + pub asset_filter: GithubAssetFilter, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct GithubAssetFilter { + pub include: Option, + pub exclude: Option, +} + +#[derive(Debug, thiserror::Error)] +pub enum GithubProviderError { + #[error("failed to parse GitHub releases JSON: {0}")] + Json(#[from] serde_json::Error), + #[error("invalid GitHub asset {filter_kind} regex '{pattern}': {source}")] + AssetFilterRegex { + filter_kind: &'static str, + pattern: String, + #[source] + source: regex::Error, + }, +} + +pub fn parse_github_releases_json(json: &str) -> Result, GithubProviderError> { + Ok(serde_json::from_str(json)?) +} + +pub fn github_release_update_candidates( + releases: &[GithubRelease], + options: &GithubReleaseCandidateOptions, +) -> Result, GithubProviderError> { + let filter = CompiledGithubAssetFilter::new(&options.asset_filter)?; + Ok(releases + .iter() + .filter(|release| !release.draft) + .filter(|release| options.include_prereleases || !release.prerelease) + .filter_map(|release| github_release_update_candidate(release, &filter)) + .collect()) +} + +fn github_release_update_candidate( + release: &GithubRelease, + filter: &CompiledGithubAssetFilter, +) -> Option { + let artifacts = release + .assets + .iter() + .filter(|asset| filter.matches(asset)) + .map(github_asset_update_artifact) + .collect::>(); + if artifacts.is_empty() { + return None; + } + + Some(UpdateCandidate { + version: release.tag_name.clone(), + version_code: None, + channel: release.prerelease.then(|| "prerelease".to_owned()), + source: Some("github".to_owned()), + artifacts, + }) +} + +fn github_asset_update_artifact(asset: &GithubReleaseAsset) -> UpdateArtifact { + UpdateArtifact { + name: asset.name.clone(), + url: asset.browser_download_url.clone(), + file_name: Some(asset.name.clone()), + sha256: github_asset_sha256(asset.digest.as_deref()), + size: asset.size, + } +} + +fn github_asset_sha256(digest: Option<&str>) -> Option { + digest + .and_then(|value| value.strip_prefix("sha256:")) + .filter(|value| { + value.len() == 64 && value.chars().all(|character| character.is_ascii_hexdigit()) + }) + .map(str::to_owned) +} + +struct CompiledGithubAssetFilter { + include: Option, + exclude: Option, +} + +impl CompiledGithubAssetFilter { + fn new(filter: &GithubAssetFilter) -> Result { + Ok(Self { + include: compile_github_asset_regex("include", filter.include.as_deref())?, + exclude: compile_github_asset_regex("exclude", filter.exclude.as_deref())?, + }) + } + + fn matches(&self, asset: &GithubReleaseAsset) -> bool { + self.include + .as_ref() + .is_none_or(|include| include.is_match(&asset.name)) + && !self + .exclude + .as_ref() + .is_some_and(|exclude| exclude.is_match(&asset.name)) + } +} + +fn compile_github_asset_regex( + filter_kind: &'static str, + pattern: Option<&str>, +) -> Result, GithubProviderError> { + pattern + .map(|pattern| { + regex::Regex::new(pattern).map_err(|source| GithubProviderError::AssetFilterRegex { + filter_kind, + pattern: pattern.to_owned(), + source, + }) + }) + .transpose() +} + #[cfg(test)] mod tests { use super::*; @@ -342,4 +496,159 @@ mod tests { assert!(matches!(error, FdroidCatalogError::Xml(_))); } + + #[test] + fn parses_github_releases_json_into_provider_facts() { + let releases = parse_github_releases_json(GITHUB_RELEASES_FIXTURE).unwrap(); + + assert_eq!(releases.len(), 3); + let release = &releases[0]; + assert_eq!(release.tag_name, "v1.2.0"); + assert_eq!(release.name.as_deref(), Some("Release 1.2.0")); + assert_eq!(release.body.as_deref(), Some("Release notes")); + assert!(!release.draft); + assert!(!release.prerelease); + assert_eq!( + release.published_at.as_deref(), + Some("2026-06-01T00:00:00Z") + ); + assert_eq!(release.assets.len(), 3); + let asset = &release.assets[0]; + assert_eq!(asset.name, "app-release.apk"); + assert_eq!( + asset.content_type.as_deref(), + Some("application/vnd.android.package-archive") + ); + assert_eq!(asset.size, Some(1234)); + assert_eq!( + asset.browser_download_url, + "https://github.com/example/app/releases/download/v1.2.0/app-release.apk" + ); + assert_eq!( + asset.digest.as_deref(), + Some("sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ); + } + + #[test] + fn normalizes_github_release_assets_to_update_candidates_with_filters() { + let releases = parse_github_releases_json(GITHUB_RELEASES_FIXTURE).unwrap(); + let options = GithubReleaseCandidateOptions { + asset_filter: GithubAssetFilter { + include: Some(r"\.apk$".to_owned()), + exclude: Some("debug".to_owned()), + }, + ..Default::default() + }; + + let candidates = github_release_update_candidates(&releases, &options).unwrap(); + + assert_eq!(candidates.len(), 1); + assert_eq!(candidates[0].version, "v1.2.0"); + assert_eq!(candidates[0].source.as_deref(), Some("github")); + assert_eq!(candidates[0].artifacts.len(), 1); + let artifact = &candidates[0].artifacts[0]; + assert_eq!(artifact.name, "app-release.apk"); + assert_eq!( + artifact.url, + "https://github.com/example/app/releases/download/v1.2.0/app-release.apk" + ); + assert_eq!(artifact.file_name.as_deref(), Some("app-release.apk")); + assert_eq!( + artifact.sha256.as_deref(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ); + assert_eq!(artifact.size, Some(1234)); + } + + #[test] + fn ignores_unrecognized_github_asset_digests() { + let releases = parse_github_releases_json(GITHUB_INVALID_DIGEST_FIXTURE).unwrap(); + + let candidates = github_release_update_candidates( + &releases, + &GithubReleaseCandidateOptions { + asset_filter: GithubAssetFilter { + include: Some(r"\.apk$".to_owned()), + exclude: None, + }, + ..Default::default() + }, + ) + .unwrap(); + + assert_eq!(candidates[0].artifacts[0].sha256, None); + } + + const GITHUB_RELEASES_FIXTURE: &str = r#"[ + { + "tag_name": "v1.2.0", + "name": "Release 1.2.0", + "body": "Release notes", + "draft": false, + "prerelease": false, + "published_at": "2026-06-01T00:00:00Z", + "assets": [ + { + "name": "app-release.apk", + "content_type": "application/vnd.android.package-archive", + "size": 1234, + "browser_download_url": "https://github.com/example/app/releases/download/v1.2.0/app-release.apk", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "name": "app-debug.apk", + "content_type": "application/vnd.android.package-archive", + "size": 2345, + "browser_download_url": "https://github.com/example/app/releases/download/v1.2.0/app-debug.apk" + }, + { + "name": "notes.txt", + "content_type": "text/plain", + "size": 345, + "browser_download_url": "https://github.com/example/app/releases/download/v1.2.0/notes.txt" + } + ] + }, + { + "tag_name": "v1.3.0-beta1", + "draft": false, + "prerelease": true, + "assets": [ + { + "name": "app-beta.apk", + "size": 456, + "browser_download_url": "https://github.com/example/app/releases/download/v1.3.0-beta1/app-beta.apk" + } + ] + }, + { + "tag_name": "v1.4.0-draft", + "draft": true, + "prerelease": false, + "assets": [ + { + "name": "app-draft.apk", + "size": 567, + "browser_download_url": "https://github.com/example/app/releases/download/v1.4.0-draft/app-draft.apk" + } + ] + } +]"#; + + const GITHUB_INVALID_DIGEST_FIXTURE: &str = r#"[ + { + "tag_name": "v1.2.0", + "draft": false, + "prerelease": false, + "assets": [ + { + "name": "app-release.apk", + "size": 1234, + "browser_download_url": "https://github.com/example/app/releases/download/v1.2.0/app-release.apk", + "digest": "sha256:not-a-valid-sha256" + } + ] + } +]"#; } From 32e0fb337afa3c773547645b17553deb70282767 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 13:20:43 +0800 Subject: [PATCH 41/71] refactor(repository): remove flat Lua repository layout --- README.md | 2 +- crates/getter-cli/src/lib.rs | 44 +-- crates/getter-cli/tests/bdd_cli.rs | 119 +++---- .../tests/features/cli/repo_validate.feature | 8 +- crates/getter-core/src/diagnostics.rs | 166 ++------- crates/getter-core/src/lua.rs | 320 ++++-------------- crates/getter-core/src/repository.rs | 248 +------------- crates/getter-operations/src/autogen.rs | 26 +- crates/getter-operations/src/read_model.rs | 75 +--- crates/getter-operations/src/runtime.rs | 108 +----- 10 files changed, 158 insertions(+), 958 deletions(-) diff --git a/README.md b/README.md index 6fc932d..095e838 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This repository is intentionally usable outside the UpgradeAll UI. The UpgradeAl - Rust workspace split into `getter-core`, `getter-storage`, `getter-cli`, and placeholder provider/downloader/RPC/FFI crates. - Package IDs are readable, for example `android/org.fdroid.fdroid`. -- Lua package repositories use `repo.toml` plus `packages/`, `lib/`, and `templates/` directories. +- Lua package repositories are repository alias directories under `repo/`; packages are directories containing `metadata.jsonc`, optional `Manifest`, and direct child version scripts such as `1.20.0.lua` or `9999.lua`. Shared Lua classes live under repository-root `luaclass/`. - SQLite storage uses a main DB and a separate cache DB. - `getter-cli` exposes JSON command contracts for init, app list, repository registration/evaluation, package evaluation, storage validation, legacy bridge-bundle import, and sanitized legacy report listing. diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 8306d7c..7c7efbe 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -6,9 +6,9 @@ use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; -use getter_core::lua::{evaluate_package_directory_script, evaluate_package_file}; +use getter_core::lua::evaluate_package_directory_script; use getter_core::repository::{ - default_repository_priority, GetterDataDirLayout, RepositoryLayout, RepositoryMetadata, + default_repository_priority, GetterDataDirLayout, RepositoryMetadata, RepositoryPackageDirectoryLayout, REPOSITORY_ROOT_METADATA_FILE, REPO_API_VERSION_V1, }; use getter_core::runtime::{GetterRuntime, SealedActionPlan}; @@ -1225,10 +1225,6 @@ fn parse_autogen_acceptance(args: &[String]) -> Result Result { - RepositoryLayout::load(path).map_err(|source| CliError::Repository(source.to_string())) -} - fn load_package_directory_layout( path: &Path, ) -> Result { @@ -1241,20 +1237,6 @@ fn load_repository_metadata( path: &Path, priority: Option, ) -> Result { - if path.join("repo.toml").is_file() { - let layout = load_repository_layout(path)?; - if &layout.metadata.id != id { - return Err(CliError::Repository(format!( - "repo.toml id '{}' does not match requested id '{}'", - layout.metadata.id, id - ))); - } - return Ok(RepositoryMetadata { - priority: priority.unwrap_or(layout.metadata.priority), - ..layout.metadata - }); - } - RepositoryPackageDirectoryLayout::load(path) .map_err(|source| CliError::Repository(source.to_string()))?; Ok(RepositoryMetadata { @@ -1312,18 +1294,6 @@ fn evaluate_repository_packages( repo: &StoredRepository, ) -> Result, CliError> { let path = repo_path(repo)?; - if path.join("repo.toml").is_file() { - let layout = load_repository_layout(&path)?; - return layout - .packages - .iter() - .map(|package_file| { - evaluate_package_file(&layout, &package_file.path) - .map_err(|error| CliError::PackageEval(error.to_string())) - }) - .collect(); - } - let layout = load_package_directory_layout(&path)?; layout .packages @@ -1337,16 +1307,6 @@ fn evaluate_package_in_repository( package_id: &PackageId, ) -> Result, CliError> { let path = repo_path(repo)?; - if path.join("repo.toml").is_file() { - let layout = load_repository_layout(&path)?; - let Some(package_file) = layout.package_file(package_id) else { - return Ok(None); - }; - return evaluate_package_file(&layout, &package_file.path) - .map(Some) - .map_err(|error| CliError::PackageEval(error.to_string())); - } - let layout = load_package_directory_layout(&path)?; let Some(package) = layout.package(package_id) else { return Ok(None); diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 54b584a..f5f0887 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -507,8 +507,8 @@ fn fixture_lua_repository_invalid_lua(world: &mut CliWorld, repo_id: String, pac create_custom_fixture_lua_repository( world, repo_id, - package_id.clone(), - format!("return package_def {{ id = \"{package_id}\", name = "), + package_id, + "#!/bin/upa-lua v1\nreturn package_version { name = ".to_owned(), ); } @@ -521,8 +521,8 @@ fn fixture_lua_repository_invalid_schema( create_custom_fixture_lua_repository( world, repo_id, - package_id.clone(), - format!("return {{ id = \"{package_id}\" }}"), + package_id, + "#!/bin/upa-lua v1\nreturn package_version { source_priority = \"fdroid\" }".to_owned(), ); } @@ -536,7 +536,7 @@ fn fixture_lua_repository_mismatched_path( world, repo_id, package_id, - "return { id = \"android/com.termux\", name = \"Termux\" }".to_owned(), + "#!/bin/upa-lua v1\nreturn package_version { id = \"android/com.termux\", name = \"Termux\" }".to_owned(), ); } @@ -544,14 +544,10 @@ fn fixture_lua_repository_mismatched_path( fn incomplete_lua_repository(world: &mut CliWorld, repo_id: String) { let temp = world.temp.as_ref().expect("tempdir exists"); let repo_path = temp.path().join(format!("repo-{repo_id}")); - fs::create_dir_all(&repo_path).expect("create incomplete repo dir"); - fs::write( - repo_path.join("repo.toml"), - format!( - "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" - ), - ) - .expect("write repo.toml"); + let package_dir = repo_path.join("android/app/broken"); + fs::create_dir_all(&package_dir).expect("create incomplete package dir"); + fs::write(package_dir.join("metadata.jsonc"), "{not-json") + .expect("write invalid package metadata"); world.fixture_repo_id = Some(repo_id); world.fixture_repo_path = Some(repo_path); world.fixture_package_id = None; @@ -1402,7 +1398,7 @@ fn output_reports_repository_diagnostic(world: &mut CliWorld, code: String) { assert!(diagnostic["location"]["path"].as_str().is_some()); if code == "package.schema" { assert_eq!(diagnostic["package_id"], "android/org.fdroid.fdroid"); - assert_eq!(diagnostic["location"]["field"], "name"); + assert!(diagnostic["location"]["field"].as_str().is_some()); } } @@ -1958,61 +1954,34 @@ fn create_fixture_lua_repository( ) { let temp = world.temp.as_ref().expect("tempdir exists"); let repo_path = temp.path().join(format!("repo-{repo_id}")); - if let Some(package_name_path) = package_id.strip_prefix("android/app/") { - let package_dir = repo_path.join(package_relative_path(&package_id)); - fs::create_dir_all(&package_dir).expect("create package dir"); - fs::write( - package_dir.join("metadata.jsonc"), - format!( - r#"{{ "type": "android:app", "display_name": {name:?}, "android": {{ "package_name": {package_name_path:?} }} }}"#, - name = package_name, - ), - ) - .expect("write package metadata"); - fs::write(package_dir.join("Manifest"), "").expect("write Manifest"); - fs::write( - package_dir.join("9999.lua"), - format!( - r#"#!/bin/upa-lua v1 + let android_package_name = package_id + .strip_prefix("android/app/") + .or_else(|| package_id.strip_prefix("android/")) + .expect("fixture package id should be android package id"); + let package_dir = repo_path.join(package_relative_path(&package_id)); + fs::create_dir_all(&package_dir).expect("create package dir"); + fs::write( + package_dir.join("metadata.jsonc"), + format!( + r#"{{ "type": "android:app", "display_name": {name:?}, "android": {{ "package_name": {android_package_name:?} }} }}"#, + name = package_name, + ), + ) + .expect("write package metadata"); + fs::write(package_dir.join("Manifest"), "").expect("write Manifest"); + fs::write( + package_dir.join("9999.lua"), + format!( + r#"#!/bin/upa-lua v1 return package_version {{ installed = {{ - {{ kind = "android_package", package_name = "{package_name_path}" }}, - }}, -}} -"# - ), - ) - .expect("write package Lua"); - } else { - let package_name_path = package_id - .strip_prefix("android/") - .expect("fixture package id should be android package id"); - fs::create_dir_all(repo_path.join("packages/android")).expect("create packages dir"); - fs::create_dir(repo_path.join("lib")).expect("create lib dir"); - fs::create_dir(repo_path.join("templates")).expect("create templates dir"); - fs::write( - repo_path.join("repo.toml"), - format!( - "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" - ), - ) - .expect("write repo.toml"); - fs::write( - repo_path.join(format!("packages/android/{package_name_path}.lua")), - format!( - r#" -return package_def {{ - id = "{package_id}", - name = "{package_name}", - installed = {{ - {{ kind = "android_package", package_name = "{package_name_path}" }}, + {{ kind = "android_package", package_name = "{android_package_name}" }}, }}, }} "# - ), - ) - .expect("write package Lua"); - } + ), + ) + .expect("write package Lua"); world.fixture_repo_id = Some(repo_id); world.fixture_repo_path = Some(repo_path); @@ -2184,24 +2153,14 @@ fn create_custom_fixture_lua_repository( ) { let temp = world.temp.as_ref().expect("tempdir exists"); let repo_path = temp.path().join(format!("repo-{repo_id}")); - let package_name_path = package_id - .strip_prefix("android/") - .expect("fixture package id should be android package id"); - fs::create_dir_all(repo_path.join("packages/android")).expect("create packages dir"); - fs::create_dir(repo_path.join("lib")).expect("create lib dir"); - fs::create_dir(repo_path.join("templates")).expect("create templates dir"); - fs::write( - repo_path.join("repo.toml"), - format!( - "id = \"{repo_id}\"\nname = \"Fixture {repo_id}\"\npriority = 0\napi_version = \"getter.repo.v1\"\n" - ), - ) - .expect("write repo.toml"); + let package_dir = repo_path.join(package_id.replace('/', std::path::MAIN_SEPARATOR_STR)); + fs::create_dir_all(&package_dir).expect("create package dir"); fs::write( - repo_path.join(format!("packages/android/{package_name_path}.lua")), - package_source, + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "display_name": "F-Droid", "android": { "package_name": "org.fdroid.fdroid" } }"#, ) - .expect("write package Lua"); + .expect("write package metadata"); + fs::write(package_dir.join("1.20.0.lua"), package_source).expect("write package Lua"); world.fixture_repo_id = Some(repo_id); world.fixture_repo_path = Some(repo_path); diff --git a/crates/getter-cli/tests/features/cli/repo_validate.feature b/crates/getter-cli/tests/features/cli/repo_validate.feature index 9c6d1a1..55604f6 100644 --- a/crates/getter-cli/tests/features/cli/repo_validate.feature +++ b/crates/getter-cli/tests/features/cli/repo_validate.feature @@ -27,16 +27,16 @@ Feature: Getter CLI repository validation Then the command succeeds And the output reports repository diagnostic "package.schema" - Scenario: User receives diagnostics for package path mismatch + Scenario: User receives diagnostics for package scripts declaring an id Given an initialized getter data directory And a fixture Lua repository "broken" with mismatched package path "android/org.fdroid.fdroid" When I run getter repo validate for that repository Then the command succeeds - And the output reports repository diagnostic "package.domain" + And the output reports repository diagnostic "package.schema" - Scenario: User receives diagnostics for an incomplete repository layout + Scenario: User receives diagnostics for invalid package metadata Given an initialized getter data directory And an incomplete Lua repository "broken" When I run getter repo validate for that repository Then the command succeeds - And the output reports repository diagnostic "repository.missing_directory" + And the output reports repository diagnostic "package.metadata" diff --git a/crates/getter-core/src/diagnostics.rs b/crates/getter-core/src/diagnostics.rs index a78ff21..0fe663c 100644 --- a/crates/getter-core/src/diagnostics.rs +++ b/crates/getter-core/src/diagnostics.rs @@ -3,10 +3,9 @@ //! Diagnostics are getter-owned DTOs used by CLI and future app bridges. They //! describe what getter observed; Flutter should only render them. -use crate::lua::{evaluate_package_directory_script, evaluate_package_file, LuaPackageError}; +use crate::lua::{evaluate_package_directory_script, LuaPackageError}; use crate::repository::{ - package_cache_key, InvalidPackageDirectory, RepositoryLayout, RepositoryLoadError, - RepositoryPackageDirectoryLayout, + InvalidPackageDirectory, RepositoryLoadError, RepositoryPackageDirectoryLayout, }; use crate::PackageId; use serde::{Deserialize, Serialize}; @@ -64,36 +63,7 @@ impl RepositoryValidationReport { /// This intentionally loads and evaluates local Lua package files only. It must /// not perform provider/network checks. pub fn validate_repository_path(path: impl AsRef) -> RepositoryValidationReport { - let root = path.as_ref(); - if root.join("repo.toml").is_file() { - validate_legacy_repository_path(root) - } else { - validate_package_directory_repository_path(root) - } -} - -fn validate_legacy_repository_path(root: &Path) -> RepositoryValidationReport { - let layout = match RepositoryLayout::load(root) { - Ok(layout) => layout, - Err(error) => { - return RepositoryValidationReport::new(0, vec![repository_load_diagnostic(error)]) - } - }; - - let mut diagnostics = Vec::new(); - let mut package_count = 0usize; - for package_file in &layout.packages { - if let Err(error) = package_cache_key(&layout, package_file) { - diagnostics.push(repository_load_diagnostic(error)); - continue; - } - match evaluate_package_file(&layout, &package_file.path) { - Ok(_) => package_count += 1, - Err(error) => diagnostics.push(lua_diagnostic(error, Some(package_file.id.clone()))), - } - } - - RepositoryValidationReport::new(package_count, diagnostics) + validate_package_directory_repository_path(path.as_ref()) } fn validate_package_directory_repository_path(root: &Path) -> RepositoryValidationReport { @@ -167,40 +137,20 @@ fn repository_load_diagnostic(error: RepositoryLoadError) -> PackageValidationDi path, format!("failed to read repository root: {source}"), ), - RepositoryLoadError::MissingGeneratedRepository { alias, path } => ( - "repository.missing_generated_repository", - path, - format!("configured generated repository '{alias}' does not exist"), - ), - RepositoryLoadError::ReadRepoToml { path, source } => ( - "repository.read_repo_toml", - path, - format!("failed to read repo.toml: {source}"), - ), - RepositoryLoadError::ParseRepoToml { path, source } => ( - "repository.parse_repo_toml", - path, - format!("failed to parse repo.toml: {source}"), - ), RepositoryLoadError::RepositoryId(source) => ( "repository.invalid_id", - PathBuf::from("repo.toml"), + PathBuf::from("repo"), source.to_string(), ), - RepositoryLoadError::UnsupportedApiVersion(version) => ( - "repository.unsupported_api_version", - PathBuf::from("repo.toml"), - format!("unsupported repository api_version '{version}'"), - ), - RepositoryLoadError::MissingDirectory { path, directory } => ( - "repository.missing_directory", + RepositoryLoadError::MissingGeneratedRepository { alias, path } => ( + "repository.missing_generated_repository", path, - format!("missing required directory '{directory}'"), + format!("configured generated repository '{alias}' does not exist"), ), RepositoryLoadError::ReadPackagesDir { path, source } => ( - "repository.read_packages_dir", + "repository.read_package_directory", path, - format!("failed to read packages directory: {source}"), + format!("failed to read package directory: {source}"), ), RepositoryLoadError::InvalidPackagePath { path, reason } => { ("repository.invalid_package_path", path, reason) @@ -351,40 +301,6 @@ mod tests { use super::*; use std::fs; - fn fixture_repo() -> tempfile::TempDir { - let temp = tempfile::tempdir().unwrap(); - let root = temp.path(); - fs::write( - root.join("repo.toml"), - r#"id = "official" -name = "Official" -priority = 0 -api_version = "getter.repo.v1" -"#, - ) - .unwrap(); - fs::create_dir_all(root.join("packages/android")).unwrap(); - fs::create_dir(root.join("lib")).unwrap(); - fs::create_dir(root.join("templates")).unwrap(); - temp - } - - #[test] - fn valid_repository_has_no_diagnostics() { - let temp = fixture_repo(); - fs::write( - temp.path().join("packages/android/org.fdroid.fdroid.lua"), - r#"return package_def { id = "android/org.fdroid.fdroid", name = "F-Droid" }"#, - ) - .unwrap(); - - let report = validate_repository_path(temp.path()); - assert!(report.valid, "{report:?}"); - assert_eq!(report.package_count, 1); - assert!(report.diagnostics.is_empty()); - assert!(!report.network_required); - } - #[test] fn package_directory_repository_has_no_diagnostics() { let temp = tempfile::tempdir().unwrap(); @@ -478,79 +394,55 @@ api_version = "getter.repo.v1" } #[test] - fn missing_directory_is_stable_repository_diagnostic() { + fn lua_schema_error_is_stable_package_diagnostic() { let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); fs::write( - temp.path().join("repo.toml"), - r#"id = "official" -name = "Official" -api_version = "getter.repo.v1" -"#, + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, ) .unwrap(); - - let report = validate_repository_path(temp.path()); - assert!(!report.valid); - assert_eq!(report.diagnostics[0].code, "repository.missing_directory"); - } - - #[test] - fn lua_schema_error_is_stable_package_diagnostic() { - let temp = fixture_repo(); fs::write( - temp.path().join("packages/android/org.fdroid.fdroid.lua"), - r#"return { id = "android/org.fdroid.fdroid" }"#, + package_dir.join("9999.lua"), + "#!/bin/upa-lua v1\nreturn package_version { source_priority = \"fdroid\" }", ) .unwrap(); let report = validate_repository_path(temp.path()); assert!(!report.valid); assert_eq!(report.diagnostics[0].code, "package.schema"); - assert_eq!( - report.diagnostics[0].location.field.as_deref(), - Some("name") - ); assert_eq!( report.diagnostics[0] .package_id .as_ref() .unwrap() .to_string(), - "android/org.fdroid.fdroid" + "android/app/org.fdroid.fdroid" ); } #[test] - fn package_id_path_mismatch_is_domain_diagnostic() { - let temp = fixture_repo(); + fn package_version_script_id_field_is_schema_diagnostic() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); fs::write( - temp.path().join("packages/android/org.fdroid.fdroid.lua"), - r#"return { id = "android/com.termux", name = "Termux" }"#, + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "org.fdroid.fdroid" } }"#, ) .unwrap(); - - let report = validate_repository_path(temp.path()); - assert!(!report.valid); - assert_eq!(report.diagnostics[0].code, "package.domain"); - } - - #[test] - fn unsupported_api_version_is_stable_repository_diagnostic() { - let temp = tempfile::tempdir().unwrap(); fs::write( - temp.path().join("repo.toml"), - r#"id = "official" -name = "Official" -api_version = "getter.repo.v2" -"#, + package_dir.join("9999.lua"), + "#!/bin/upa-lua v1\nreturn package_version { id = \"android/app/org.fdroid.fdroid\" }", ) .unwrap(); let report = validate_repository_path(temp.path()); assert!(!report.valid); - assert_eq!( - report.diagnostics[0].code, - "repository.unsupported_api_version" - ); + assert_eq!(report.diagnostics[0].code, "package.schema"); + assert!(report.diagnostics[0] + .message + .contains("must not be declared")); } } diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 8e33718..2507fd7 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -2,8 +2,7 @@ use crate::repository::{ PackageDirectory, PackageDirectoryMetadata, PackageLuaPermission, PackageTypeMetadata, - PackageVersionScript, RepositoryLayout, RepositoryLoadError, LUA_API_SHEBANG_V1, - REPOSITORY_LUACLASS_DIR, + PackageVersionScript, RepositoryLoadError, LUA_API_SHEBANG_V1, REPOSITORY_LUACLASS_DIR, }; use crate::{ InstalledTarget, PackageId, PackagePermissions, RepositoryId, ResolvedPackage, UpdateArtifact, @@ -48,35 +47,6 @@ pub enum LuaPackageError { }, } -/// Evaluate and validate a Lua package file from a repository layout. -pub fn evaluate_package_file( - repository: &RepositoryLayout, - path: impl AsRef, -) -> Result { - let path = path.as_ref(); - let source = fs::read_to_string(path).map_err(|source| LuaPackageError::ReadFile { - path: path.to_path_buf(), - source, - })?; - evaluate_package_source(repository, path, &source) -} - -/// Evaluate package source text. This is public for focused tests and future CLI -/// plumbing; repository callers should normally use [`evaluate_package_file`]. -pub fn evaluate_package_source( - repository: &RepositoryLayout, - path: impl AsRef, - source: &str, -) -> Result { - let path = path.as_ref().to_path_buf(); - let json = evaluate_package_source_to_json( - &LuaRepositoryEnvironment::Legacy(repository), - &path, - source, - )?; - validate_package_json(repository, &path, json) -} - pub fn evaluate_package_directory_script( repository_id: &RepositoryId, package: &PackageDirectory, @@ -140,7 +110,6 @@ fn evaluate_package_source_to_json( } enum LuaRepositoryEnvironment<'a> { - Legacy(&'a RepositoryLayout), PackageDirectory { package: &'a PackageDirectory }, } @@ -152,17 +121,6 @@ fn configure_package_path( package.set("cpath", "")?; package.set("loadlib", Value::Nil)?; match environment { - LuaRepositoryEnvironment::Legacy(repository) => { - let lib_pattern = repository.lib_dir.join("?.lua"); - let nested_lib_pattern = repository.lib_dir.join("?/init.lua"); - let new_path = format!( - "{};{}", - lib_pattern.to_string_lossy(), - nested_lib_pattern.to_string_lossy() - ); - package.set("path", new_path)?; - install_lib_prefix_searcher(lua, &package, repository.lib_dir.clone())?; - } LuaRepositoryEnvironment::PackageDirectory { package: package_dir, } => { @@ -207,16 +165,6 @@ fn remove_unsafe_globals(lua: &Lua) -> mlua::Result<()> { Ok(()) } -fn install_lib_prefix_searcher(lua: &Lua, package: &Table, lib_dir: PathBuf) -> mlua::Result<()> { - install_prefixed_file_searcher( - lua, - package, - "lib.", - lib_dir, - "constrained repository lib searcher only handles lib.* modules", - ) -} - fn install_luaclass_prefix_searcher( lua: &Lua, package: &Table, @@ -315,9 +263,8 @@ fn install_helpers(lua: &Lua, environment: &LuaRepositoryEnvironment<'_>) -> mlu lua.globals().set("android_app", package_fn.clone())?; lua.globals().set("magisk_module", package_fn.clone())?; lua.globals().set("generic_package", package_fn)?; - if let LuaRepositoryEnvironment::PackageDirectory { package } = environment { - install_package_file_helpers(lua, package)?; - } + let LuaRepositoryEnvironment::PackageDirectory { package } = environment; + install_package_file_helpers(lua, package)?; Ok(()) } @@ -472,45 +419,6 @@ fn is_array_table(table: &Table) -> mlua::Result { Ok(count == len) } -fn validate_package_json( - repository: &RepositoryLayout, - path: &Path, - value: JsonValue, -) -> Result { - let object = value.as_object().ok_or_else(|| LuaPackageError::Schema { - path: path.to_path_buf(), - message: "package value must be an object".to_owned(), - })?; - - let id = required_string(path, object, "id")?; - let id: PackageId = id.parse().map_err(|source| LuaPackageError::Schema { - path: path.to_path_buf(), - message: format!("invalid package id: {source}"), - })?; - let path_id = crate::repository::package_id_from_path(&repository.packages_dir, path).map_err( - |source| LuaPackageError::Domain { - path: path.to_path_buf(), - message: format!("failed to derive package id from path: {source}"), - }, - )?; - if id != path_id { - return Err(LuaPackageError::Domain { - path: path.to_path_buf(), - message: format!("package id '{id}' does not match path-derived id '{path_id}'"), - }); - } - - let name = required_string(path, object, "name")?.to_owned(); - package_from_version_json( - repository.metadata.id.clone(), - id, - name, - PackagePermissions::default(), - path, - object, - ) -} - fn validate_package_directory_version_json( repository_id: &RepositoryId, package: &PackageDirectory, @@ -814,45 +722,48 @@ fn parse_string_array( #[cfg(test)] mod tests { use super::*; - use crate::repository::{RepositoryLayout, RepositoryPackageDirectoryLayout}; + use crate::repository::RepositoryPackageDirectoryLayout; use crate::RepositoryId; use std::fs; - fn fixture_repo() -> (tempfile::TempDir, RepositoryLayout, PathBuf) { - let temp = tempfile::tempdir().unwrap(); - let root = temp.path(); - fs::write( - root.join("repo.toml"), - r#"id = "official" -name = "UpgradeAll Official" -priority = 0 -api_version = "getter.repo.v1" -"#, + fn evaluate_single_package_directory( + root: &Path, + repository_id: &str, + ) -> Result { + let layout = RepositoryPackageDirectoryLayout::load(root).unwrap(); + let package = &layout.packages[0]; + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + evaluate_package_directory_script( + &RepositoryId::new(repository_id).unwrap(), + package, + &metadata, + script, ) - .unwrap(); - fs::create_dir(root.join("packages")).unwrap(); - fs::create_dir(root.join("packages/android")).unwrap(); - fs::create_dir(root.join("lib")).unwrap(); - fs::create_dir(root.join("templates")).unwrap(); - let package_path = root.join("packages/android/org.fdroid.fdroid.lua"); - fs::write(&package_path, "return {}").unwrap(); - let layout = RepositoryLayout::load(root).unwrap(); - (temp, layout, package_path) } #[test] fn evaluates_json_like_lua_package_table() { - let (_temp, layout, package_path) = fixture_repo(); + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); fs::write( - &package_path, - r#" -return package_def { - id = "android/org.fdroid.fdroid", - name = "F-Droid", + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "F-Droid", + "android": { "package_name": "org.fdroid.fdroid" }, + "lua": { "9999.lua": { "permission": ["allow_free_network"] } } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { installed = { { kind = "android_package", package_name = "org.fdroid.fdroid" }, }, - permissions = { free_network = true }, source_priority = { "github", "fdroid" }, updates = { { @@ -876,8 +787,8 @@ return package_def { ) .unwrap(); - let package = evaluate_package_file(&layout, &package_path).unwrap(); - assert_eq!(package.id.to_string(), "android/org.fdroid.fdroid"); + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + assert_eq!(package.id.to_string(), "android/app/org.fdroid.fdroid"); assert_eq!(package.repository.as_str(), "official"); assert_eq!(package.name, "F-Droid"); assert_eq!( @@ -1213,130 +1124,26 @@ return package_version { name = getter_builtin.read_package_file("name.txt") } assert_eq!(package.name, "from builtin"); } - #[test] - fn rejects_package_id_that_does_not_match_path() { - let (_temp, layout, package_path) = fixture_repo(); - fs::write( - &package_path, - r#"return { id = "android/com.termux", name = "Termux" }"#, - ) - .unwrap(); - - let err = evaluate_package_file(&layout, &package_path).unwrap_err(); - assert!(matches!(err, LuaPackageError::Domain { .. })); - } - - #[test] - fn require_can_load_repository_lib_modules() { - let (_temp, layout, package_path) = fixture_repo(); - fs::write( - layout.lib_dir.join("android.lua"), - r#" -return { - local_app = function(input) - return { - id = input.id, - name = input.name, - installed = { - { kind = "android_package", package_name = input.package_name }, - }, - } - end -} -"#, - ) - .unwrap(); - fs::write( - &package_path, - r#" -local android = require("android") -return android.local_app { - id = "android/org.fdroid.fdroid", - name = "F-Droid", - package_name = "org.fdroid.fdroid", -} -"#, - ) - .unwrap(); - - let package = evaluate_package_file(&layout, &package_path).unwrap(); - assert_eq!(package.name, "F-Droid"); - assert_eq!(package.installed.len(), 1); - } - - #[test] - fn require_can_load_repository_lib_modules_with_lib_prefix() { - let (_temp, layout, package_path) = fixture_repo(); - fs::write( - layout.lib_dir.join("android.lua"), - r#" -return { - local_app = function(input) - return { - id = input.id, - name = input.name, - installed = { - { kind = "android_package", package_name = input.package_name }, - }, - } - end -} -"#, - ) - .unwrap(); - fs::write( - &package_path, - r#" -local android = require("lib.android") -return android.local_app { - id = "android/org.fdroid.fdroid", - name = "F-Droid", - package_name = "org.fdroid.fdroid", -} -"#, - ) - .unwrap(); - - let package = evaluate_package_file(&layout, &package_path).unwrap(); - assert_eq!(package.name, "F-Droid"); - assert_eq!(package.installed.len(), 1); - } - - #[test] - fn lib_prefixed_searcher_does_not_expose_repository_templates() { - let (_temp, layout, package_path) = fixture_repo(); - fs::write( - layout.templates_dir.join("android.lua"), - r#"return { leaked = true }"#, - ) - .unwrap(); - fs::write( - &package_path, - r#" -local ok = pcall(require, "templates.android") -return { - id = "android/org.fdroid.fdroid", - name = ok and "leaked" or "F-Droid", -} -"#, - ) - .unwrap(); - - let package = evaluate_package_file(&layout, &package_path).unwrap(); - assert_eq!(package.name, "F-Droid"); - } - #[test] fn lua_environment_does_not_expose_process_or_file_system_globals() { let temp = tempfile::tempdir().unwrap(); let side_effect = temp.path().join("side-effect"); - let (_repo_temp, layout, package_path) = fixture_repo(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); fs::write( - &package_path, + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "F-Droid", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), format!( - r#" -return {{ - id = "android/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +return package_version {{ name = (os == nil and io == nil and debug == nil and dofile == nil and loadfile == nil and package == nil) and "F-Droid" or "leaked", side_effect = rawget(_G, "os") and os.execute("touch {}"), }} @@ -1346,40 +1153,43 @@ return {{ ) .unwrap(); - let package = evaluate_package_file(&layout, &package_path).unwrap(); + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); assert_eq!(package.name, "F-Droid"); assert!(!side_effect.exists()); } #[test] fn repository_root_is_not_exposed_even_when_cwd_is_repository_root() { - let (_temp, layout, package_path) = fixture_repo(); + let temp = tempfile::tempdir().unwrap(); fs::write( - layout.root.join("rootleak.lua"), + temp.path().join("rootleak.lua"), r#"return { leaked = true }"#, ) .unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); fs::write( - layout.templates_dir.join("android.lua"), - r#"return { leaked = true }"#, + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "F-Droid", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, ) .unwrap(); fs::write( - &package_path, - r#" + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 local root_ok = pcall(require, "rootleak") local template_ok = pcall(require, "templates.android") -return { - id = "android/org.fdroid.fdroid", - name = (root_ok or template_ok) and "leaked" or "F-Droid", -} +return package_version { name = (root_ok or template_ok) and "leaked" or "F-Droid" } "#, ) .unwrap(); let original_cwd = std::env::current_dir().unwrap(); - std::env::set_current_dir(&layout.root).unwrap(); - let result = evaluate_package_file(&layout, &package_path); + std::env::set_current_dir(temp.path()).unwrap(); + let result = evaluate_single_package_directory(temp.path(), "official"); std::env::set_current_dir(original_cwd).unwrap(); let package = result.unwrap(); diff --git a/crates/getter-core/src/repository.rs b/crates/getter-core/src/repository.rs index f5f0e99..1ad1360 100644 --- a/crates/getter-core/src/repository.rs +++ b/crates/getter-core/src/repository.rs @@ -24,16 +24,6 @@ pub const LUA_API_SHEBANG_V1: &str = "#!/bin/upa-lua v1"; pub const LOCAL_REPOSITORY_ALIAS: &str = "local"; pub const DEFAULT_GENERATED_REPOSITORY_ALIAS: &str = "autogen"; -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct RepositoryLayout { - pub root: PathBuf, - pub metadata: RepositoryMetadata, - pub packages_dir: PathBuf, - pub lib_dir: PathBuf, - pub templates_dir: PathBuf, - pub packages: Vec, -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct RepositoryMetadata { pub id: RepositoryId, @@ -236,12 +226,6 @@ pub fn generated_repository_target( } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct PackageFile { - pub id: PackageId, - pub path: PathBuf, -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct RepositoryPackageDirectoryLayout { pub root: PathBuf, @@ -365,27 +349,8 @@ pub enum RepositoryLoadError { }, #[error("configured generated repository '{alias}' does not exist at {path}")] MissingGeneratedRepository { alias: RepositoryId, path: PathBuf }, - #[error("failed to read repo.toml at {path}: {source}")] - ReadRepoToml { - path: PathBuf, - #[source] - source: std::io::Error, - }, - #[error("failed to parse repo.toml at {path}: {source}")] - ParseRepoToml { - path: PathBuf, - #[source] - source: toml::de::Error, - }, - #[error("invalid repository id in repo.toml: {0}")] + #[error("invalid repository id: {0}")] RepositoryId(#[from] RepositoryIdError), - #[error("unsupported repository api_version '{0}'")] - UnsupportedApiVersion(String), - #[error("repository path {path} is missing required directory '{directory}'")] - MissingDirectory { - path: PathBuf, - directory: &'static str, - }, #[error("failed to read packages directory {path}: {source}")] ReadPackagesDir { path: PathBuf, @@ -490,104 +455,6 @@ impl PackageDirectoryMetadata { } } -impl RepositoryLayout { - pub fn load(root: impl AsRef) -> Result { - let root = root.as_ref().to_path_buf(); - let repo_toml_path = root.join("repo.toml"); - let raw = fs::read_to_string(&repo_toml_path).map_err(|source| { - RepositoryLoadError::ReadRepoToml { - path: repo_toml_path.clone(), - source, - } - })?; - let raw_metadata: RawRepositoryMetadata = - toml::from_str(&raw).map_err(|source| RepositoryLoadError::ParseRepoToml { - path: repo_toml_path.clone(), - source, - })?; - let api_version = raw_metadata.api_version; - if api_version != REPO_API_VERSION_V1 { - return Err(RepositoryLoadError::UnsupportedApiVersion(api_version)); - } - let metadata = RepositoryMetadata { - id: RepositoryId::new(raw_metadata.id)?, - name: raw_metadata.name, - priority: RepositoryPriority::new(raw_metadata.priority.unwrap_or_default()), - api_version, - }; - - let packages_dir = root.join("packages"); - let lib_dir = root.join("lib"); - let templates_dir = root.join("templates"); - require_dir(&root, &packages_dir, "packages")?; - require_dir(&root, &lib_dir, "lib")?; - require_dir(&root, &templates_dir, "templates")?; - - let mut packages = Vec::new(); - collect_package_files(&packages_dir, &packages_dir, &mut packages)?; - packages.sort_by_key(|package| package.id.to_string()); - - Ok(Self { - root, - metadata, - packages_dir, - lib_dir, - templates_dir, - packages, - }) - } - - pub fn package_file(&self, id: &PackageId) -> Option<&PackageFile> { - self.packages.iter().find(|package| &package.id == id) - } -} - -#[derive(Debug, Deserialize)] -struct RawRepositoryMetadata { - id: String, - name: String, - #[serde(default)] - priority: Option, - api_version: String, -} - -fn require_dir( - root: &Path, - path: &Path, - directory: &'static str, -) -> Result<(), RepositoryLoadError> { - if path.is_dir() { - Ok(()) - } else { - Err(RepositoryLoadError::MissingDirectory { - path: root.to_path_buf(), - directory, - }) - } -} - -fn collect_package_files( - packages_root: &Path, - current: &Path, - out: &mut Vec, -) -> Result<(), RepositoryLoadError> { - for entry in read_dir_entries(current)? { - let path = entry.path(); - let file_type = entry_file_type(&entry, current)?; - if file_type.is_dir() { - collect_package_files(packages_root, &path, out)?; - } else if file_type.is_file() - && path - .extension() - .is_some_and(|ext| ext == LUA_SCRIPT_EXTENSION) - { - let id = package_id_from_path(packages_root, &path)?; - out.push(PackageFile { id, path }); - } - } - Ok(()) -} - fn collect_package_directories( repository_root: &Path, current: &Path, @@ -773,27 +640,6 @@ pub fn package_id_from_package_dir( package_id_from_relative_path(package_dir, relative) } -pub fn package_id_from_path( - packages_root: impl AsRef, - path: impl AsRef, -) -> Result { - let packages_root = packages_root.as_ref(); - let path = path.as_ref(); - let relative = - path.strip_prefix(packages_root) - .map_err(|_| RepositoryLoadError::InvalidPackagePath { - path: path.to_path_buf(), - reason: format!("path is not under {}", packages_root.display()), - })?; - if relative.extension().is_none_or(|ext| ext != "lua") { - return Err(RepositoryLoadError::InvalidPackagePath { - path: path.to_path_buf(), - reason: "package file must have .lua extension".to_owned(), - }); - } - package_id_from_relative_path(path, &relative.with_extension("")) -} - fn package_id_from_relative_path( path: &Path, relative: &Path, @@ -826,18 +672,6 @@ fn package_id_from_relative_path( }) } -pub fn package_cache_key( - repository: &RepositoryLayout, - package: &PackageFile, -) -> Result { - Ok(RepositoryPackageCacheKey { - repository_id: repository.metadata.id.clone(), - package_id: package.id.clone(), - api_version: repository.metadata.api_version.clone(), - package_file_hash: package_file_content_hash(&package.path)?, - }) -} - pub fn package_directory_cache_key( repository_id: &RepositoryId, package: &PackageDirectory, @@ -973,7 +807,6 @@ where #[cfg(test)] mod tests { use super::*; - use std::io::Write; #[test] fn data_dir_layout_uses_repo_and_rc_roots() { @@ -1123,14 +956,6 @@ mod tests { )); } - #[test] - fn derives_package_id_from_lua_path() { - let root = PathBuf::from("repo/packages"); - let id = - package_id_from_path(&root, "repo/packages/android/org.fdroid.fdroid.lua").unwrap(); - assert_eq!(id.to_string(), "android/org.fdroid.fdroid"); - } - #[test] fn derives_package_id_from_package_directory() { let root = PathBuf::from("repo/official"); @@ -1280,77 +1105,6 @@ mod tests { .contains("unsupported package kind")); } - #[test] - fn loads_repository_layout_with_required_directories() { - let temp = tempfile::tempdir().unwrap(); - let root = temp.path(); - fs::write( - root.join("repo.toml"), - r#"id = "official" -name = "UpgradeAll Official" -priority = 0 -api_version = "getter.repo.v1" -"#, - ) - .unwrap(); - fs::create_dir(root.join("packages")).unwrap(); - fs::create_dir(root.join("packages/android")).unwrap(); - fs::create_dir(root.join("lib")).unwrap(); - fs::create_dir(root.join("templates")).unwrap(); - let mut file = - fs::File::create(root.join("packages/android/org.fdroid.fdroid.lua")).unwrap(); - writeln!(file, "return {{ id = 'android/org.fdroid.fdroid' }}").unwrap(); - - let layout = RepositoryLayout::load(root).unwrap(); - assert_eq!(layout.metadata.id.as_str(), "official"); - assert_eq!(layout.metadata.priority, RepositoryPriority::DEFAULT); - assert_eq!(layout.packages.len(), 1); - assert_eq!( - layout.packages[0].id.to_string(), - "android/org.fdroid.fdroid" - ); - } - - #[test] - fn package_cache_key_changes_when_package_file_content_changes() { - let temp = tempfile::tempdir().unwrap(); - let root = temp.path(); - fs::write( - root.join("repo.toml"), - r#"id = "official" -name = "UpgradeAll Official" -priority = 0 -api_version = "getter.repo.v1" -"#, - ) - .unwrap(); - fs::create_dir(root.join("packages")).unwrap(); - fs::create_dir(root.join("packages/android")).unwrap(); - fs::create_dir(root.join("lib")).unwrap(); - fs::create_dir(root.join("templates")).unwrap(); - let package_path = root.join("packages/android/org.fdroid.fdroid.lua"); - fs::write( - &package_path, - r#"return { id = "android/org.fdroid.fdroid", name = "F-Droid" }"#, - ) - .unwrap(); - let layout = RepositoryLayout::load(root).unwrap(); - let first = package_cache_key(&layout, &layout.packages[0]).unwrap(); - - fs::write( - &package_path, - r#"return { id = "android/org.fdroid.fdroid", name = "F-Droid Nightly" }"#, - ) - .unwrap(); - let layout = RepositoryLayout::load(root).unwrap(); - let second = package_cache_key(&layout, &layout.packages[0]).unwrap(); - - assert_eq!(first.repository_id.as_str(), "official"); - assert_eq!(first.package_id.to_string(), "android/org.fdroid.fdroid"); - assert_eq!(first.api_version, REPO_API_VERSION_V1); - assert_ne!(first.package_file_hash, second.package_file_hash); - } - #[test] fn package_directory_cache_key_changes_when_local_files_change() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-operations/src/autogen.rs b/crates/getter-operations/src/autogen.rs index 0b597f4..770d3bf 100644 --- a/crates/getter-operations/src/autogen.rs +++ b/crates/getter-operations/src/autogen.rs @@ -13,7 +13,7 @@ use getter_core::autogen::{ DEFAULT_AUTOGEN_REPOSITORY_ID, DEFAULT_AUTOGEN_REPOSITORY_NAME, INSTALLED_AUTOGEN_GENERATOR, }; use getter_core::repository::{ - generated_repository_target, GeneratedRepositoryTarget, GetterDataDirLayout, RepositoryLayout, + generated_repository_target, GeneratedRepositoryTarget, GetterDataDirLayout, RepositoryLoadError, RepositoryMetadata, RepositoryPackageDirectoryLayout, RepositoryRootConfig, REPO_API_VERSION_V1, }; @@ -358,23 +358,13 @@ pub(crate) fn higher_priority_package_coverage( } fn load_repository_package_ids(path: &Path) -> AutogenOperationResult> { - if path.join("repo.toml").is_file() { - let layout = RepositoryLayout::load(path) - .map_err(|source| AutogenOperationError::Repository(source.to_string()))?; - Ok(layout - .packages - .into_iter() - .map(|package| package.id) - .collect()) - } else { - let layout = RepositoryPackageDirectoryLayout::load(path) - .map_err(|source| AutogenOperationError::Repository(source.to_string()))?; - Ok(layout - .packages - .into_iter() - .map(|package| package.id) - .collect()) - } + let layout = RepositoryPackageDirectoryLayout::load(path) + .map_err(|source| AutogenOperationError::Repository(source.to_string()))?; + Ok(layout + .packages + .into_iter() + .map(|package| package.id) + .collect()) } fn autogen_candidate_json(candidate: &AutogenCandidate) -> AutogenOperationResult { diff --git a/crates/getter-operations/src/read_model.rs b/crates/getter-operations/src/read_model.rs index 79f5cad..2c75b90 100644 --- a/crates/getter-operations/src/read_model.rs +++ b/crates/getter-operations/src/read_model.rs @@ -6,11 +6,9 @@ //! to parse and render. #[cfg(feature = "lua")] -use getter_core::lua::{evaluate_package_directory_script, evaluate_package_file}; +use getter_core::lua::evaluate_package_directory_script; #[cfg(feature = "lua")] -use getter_core::repository::{ - RepositoryLayout, RepositoryLoadError, RepositoryPackageDirectoryLayout, -}; +use getter_core::repository::{RepositoryLoadError, RepositoryPackageDirectoryLayout}; #[cfg(feature = "lua")] use getter_core::{PackageId, RepositoryId}; use getter_storage::{MainDb, StorageError, StoredRepository, StoredTrackedPackage}; @@ -171,16 +169,6 @@ fn evaluate_package_in_repository( package_id: &PackageId, ) -> Result, ReadModelOperationError> { let path = repo_path(repo)?; - if path.join("repo.toml").is_file() { - let layout = RepositoryLayout::load(&path)?; - let Some(package_file) = layout.package_file(package_id) else { - return Ok(None); - }; - return evaluate_package_file(&layout, &package_file.path) - .map(Some) - .map_err(|source| ReadModelOperationError::PackageEval(source.to_string())); - } - let layout = RepositoryPackageDirectoryLayout::load(&path)?; let Some(package) = layout.package(package_id) else { return Ok(None); @@ -291,36 +279,6 @@ mod tests { ); } - #[cfg(feature = "lua")] - #[test] - fn package_eval_reads_registered_lua_repository() { - let temp = tempdir().unwrap(); - let repo_path = temp.path().join("repo"); - write_legacy_lua_repo(&repo_path); - - let db = MainDb::open(temp.path().join(MAIN_DB_FILE)).unwrap(); - db.upsert_repository( - &RepositoryMetadata { - id: "official".parse().unwrap(), - name: "Official".to_owned(), - priority: RepositoryPriority::new(10), - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(&repo_path), - None, - ) - .unwrap(); - - let result = package_eval_json( - temp.path(), - r#"{"package_id":"android/org.fdroid.fdroid","repository_id":"official"}"#, - ) - .unwrap(); - assert_eq!(result["package"]["id"], "android/org.fdroid.fdroid"); - assert_eq!(result["package"]["name"], "F-Droid"); - assert_eq!(result["package"]["permissions"]["free_network"], true); - } - #[cfg(feature = "lua")] #[test] fn package_eval_reads_registered_package_directory_repository() { @@ -356,35 +314,6 @@ mod tests { ); } - #[cfg(feature = "lua")] - fn write_legacy_lua_repo(repo_path: &Path) { - fs::create_dir_all(repo_path.join("packages/android")).unwrap(); - fs::create_dir_all(repo_path.join("lib")).unwrap(); - fs::create_dir_all(repo_path.join("templates")).unwrap(); - fs::write( - repo_path.join("repo.toml"), - r#"id = "official" -name = "Official" -priority = 10 -api_version = "getter.repo.v1" -"#, - ) - .unwrap(); - fs::write( - repo_path.join("packages/android/org.fdroid.fdroid.lua"), - r#"return package_def { - id = "android/org.fdroid.fdroid", - name = "F-Droid", - installed = { - { kind = "android_package", package_name = "org.fdroid.fdroid" }, - }, - permissions = { free_network = true }, -} -"#, - ) - .unwrap(); - } - #[cfg(feature = "lua")] fn write_package_directory_repo(repo_path: &Path) { let package_dir = repo_path.join("android/app/com.example.autogen"); diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 3721eed..5a3b94e 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -8,10 +8,9 @@ #[cfg(feature = "lua")] use getter_core::{ - lua::{evaluate_package_directory_script, evaluate_package_file}, + lua::evaluate_package_directory_script, repository::{ - package_cache_key, package_directory_cache_key, RepositoryLayout, RepositoryLoadError, - RepositoryPackageDirectoryLayout, + package_directory_cache_key, RepositoryLoadError, RepositoryPackageDirectoryLayout, }, }; use getter_core::{ @@ -386,21 +385,6 @@ fn evaluate_registered_package( continue; }; let root = PathBuf::from(root); - if root.join("repo.toml").is_file() { - let layout = RepositoryLayout::load(&root)?; - let Some(package_file) = layout.package_file(&request.package_id) else { - continue; - }; - let package = evaluate_package_file(&layout, &package_file.path) - .map_err(|source| RuntimeOperationError::PackageEval(source.to_string()))?; - let cache_key = package_cache_key(&layout, package_file)?; - let dependency_digest = format!( - "repo:{}:package:{}:hash:{}", - cache_key.repository_id, cache_key.package_id, cache_key.package_file_hash - ); - return Ok((package, dependency_digest)); - } - let layout = RepositoryPackageDirectoryLayout::load(&root)?; let Some(package_directory) = layout.package(&request.package_id) else { continue; @@ -465,46 +449,6 @@ mod tests { #[cfg(feature = "lua")] use std::fs; - #[cfg(feature = "lua")] - #[test] - fn registered_package_update_check_issues_action_from_lua_static_updates() { - let temp = tempfile::tempdir().unwrap(); - let repo_root = temp.path().join("repo"); - write_static_update_repo(&repo_root); - let db = MainDb::open_in_memory().unwrap(); - db.upsert_repository( - &RepositoryMetadata { - id: "official".parse().unwrap(), - name: "Official".to_owned(), - priority: RepositoryPriority::new(0), - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(&repo_root), - None, - ) - .unwrap(); - let mut runtime = GetterRuntime::new(); - - let issued = issue_action_from_registered_package_json( - &mut runtime, - &db, - &json!({ - "package_id": "android/org.fdroid.fdroid", - "installed_version": "1.0.0" - }) - .to_string(), - ) - .unwrap(); - - assert_eq!(issued["package"]["repository"], "official"); - assert_eq!(issued["update"]["status"], "update_available"); - let action_id = issued["action"]["action_id"].as_str().unwrap(); - let submitted = - submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) - .unwrap(); - assert_eq!(submitted["package_id"], "android/org.fdroid.fdroid"); - } - #[cfg(feature = "lua")] #[test] fn registered_package_update_check_issues_action_from_package_directory_static_updates() { @@ -551,13 +495,13 @@ mod tests { fn registered_package_update_check_without_update_does_not_issue_action() { let temp = tempfile::tempdir().unwrap(); let repo_root = temp.path().join("repo"); - write_static_update_repo(&repo_root); + write_package_directory_static_update_repo(&repo_root); let db = MainDb::open_in_memory().unwrap(); db.upsert_repository( &RepositoryMetadata { - id: "official".parse().unwrap(), - name: "Official".to_owned(), - priority: RepositoryPriority::new(0), + id: "autogen".parse().unwrap(), + name: "Autogen".to_owned(), + priority: RepositoryPriority::new(-1), api_version: REPO_API_VERSION_V1.to_owned(), }, Some(&repo_root), @@ -570,7 +514,7 @@ mod tests { &mut runtime, &db, &json!({ - "package_id": "android/org.fdroid.fdroid", + "package_id": "android/app/com.example.autogen", "installed_version": "1.2.0" }) .to_string(), @@ -706,44 +650,6 @@ mod tests { runtime.submit_action(&action.action_id).unwrap().task_id } - #[cfg(feature = "lua")] - fn write_static_update_repo(root: &std::path::Path) { - fs::create_dir_all(root.join("packages/android")).unwrap(); - fs::create_dir(root.join("lib")).unwrap(); - fs::create_dir(root.join("templates")).unwrap(); - fs::write( - root.join("repo.toml"), - r#"id = "official" -name = "Official" -priority = 0 -api_version = "getter.repo.v1" -"#, - ) - .unwrap(); - fs::write( - root.join("packages/android/org.fdroid.fdroid.lua"), - r#" -return package_def { - id = "android/org.fdroid.fdroid", - name = "F-Droid", - updates = { - { - version = "1.2.0", - artifacts = { - { - name = "app.apk", - url = "https://example.invalid/app.apk", - file_name = "app.apk", - }, - }, - }, - }, -} -"#, - ) - .unwrap(); - } - #[cfg(feature = "lua")] fn write_package_directory_static_update_repo(root: &std::path::Path) { let package_dir = root.join("android/app/com.example.autogen"); From a67e8227ffefae4b6665ec1a4a2e29e4839fe3e1 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 16:46:46 +0800 Subject: [PATCH 42/71] feat(providers): add GitHub latest commit fixtures --- crates/getter-cli/src/lib.rs | 226 ++++++++- crates/getter-cli/tests/bdd_cli.rs | 74 +++ .../cli/provider_github_latest_commit.feature | 10 + .../src/github_latest_commit.rs | 477 ++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + crates/getter-providers/src/lib.rs | 164 +++++- 6 files changed, 950 insertions(+), 2 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/provider_github_latest_commit.feature create mode 100644 crates/getter-operations/src/github_latest_commit.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 7c7efbe..0d32f2f 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -23,6 +23,7 @@ use getter_downloader::{ }; use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; use getter_operations::fdroid_autogen; +use getter_operations::github_latest_commit::{self, GithubLatestCommitOperationError}; use getter_operations::github_releases::{self, GithubReleaseOperationError}; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; use getter_operations::runtime as runtime_operations; @@ -146,6 +147,13 @@ pub enum CliCommand { include_prereleases: bool, refresh: bool, }, + ProviderGithubLatestCommit { + owner: String, + repo: String, + reference: Option, + commit: PathBuf, + refresh: bool, + }, LegacyImportRoomBundle { bundle: PathBuf, }, @@ -329,6 +337,16 @@ impl From for CliError { } } +impl From for CliError { + fn from(value: GithubLatestCommitOperationError) -> Self { + match value { + GithubLatestCommitOperationError::Storage(source) => Self::Storage(source.to_string()), + GithubLatestCommitOperationError::InvalidRequest(detail) => Self::Usage(detail), + other => Self::Provider(other.to_string()), + } + } +} + impl From for CliError { fn from(value: GithubReleaseOperationError) -> Self { match value { @@ -599,6 +617,18 @@ where refresh: args.refresh, } } + [domain, provider, action, rest @ ..] + if domain == "provider" && provider == "github" && action == "latest-commit" => + { + let args = parse_provider_github_latest_commit_args(rest)?; + CliCommand::ProviderGithubLatestCommit { + owner: args.owner, + repo: args.repo, + reference: args.reference, + commit: args.commit, + refresh: args.refresh, + } + } [domain, subject, action, flag, preview, rest @ ..] if domain == "autogen" && subject == "cleanup" @@ -863,6 +893,25 @@ fn execute(invocation: CliInvocation) -> Result { }); github_releases::github_releases_json(&db, &request.to_string()).map_err(CliError::from) } + CliCommand::ProviderGithubLatestCommit { + owner, + repo, + reference, + commit, + refresh, + } => { + let db = open_cache_db(&invocation.data_dir)?; + let commit_json = read_github_commit_fixture(&commit)?; + let request = json!({ + "owner": owner, + "repo": repo, + "ref": reference, + "mode": if refresh { "force_refresh" } else { "use_cached" }, + "commit_json": commit_json, + }); + github_latest_commit::github_latest_commit_json(&db, &request.to_string()) + .map_err(CliError::from) + } CliCommand::LegacyImportRoomBundle { bundle } => { let db = open_main_db(&invocation.data_dir)?; if db.migration_record_exists(LEGACY_ROOM_MIGRATION_ID)? { @@ -1099,6 +1148,15 @@ struct ProviderGithubReleasesArgs { refresh: bool, } +#[derive(Debug, Default, PartialEq, Eq)] +struct ProviderGithubLatestCommitArgs { + owner: String, + repo: String, + reference: Option, + commit: PathBuf, + refresh: bool, +} + fn parse_provider_github_releases_args( args: &[String], ) -> Result { @@ -1198,6 +1256,87 @@ fn parse_provider_github_releases_args( Ok(parsed) } +fn parse_provider_github_latest_commit_args( + args: &[String], +) -> Result { + let mut parsed = ProviderGithubLatestCommitArgs::default(); + let mut position = 0; + while position < args.len() { + match args[position].as_str() { + "--owner" => { + parsed.owner = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github latest-commit --owner requires an owner".to_owned(), + ) + })? + .clone(); + position += 2; + } + "--repo" => { + parsed.repo = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github latest-commit --repo requires a repo".to_owned(), + ) + })? + .clone(); + position += 2; + } + "--commit" => { + let path = args.get(position + 1).ok_or_else(|| { + CliError::Usage( + "provider github latest-commit --commit requires a fixture path".to_owned(), + ) + })?; + parsed.commit = PathBuf::from(path); + position += 2; + } + "--ref" => { + parsed.reference = Some( + args.get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "provider github latest-commit --ref requires a ref".to_owned(), + ) + })? + .clone(), + ); + position += 2; + } + "--refresh" => { + parsed.refresh = true; + position += 1; + } + other => { + return Err(CliError::Usage(format!( + "unsupported provider github latest-commit argument '{other}'" + ))) + } + } + } + + if parsed.owner.trim().is_empty() { + return Err(CliError::Usage( + "provider github latest-commit requires --owner ".to_owned(), + )); + } + if parsed.repo.trim().is_empty() { + return Err(CliError::Usage( + "provider github latest-commit requires --repo ".to_owned(), + )); + } + if parsed.commit.as_os_str().is_empty() { + return Err(CliError::Usage( + "provider github latest-commit requires --commit ".to_owned(), + )); + } + + Ok(parsed) +} + fn parse_autogen_acceptance(args: &[String]) -> Result { match args { [flag] if flag == "--accept-all" => Ok(AutogenAcceptance::AcceptAll), @@ -1340,6 +1479,12 @@ fn read_github_releases_fixture(path: &Path) -> Result { }) } +fn read_github_commit_fixture(path: &Path) -> Result { + fs::read_to_string(path).map_err(|source| { + CliError::Provider(format!("failed to read GitHub commit fixture: {source}")) + }) +} + fn read_installed_inventory(path: &Path) -> Result { let bytes = fs::read(path) .map_err(|source| CliError::Autogen(format!("failed to read inventory: {source}")))?; @@ -1869,7 +2014,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo --releases [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo --releases [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -1939,6 +2084,7 @@ impl CliCommand { Self::AutogenFdroidPreview { .. } => "autogen fdroid preview", Self::AutogenFdroidApply { .. } => "autogen fdroid apply", Self::ProviderGithubReleases { .. } => "provider github releases", + Self::ProviderGithubLatestCommit { .. } => "provider github latest-commit", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", Self::LegacyImportRoomDb { .. } => "legacy import-room-db", Self::LegacyReportList => "legacy report-list", @@ -1996,6 +2142,84 @@ mod tests { ); } + #[test] + fn parses_provider_github_latest_commit_command() { + let parsed = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "provider", + "github", + "latest-commit", + "--owner", + "DUpdateSystem", + "--repo", + "UpgradeAll", + "--commit", + "/tmp/commit.json", + "--ref", + "main", + "--refresh", + ]) + .unwrap(); + + assert_eq!( + parsed.command, + CliCommand::ProviderGithubLatestCommit { + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + reference: Some("main".to_owned()), + commit: PathBuf::from("/tmp/commit.json"), + refresh: true, + } + ); + } + + #[test] + fn provider_github_latest_commit_missing_cli_flags_is_usage_error() { + let output = run([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "provider", + "github", + "latest-commit", + "--owner", + "DUpdateSystem", + ]); + + assert_eq!(output.exit_code, ExitCode::Usage); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["error"]["code"], "cli.usage"); + } + + #[test] + fn provider_github_latest_commit_malformed_fixture_is_provider_error() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path().join("getter-data"); + let commit = temp.path().join("commit.json"); + fs::write(&commit, "not json").unwrap(); + + let output = run([ + "getter".to_owned(), + "--data-dir".to_owned(), + data_dir.to_string_lossy().to_string(), + "provider".to_owned(), + "github".to_owned(), + "latest-commit".to_owned(), + "--owner".to_owned(), + "DUpdateSystem".to_owned(), + "--repo".to_owned(), + "UpgradeAll".to_owned(), + "--commit".to_owned(), + commit.to_string_lossy().to_string(), + ]); + + assert_eq!(output.exit_code, ExitCode::Provider); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["error"]["code"], "provider.error"); + } + #[test] fn old_task_namespace_is_not_public_cli_surface() { let output = run(["getter", "--data-dir", "/tmp/ua-getter", "task", "list"]); diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index f5f0887..b0a05e2 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -17,6 +17,7 @@ struct CliWorld { update_fixture: Option, fdroid_index: Option, github_releases: Option, + github_commit: Option, task_request: Option, runtime_script: Option, remembered_task_id: Option, @@ -240,6 +241,31 @@ fn fixture_github_releases_response(world: &mut CliWorld, project: String) { world.github_releases = Some(releases); } +#[given(expr = "a fixture GitHub commit response for {string}")] +fn fixture_github_commit_response(world: &mut CliWorld, project: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let commit = temp.path().join("github-commit.json"); + fs::write( + &commit, + serde_json::to_vec_pretty(&serde_json::json!({ + "sha": "0123456789abcdef0123456789abcdef01234567", + "html_url": "https://github.com/DUpdateSystem/UpgradeAll/commit/0123456789abcdef0123456789abcdef01234567", + "commit": { + "message": format!("{project} live commit"), + "author": { + "date": "2026-06-02T03:04:05Z" + }, + "committer": { + "date": "2026-06-02T04:05:06Z" + } + } + })) + .expect("GitHub commit serializes"), + ) + .expect("write GitHub commit fixture"); + world.github_commit = Some(commit); +} + #[given("an empty installed inventory")] fn empty_installed_inventory(world: &mut CliWorld) { let temp = world.temp.as_ref().expect("tempdir exists"); @@ -985,6 +1011,30 @@ fn run_getter_provider_github_releases(world: &mut CliWorld, owner: String, repo world.json = None; } +#[when(expr = "I run getter provider github latest-commit for owner {string} repo {string}")] +fn run_getter_provider_github_latest_commit(world: &mut CliWorld, owner: String, repo: String) { + let commit = world + .github_commit + .as_ref() + .expect("GitHub commit fixture exists"); + let output = run_getter( + world, + [ + "provider".to_owned(), + "github".to_owned(), + "latest-commit".to_owned(), + "--owner".to_owned(), + owner, + "--repo".to_owned(), + repo, + "--commit".to_owned(), + commit.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen fdroid apply for that preview with accept-all")] fn run_getter_autogen_fdroid_apply_accept_all(world: &mut CliWorld) { let preview = world @@ -1605,6 +1655,30 @@ fn github_release_provider_returns_candidate( assert!(json["data"]["diagnostics"].as_array().unwrap().is_empty()); } +#[then(expr = "the GitHub latest-commit provider returns live revision {string}")] +fn github_latest_commit_provider_returns_live_revision(world: &mut CliWorld, revision: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "provider github latest-commit"); + assert_eq!(json["data"]["operation"], "github.latest_commit"); + assert_eq!(json["data"]["provider"], "github"); + assert_eq!(json["data"]["source"], "refreshed"); + assert_eq!(json["data"]["live"], true); + assert_eq!(json["data"]["version"], revision); + assert_eq!(json["data"]["revision"], revision); + assert_eq!(json["data"]["latest_commit"]["revision"], revision); + assert_eq!( + json["data"]["latest_commit"]["published_at"], + "2026-06-02T03:04:05Z" + ); + assert!(json["data"].get("candidates").is_none()); + assert!(json["data"].get("selected_update").is_none()); + assert!(json["data"].get("artifacts").is_none()); + assert!(json["data"].get("actions").is_none()); + assert!(json["data"].get("action_id").is_none()); + assert!(json["data"]["diagnostics"].as_array().unwrap().is_empty()); +} + #[then(expr = "the autogen repository contains generated F-Droid package {string}")] fn autogen_repository_contains_generated_fdroid_package(world: &mut CliWorld, package_id: String) { autogen_repository_contains_generated_package(world, package_id.clone()); diff --git a/crates/getter-cli/tests/features/cli/provider_github_latest_commit.feature b/crates/getter-cli/tests/features/cli/provider_github_latest_commit.feature new file mode 100644 index 0000000..4cdfbbd --- /dev/null +++ b/crates/getter-cli/tests/features/cli/provider_github_latest_commit.feature @@ -0,0 +1,10 @@ +@getter-cli @provider @github +Feature: GitHub latest commit provider fixtures + Scenario: User queries fixture-backed GitHub latest commit as a live revision + Given an initialized getter data directory + And a fixture GitHub commit response for "DUpdateSystem/UpgradeAll" + When I run getter provider github latest-commit for owner "DUpdateSystem" repo "UpgradeAll" + Then the command succeeds + And the output is valid JSON + And the GitHub latest-commit provider returns live revision "0123456789abcdef0123456789abcdef01234567" + And the autogen repository has not been written diff --git a/crates/getter-operations/src/github_latest_commit.rs b/crates/getter-operations/src/github_latest_commit.rs new file mode 100644 index 0000000..15e8b7e --- /dev/null +++ b/crates/getter-operations/src/github_latest_commit.rs @@ -0,0 +1,477 @@ +//! Fixture-backed GitHub latest-commit cache/query operations. +//! +//! This ADR-0012 provider slice models latest-commit checks as live/floating +//! provider facts. It parses controlled GitHub commit fixtures through the +//! shared provider cache without producing ordinary release candidates, runtime +//! actions, downloads, installer handoffs, or Flutter/Kotlin provider parsing. + +use crate::provider_cache::{ + read_or_refresh_provider_response, ProviderCacheDiagnostic, ProviderCacheMode, + ProviderCacheOperationError, ProviderCacheRequest, ProviderCacheSource, +}; +use getter_providers::{ + github_latest_commit_live_revision, parse_github_commit_json, GithubCommit, GithubLiveRevision, + GithubProviderError, +}; +use getter_storage::{CacheDb, StorageError}; +use serde::Deserialize; +use serde_json::{json, Value}; +use sha2::{Digest, Sha512}; + +pub const GITHUB_PROVIDER_ID: &str = "github"; +pub const GITHUB_LATEST_COMMIT_CACHE_VERSION: &str = "github-latest-commit-v1"; +pub const DEFAULT_GITHUB_API_BASE_URL: &str = "https://api.github.com"; +pub const DEFAULT_GITHUB_REF: &str = "HEAD"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GithubLatestCommitConfig { + pub api_base_url: String, + pub owner: String, + pub repo: String, + pub reference: String, +} + +impl GithubLatestCommitConfig { + pub fn cache_key(&self) -> String { + format!( + "{GITHUB_PROVIDER_ID}:{GITHUB_LATEST_COMMIT_CACHE_VERSION}:latest_commit:{}:{}/{}/{}", + digest_hex(&self.api_base_url), + self.owner, + self.repo, + self.reference, + ) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GithubLatestCommitResult { + pub config: GithubLatestCommitConfig, + pub cache_key: String, + pub commit: GithubCommit, + pub live_revision: GithubLiveRevision, + pub source: ProviderCacheSource, + pub diagnostics: Vec, +} + +#[derive(Debug, thiserror::Error)] +pub enum GithubLatestCommitOperationError { + #[error("provider cache operation failed: {0}")] + Cache(#[from] ProviderCacheOperationError), + #[error("storage error: {0}")] + Storage(#[from] StorageError), + #[error("GitHub provider parse failed: {0}")] + Provider(#[from] GithubProviderError), + #[error("GitHub provider serialization failed: {0}")] + Serialization(#[from] serde_json::Error), + #[error("invalid GitHub latest-commit request: {0}")] + InvalidRequest(String), +} + +pub fn read_or_refresh_github_latest_commit( + db: &CacheDb, + config: GithubLatestCommitConfig, + mode: ProviderCacheMode, + refresh_json: F, +) -> Result +where + F: FnOnce() -> Result, +{ + let cache_key = config.cache_key(); + let cache_result = read_or_refresh_provider_response( + db, + ProviderCacheRequest { + cache_key: &cache_key, + provider: GITHUB_PROVIDER_ID, + mode, + }, + || { + let json = refresh_json()?; + let commit = parse_github_commit_json(&json).map_err(|source| source.to_string())?; + github_latest_commit_live_revision(&commit).map_err(|source| source.to_string())?; + serde_json::to_value(commit).map_err(|source| source.to_string()) + }, + )?; + let commit = serde_json::from_value(cache_result.response.response_json)?; + let live_revision = github_latest_commit_live_revision(&commit)?; + + Ok(GithubLatestCommitResult { + config, + cache_key, + commit, + live_revision, + source: cache_result.source, + diagnostics: cache_result.diagnostics, + }) +} + +pub fn github_latest_commit_json( + db: &CacheDb, + request_json: &str, +) -> Result { + let request: GithubLatestCommitJsonRequest = serde_json::from_str(request_json)?; + let owner = required_non_empty(request.owner, "owner")?; + let repo = required_non_empty(request.repo, "repo")?; + let reference = request + .reference + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_REF.to_owned()); + let config = GithubLatestCommitConfig { + api_base_url: request + .api_base_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()), + owner, + repo, + reference, + }; + let mode = match request.mode.as_deref() { + Some("force_refresh") => ProviderCacheMode::ForceRefresh, + Some("use_cached") | None => ProviderCacheMode::UseCached, + Some(other) => { + return Err(GithubLatestCommitOperationError::InvalidRequest(format!( + "unknown mode '{other}'" + ))) + } + }; + let result = read_or_refresh_github_latest_commit(db, config, mode, || { + request.commit_json.ok_or_else(|| { + "fixture-backed GitHub latest-commit refresh requires commit_json".to_owned() + }) + })?; + + Ok(json!({ + "operation": "github.latest_commit", + "provider": GITHUB_PROVIDER_ID, + "api_base_url": result.config.api_base_url, + "owner": result.config.owner, + "repo": result.config.repo, + "ref": result.config.reference, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "live": true, + "version": result.live_revision.version, + "revision": result.live_revision.revision, + "latest_commit": result.live_revision, + "commit": result.commit, + "diagnostics": result.diagnostics.iter().map(diagnostic_json).collect::>(), + })) +} + +#[derive(Debug, Deserialize)] +struct GithubLatestCommitJsonRequest { + #[serde(default)] + api_base_url: Option, + #[serde(default)] + owner: Option, + #[serde(default)] + repo: Option, + #[serde(default, rename = "ref")] + reference: Option, + #[serde(default)] + mode: Option, + #[serde(default)] + commit_json: Option, +} + +fn required_non_empty( + value: Option, + field: &'static str, +) -> Result { + value + .filter(|value| !value.trim().is_empty()) + .ok_or_else(|| GithubLatestCommitOperationError::InvalidRequest(format!("missing {field}"))) +} + +fn provider_source_json(source: ProviderCacheSource) -> &'static str { + match source { + ProviderCacheSource::Cache => "cache", + ProviderCacheSource::Refreshed => "refreshed", + ProviderCacheSource::Stale => "stale", + } +} + +fn diagnostic_json(diagnostic: &ProviderCacheDiagnostic) -> Value { + json!({ + "code": diagnostic.code, + "message": diagnostic.message, + "cache_key": diagnostic.cache_key, + "provider": diagnostic.provider, + "stale_fetched_at_unix": diagnostic.stale_fetched_at_unix, + }) +} + +fn digest_hex(value: &str) -> String { + let mut hasher = Sha512::new(); + hasher.update(value.as_bytes()); + format!("{:x}", hasher.finalize()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::provider_cache::{CACHE_REFRESH_FAILED, USED_STALE_CACHE}; + use std::cell::Cell; + + const GITHUB_COMMIT_FIXTURE: &str = r#"{ + "sha": "0123456789abcdef0123456789abcdef01234567", + "html_url": "https://github.com/DUpdateSystem/UpgradeAll/commit/0123456789abcdef0123456789abcdef01234567", + "commit": { + "message": "Update app metadata", + "author": { + "date": "2026-06-02T03:04:05Z" + }, + "committer": { + "date": "2026-06-02T04:05:06Z" + } + } +}"#; + + const UPDATED_COMMIT_FIXTURE: &str = r#"{ + "sha": "fedcba9876543210fedcba9876543210fedcba98", + "html_url": "https://github.com/DUpdateSystem/UpgradeAll/commit/fedcba9876543210fedcba9876543210fedcba98", + "commit": { + "message": "Refresh generated packages", + "committer": { + "date": "2026-06-03T03:04:05Z" + } + } +}"#; + + const EMPTY_SHA_COMMIT_FIXTURE: &str = r#"{ + "sha": " ", + "commit": { + "message": "Broken fixture" + } +}"#; + + fn config() -> GithubLatestCommitConfig { + GithubLatestCommitConfig { + api_base_url: DEFAULT_GITHUB_API_BASE_URL.to_owned(), + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + reference: DEFAULT_GITHUB_REF.to_owned(), + } + } + + #[test] + fn cache_key_identifies_latest_commit_request_and_ref() { + let main = config(); + let mut branch = config(); + branch.reference = "main".to_owned(); + let releases = crate::github_releases::GithubReleaseConfig { + api_base_url: crate::github_releases::DEFAULT_GITHUB_API_BASE_URL.to_owned(), + owner: main.owner.clone(), + repo: main.repo.clone(), + }; + + assert!(main + .cache_key() + .contains(GITHUB_LATEST_COMMIT_CACHE_VERSION)); + assert!(main.cache_key().contains(":latest_commit:")); + assert_ne!(main.cache_key(), branch.cache_key()); + assert_ne!(main.cache_key(), releases.cache_key()); + } + + #[test] + fn cache_miss_parses_and_stores_fixture_commit_as_live_revision() { + let db = CacheDb::open_in_memory().unwrap(); + let config = config(); + let cache_key = config.cache_key(); + + let result = + read_or_refresh_github_latest_commit(&db, config, ProviderCacheMode::UseCached, || { + Ok(GITHUB_COMMIT_FIXTURE.to_owned()) + }) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert!(result.live_revision.live); + assert_eq!( + result.live_revision.revision, + "0123456789abcdef0123456789abcdef01234567" + ); + assert_eq!( + result.live_revision.published_at.as_deref(), + Some("2026-06-02T03:04:05Z") + ); + let cached = db.provider_response(&cache_key).unwrap().unwrap(); + assert_eq!(cached.provider, GITHUB_PROVIDER_ID); + assert_eq!( + cached.response_json["sha"], + "0123456789abcdef0123456789abcdef01234567" + ); + } + + #[test] + fn cache_hit_avoids_refreshing_fixture_commit() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_github_latest_commit(&db, config(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_COMMIT_FIXTURE.to_owned()) + }) + .unwrap(); + let refreshed = Cell::new(false); + + let result = read_or_refresh_github_latest_commit( + &db, + config(), + ProviderCacheMode::UseCached, + || { + refreshed.set(true); + Ok(UPDATED_COMMIT_FIXTURE.to_owned()) + }, + ) + .unwrap(); + + assert!(!refreshed.get()); + assert_eq!(result.source, ProviderCacheSource::Cache); + assert_eq!( + result.live_revision.revision, + "0123456789abcdef0123456789abcdef01234567" + ); + } + + #[test] + fn forced_refresh_replaces_cached_fixture_commit() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_github_latest_commit(&db, config(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_COMMIT_FIXTURE.to_owned()) + }) + .unwrap(); + + let result = read_or_refresh_github_latest_commit( + &db, + config(), + ProviderCacheMode::ForceRefresh, + || Ok(UPDATED_COMMIT_FIXTURE.to_owned()), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!( + result.live_revision.revision, + "fedcba9876543210fedcba9876543210fedcba98" + ); + } + + #[test] + fn forced_refresh_failure_returns_stale_commit_with_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_github_latest_commit(&db, config(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_COMMIT_FIXTURE.to_owned()) + }) + .unwrap(); + + let result = read_or_refresh_github_latest_commit( + &db, + config(), + ProviderCacheMode::ForceRefresh, + || Err("HTTP 503".to_owned()), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Stale); + assert_eq!( + result.live_revision.revision, + "0123456789abcdef0123456789abcdef01234567" + ); + let codes: Vec<_> = result + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect(); + assert_eq!(codes, vec![CACHE_REFRESH_FAILED, USED_STALE_CACHE]); + } + + #[test] + fn cache_miss_with_empty_sha_is_an_error_and_does_not_store_cache() { + let db = CacheDb::open_in_memory().unwrap(); + let config = config(); + let cache_key = config.cache_key(); + + let error = + read_or_refresh_github_latest_commit(&db, config, ProviderCacheMode::UseCached, || { + Ok(EMPTY_SHA_COMMIT_FIXTURE.to_owned()) + }) + .unwrap_err(); + + assert!(matches!( + error, + GithubLatestCommitOperationError::Cache(ProviderCacheOperationError::RefreshFailed(_)) + )); + assert!(db.provider_response(&cache_key).unwrap().is_none()); + } + + #[test] + fn forced_refresh_with_empty_sha_returns_stale_commit_with_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + read_or_refresh_github_latest_commit(&db, config(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_COMMIT_FIXTURE.to_owned()) + }) + .unwrap(); + + let result = read_or_refresh_github_latest_commit( + &db, + config(), + ProviderCacheMode::ForceRefresh, + || Ok(EMPTY_SHA_COMMIT_FIXTURE.to_owned()), + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Stale); + assert_eq!( + result.live_revision.revision, + "0123456789abcdef0123456789abcdef01234567" + ); + let codes: Vec<_> = result + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect(); + assert_eq!(codes, vec![CACHE_REFRESH_FAILED, USED_STALE_CACHE]); + } + + #[test] + fn json_operation_returns_live_revision_from_cached_commit() { + let db = CacheDb::open_in_memory().unwrap(); + let first = github_latest_commit_json( + &db, + &json!({ + "owner": "DUpdateSystem", + "repo": "UpgradeAll", + "commit_json": GITHUB_COMMIT_FIXTURE + }) + .to_string(), + ) + .unwrap(); + assert_eq!(first["source"], "refreshed"); + + let second = github_latest_commit_json( + &db, + &json!({ + "owner": "DUpdateSystem", + "repo": "UpgradeAll" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(second["operation"], "github.latest_commit"); + assert_eq!(second["ref"], DEFAULT_GITHUB_REF); + assert_eq!(second["source"], "cache"); + assert_eq!(second["live"], true); + assert!(second.get("candidates").is_none()); + assert!(second.get("selected_update").is_none()); + assert!(second.get("artifacts").is_none()); + assert!(second.get("actions").is_none()); + assert!(second.get("action_id").is_none()); + assert_eq!( + second["revision"], + "0123456789abcdef0123456789abcdef01234567" + ); + assert_eq!( + second["latest_commit"]["published_at"], + "2026-06-02T03:04:05Z" + ); + assert!(second["latest_commit"]["live"].as_bool().unwrap()); + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 46511ea..30097ff 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -8,6 +8,7 @@ pub mod autogen; pub mod fdroid_autogen; pub mod fdroid_catalog; +pub mod github_latest_commit; pub mod github_releases; pub mod legacy_room; pub mod provider_cache; diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index 6a9ebfb..a71cd49 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -262,6 +262,42 @@ pub struct GithubReleaseAsset { pub digest: Option, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GithubCommit { + pub sha: String, + #[serde(default)] + pub html_url: Option, + #[serde(default)] + pub commit: GithubCommitDetails, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct GithubCommitDetails { + #[serde(default)] + pub message: Option, + #[serde(default)] + pub author: Option, + #[serde(default)] + pub committer: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GithubCommitActor { + #[serde(default)] + pub date: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct GithubLiveRevision { + pub version: String, + pub revision: String, + pub live: bool, + pub source: String, + pub published_at: Option, + pub html_url: Option, + pub message: Option, +} + #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct GithubReleaseCandidateOptions { pub include_prereleases: bool, @@ -276,8 +312,13 @@ pub struct GithubAssetFilter { #[derive(Debug, thiserror::Error)] pub enum GithubProviderError { - #[error("failed to parse GitHub releases JSON: {0}")] + #[error("failed to parse GitHub provider JSON: {0}")] Json(#[from] serde_json::Error), + #[error("invalid GitHub commit field {field}: {message}")] + InvalidCommit { + field: &'static str, + message: String, + }, #[error("invalid GitHub asset {filter_kind} regex '{pattern}': {source}")] AssetFilterRegex { filter_kind: &'static str, @@ -291,6 +332,43 @@ pub fn parse_github_releases_json(json: &str) -> Result, Gith Ok(serde_json::from_str(json)?) } +pub fn parse_github_commit_json(json: &str) -> Result { + Ok(serde_json::from_str(json)?) +} + +pub fn github_latest_commit_live_revision( + commit: &GithubCommit, +) -> Result { + let revision = commit.sha.trim(); + if revision.is_empty() { + return Err(GithubProviderError::InvalidCommit { + field: "sha", + message: "commit sha must not be empty".to_owned(), + }); + } + + Ok(GithubLiveRevision { + version: revision.to_owned(), + revision: revision.to_owned(), + live: true, + source: "github".to_owned(), + published_at: commit + .commit + .author + .as_ref() + .and_then(|author| author.date.clone()) + .or_else(|| { + commit + .commit + .committer + .as_ref() + .and_then(|committer| committer.date.clone()) + }), + html_url: commit.html_url.clone(), + message: commit.commit.message.clone(), + }) +} + pub fn github_release_update_candidates( releases: &[GithubRelease], options: &GithubReleaseCandidateOptions, @@ -580,6 +658,76 @@ mod tests { assert_eq!(candidates[0].artifacts[0].sha256, None); } + #[test] + fn parses_github_commit_json_into_live_revision() { + let commit = parse_github_commit_json(GITHUB_COMMIT_FIXTURE).unwrap(); + + assert_eq!(commit.sha, "0123456789abcdef0123456789abcdef01234567"); + let revision = github_latest_commit_live_revision(&commit).unwrap(); + + assert!(revision.live); + assert_eq!(revision.source, "github"); + assert_eq!(revision.version, "0123456789abcdef0123456789abcdef01234567"); + assert_eq!( + revision.revision, + "0123456789abcdef0123456789abcdef01234567" + ); + assert_eq!( + revision.published_at.as_deref(), + Some("2026-06-02T03:04:05Z") + ); + assert_eq!(revision.message.as_deref(), Some("Update app metadata")); + assert_eq!( + revision.html_url.as_deref(), + Some("https://github.com/DUpdateSystem/UpgradeAll/commit/0123456789abcdef0123456789abcdef01234567") + ); + } + + #[test] + fn falls_back_to_committer_date_for_live_revision() { + let commit = parse_github_commit_json( + r#"{ + "sha": "fedcba9876543210fedcba9876543210fedcba98", + "commit": { "committer": { "date": "2026-06-03T03:04:05Z" } } +}"#, + ) + .unwrap(); + + let revision = github_latest_commit_live_revision(&commit).unwrap(); + + assert_eq!( + revision.published_at.as_deref(), + Some("2026-06-03T03:04:05Z") + ); + } + + #[test] + fn omits_live_revision_date_when_commit_dates_are_missing() { + let commit = parse_github_commit_json( + r#"{ + "sha": "fedcba9876543210fedcba9876543210fedcba98", + "commit": { "message": "No dates" } +}"#, + ) + .unwrap(); + + let revision = github_latest_commit_live_revision(&commit).unwrap(); + + assert_eq!(revision.published_at, None); + } + + #[test] + fn rejects_empty_github_commit_sha() { + let commit = parse_github_commit_json(r#"{ "sha": "", "commit": {} }"#).unwrap(); + + let error = github_latest_commit_live_revision(&commit).unwrap_err(); + + assert!(matches!( + error, + GithubProviderError::InvalidCommit { field: "sha", .. } + )); + } + const GITHUB_RELEASES_FIXTURE: &str = r#"[ { "tag_name": "v1.2.0", @@ -651,4 +799,18 @@ mod tests { ] } ]"#; + + const GITHUB_COMMIT_FIXTURE: &str = r#"{ + "sha": "0123456789abcdef0123456789abcdef01234567", + "html_url": "https://github.com/DUpdateSystem/UpgradeAll/commit/0123456789abcdef0123456789abcdef01234567", + "commit": { + "message": "Update app metadata", + "author": { + "date": "2026-06-02T03:04:05Z" + }, + "committer": { + "date": "2026-06-02T04:05:06Z" + } + } +}"#; } From 496bc08b44b20fd02af00490194ec526647e7c7d Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 17:59:57 +0800 Subject: [PATCH 43/71] test(lua): cover F-Droid luaclass shape --- crates/getter-core/src/lua.rs | 80 +++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 2507fd7..186aa65 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -1008,6 +1008,86 @@ return android.package_version { assert_eq!(package.installed.len(), 1); } + #[test] + fn package_directory_can_use_repository_local_fdroid_luaclass_shape() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/f-droid/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::create_dir_all(temp.path().join("luaclass")).unwrap(); + fs::write( + temp.path().join("luaclass/fdroid_android.lua"), + r#" +local fdroid = {} + +function fdroid.package(spec) + if spec.package_name ~= "org.fdroid.fdroid" then + error("F-Droid package_name fixture mismatch") + end + return package_version { + updates = { + { + version = "1.20.0", + version_code = 1020000, + source = "fdroid", + artifacts = { + { + name = "org.fdroid.fdroid_1020000.apk", + url = "https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk", + file_name = "org.fdroid.fdroid_1020000.apk", + }, + }, + }, + }, + } +end + +return fdroid +"#, + ) + .unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { + package_name = "org.fdroid.fdroid", +} +"#, + ) + .unwrap(); + + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + + assert_eq!( + package.id.to_string(), + "android/f-droid/app/org.fdroid.fdroid" + ); + assert_eq!(package.repository.as_str(), "official"); + assert_eq!(package.name, "android/f-droid/app/org.fdroid.fdroid"); + assert_eq!( + package.installed, + vec![InstalledTarget::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned() + }] + ); + assert_eq!(package.updates.len(), 1); + assert_eq!(package.updates[0].version, "1.20.0"); + assert_eq!(package.updates[0].version_code, Some(1020000)); + assert_eq!(package.updates[0].source.as_deref(), Some("fdroid")); + assert_eq!( + package.updates[0].artifacts[0].file_name.as_deref(), + Some("org.fdroid.fdroid_1020000.apk") + ); + } + #[test] fn package_directory_can_read_package_local_files() { let temp = tempfile::tempdir().unwrap(); From d8cd8c97501c6ff20f49a7b0819eaeb372fde559 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 18:09:39 +0800 Subject: [PATCH 44/71] test(lua): cover GitHub luaclass shape --- crates/getter-core/src/lua.rs | 99 +++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 186aa65..e45801c 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -1088,6 +1088,105 @@ return fdroid.package { ); } + #[test] + fn package_directory_can_use_repository_local_github_android_luaclass_shape() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::create_dir_all(temp.path().join("luaclass")).unwrap(); + fs::write( + temp.path().join("luaclass/github_android_apk.lua"), + r#" +local github_android = {} + +function github_android.package(spec) + if spec.owner ~= "f-droid" or spec.repo ~= "fdroidclient" then + error("GitHub project fixture mismatch") + end + local asset_name = "F-Droid.apk" + if spec.asset.include and not string.match(asset_name, spec.asset.include) then + error("GitHub asset include fixture mismatch") + end + if spec.asset.exclude and string.match(asset_name, spec.asset.exclude) then + error("GitHub asset exclude fixture mismatch") + end + return package_version { + name = spec.name, + installed = { + { kind = "android_package", package_name = spec.android_package }, + }, + source_priority = { "github" }, + updates = { + { + version = "v1.20.0", + source = "github", + artifacts = { + { + name = asset_name, + url = "https://github.com/f-droid/fdroidclient/releases/download/v1.20.0/F-Droid.apk", + file_name = asset_name, + }, + }, + }, + }, + } +end + +return github_android +"#, + ) + .unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + android_package = "org.fdroid.fdroid", + owner = "f-droid", + repo = "fdroidclient", + asset = { + include = "%.apk$", + exclude = "debug", + }, +} +"#, + ) + .unwrap(); + + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + + assert_eq!(package.id.to_string(), "android/app/org.fdroid.fdroid"); + assert_eq!(package.repository.as_str(), "official"); + assert_eq!(package.name, "F-Droid"); + assert_eq!( + package.installed, + vec![InstalledTarget::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned() + }] + ); + assert_eq!(package.source_priority, vec!["github"]); + assert_eq!(package.updates.len(), 1); + assert_eq!(package.updates[0].version, "v1.20.0"); + assert_eq!(package.updates[0].source.as_deref(), Some("github")); + assert_eq!( + package.updates[0].artifacts[0].url, + "https://github.com/f-droid/fdroidclient/releases/download/v1.20.0/F-Droid.apk" + ); + assert_eq!( + package.updates[0].artifacts[0].file_name.as_deref(), + Some("F-Droid.apk") + ); + } + #[test] fn package_directory_can_read_package_local_files() { let temp = tempfile::tempdir().unwrap(); From 7eb6ab2d4b4cfe2515c0fcabf469b91c3ed04f12 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 18:39:57 +0800 Subject: [PATCH 45/71] feat(lua): add host binding injection seam --- crates/getter-core/src/lua.rs | 148 +++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 2 deletions(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index e45801c..cc1b084 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -53,6 +53,30 @@ pub fn evaluate_package_directory_script( metadata: &PackageDirectoryMetadata, script: &PackageVersionScript, ) -> Result { + evaluate_package_directory_script_with_host_bindings( + repository_id, + package, + metadata, + script, + |_| Ok(()), + ) +} + +/// Evaluates a package version script with caller-installed Lua host bindings. +/// +/// This is an internal seam for higher-level getter operations to provide +/// getter-owned host functions while keeping `getter-core` independent from +/// provider/cache crates. It does not define a stable public Lua provider API. +pub fn evaluate_package_directory_script_with_host_bindings( + repository_id: &RepositoryId, + package: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, + install_host_bindings: F, +) -> Result +where + F: FnOnce(&Lua) -> mlua::Result<()>, +{ let source = fs::read_to_string(&script.path).map_err(|source| LuaPackageError::ReadFile { path: script.path.clone(), source, @@ -70,15 +94,20 @@ pub fn evaluate_package_directory_script( &LuaRepositoryEnvironment::PackageDirectory { package }, &script.path, &source, + install_host_bindings, )?; validate_package_directory_version_json(repository_id, package, metadata, script, json) } -fn evaluate_package_source_to_json( +fn evaluate_package_source_to_json( environment: &LuaRepositoryEnvironment<'_>, path: &Path, source: &str, -) -> Result { + install_host_bindings: F, +) -> Result +where + F: FnOnce(&Lua) -> mlua::Result<()>, +{ let path = path.to_path_buf(); let lua = Lua::new(); configure_package_path(&lua, environment).map_err(|source| LuaPackageError::Runtime { @@ -93,6 +122,10 @@ fn evaluate_package_source_to_json( path: path.clone(), source: Box::new(source), })?; + install_host_bindings(&lua).map_err(|source| LuaPackageError::Runtime { + path: path.clone(), + source: Box::new(source), + })?; let value = lua .load(source) @@ -1187,6 +1220,117 @@ return github_android.package { ); } + #[test] + fn package_directory_luaclass_can_call_injected_provider_host() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::create_dir_all(temp.path().join("luaclass")).unwrap(); + fs::write( + temp.path().join("luaclass/github_android_apk.lua"), + r#" +local github_android = {} + +function github_android.package(spec) + local releases = getter_test_provider.github_releases(spec.owner, spec.repo, spec.asset) + return package_version { + name = spec.name, + source_priority = { "github" }, + updates = releases, + } +end + +return github_android +"#, + ) + .unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + owner = "f-droid", + repo = "fdroidclient", + asset = { include = "%.apk$" }, +} +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("official").unwrap(), + package_dir, + &metadata, + script, + |lua| { + let provider = lua.create_table()?; + provider.set( + "github_releases", + lua.create_function( + |lua, (owner, repo, asset): (String, String, Table)| { + if owner != "f-droid" || repo != "fdroidclient" { + return Err(mlua::Error::external("GitHub fixture mismatch")); + } + let include: String = asset.get("include")?; + if include != "%.apk$" { + return Err(mlua::Error::external("GitHub asset filter mismatch")); + } + let artifact = lua.create_table()?; + artifact.set("name", "F-Droid.apk")?; + artifact.set( + "url", + "https://github.com/f-droid/fdroidclient/releases/download/v1.20.0/F-Droid.apk", + )?; + artifact.set("file_name", "F-Droid.apk")?; + let artifacts = lua.create_table()?; + artifacts.raw_set(1, artifact)?; + let candidate = lua.create_table()?; + candidate.set("version", "v1.20.0")?; + candidate.set("source", "github")?; + candidate.set("artifacts", artifacts)?; + let candidates = lua.create_table()?; + candidates.raw_set(1, candidate)?; + Ok(candidates) + }, + )?, + )?; + lua.globals().set("getter_test_provider", provider) + }, + ) + .unwrap(); + + assert_eq!(package.id.to_string(), "android/app/org.fdroid.fdroid"); + assert_eq!(package.repository.as_str(), "official"); + assert_eq!(package.name, "F-Droid"); + assert_eq!( + package.installed, + vec![InstalledTarget::AndroidPackage { + package_name: "org.fdroid.fdroid".to_owned() + }] + ); + assert_eq!(package.source_priority, vec!["github"]); + assert_eq!(package.updates.len(), 1); + assert_eq!(package.updates[0].version, "v1.20.0"); + assert_eq!(package.updates[0].source.as_deref(), Some("github")); + assert_eq!( + package.updates[0].artifacts[0].file_name.as_deref(), + Some("F-Droid.apk") + ); + } + #[test] fn package_directory_can_read_package_local_files() { let temp = tempfile::tempdir().unwrap(); From 236cfaedf9d21833045337962cfcc375a9525c2d Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 19:52:46 +0800 Subject: [PATCH 46/71] feat(lua): add dev provider host package eval --- Cargo.lock | 1 + crates/getter-operations/Cargo.toml | 4 +- crates/getter-operations/src/lib.rs | 3 + .../src/lua_provider_host.rs | 530 ++++++++++++++++++ 4 files changed, 537 insertions(+), 1 deletion(-) create mode 100644 crates/getter-operations/src/lua_provider_host.rs diff --git a/Cargo.lock b/Cargo.lock index c072fcc..a65d2b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -618,6 +618,7 @@ dependencies = [ "getter-providers", "getter-storage", "json_comments", + "mlua", "serde", "serde_json", "sha2", diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index ed85e8c..81c2e82 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -5,13 +5,15 @@ edition.workspace = true [features] default = [] -lua = ["getter-core/lua"] +lua = ["getter-core/lua", "dep:mlua"] +lua-provider-host-dev = ["lua"] [dependencies] json_comments = "0.2" getter-core = { path = "../getter-core", default-features = false } getter-providers = { path = "../getter-providers" } getter-storage = { path = "../getter-storage" } +mlua = { version = "0.10", features = ["luajit", "vendored"], optional = true } serde = { version = "1", features = ["derive"] } serde_json = "1" sha2 = "0.10" diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 30097ff..ae19c2d 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -11,6 +11,9 @@ pub mod fdroid_catalog; pub mod github_latest_commit; pub mod github_releases; pub mod legacy_room; +#[cfg(feature = "lua-provider-host-dev")] +#[doc(hidden)] +pub mod lua_provider_host; pub mod provider_cache; pub mod read_model; pub mod runtime; diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs new file mode 100644 index 0000000..0d3b2b4 --- /dev/null +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -0,0 +1,530 @@ +//! Operation-installed Lua provider host bindings. +//! +//! This module is an implementation bridge toward ADR-0012 provider-backed Lua +//! modules. It exercises higher-level getter operations installing host +//! functions for package evaluation without making `getter-core` depend on +//! provider cache/storage crates and without defining the final stable Lua +//! provider API. + +use crate::github_releases::{ + read_or_refresh_github_releases, GithubReleaseConfig, GithubReleaseOperationError, + DEFAULT_GITHUB_API_BASE_URL, GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, +}; +use crate::provider_cache::{ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheSource}; +use getter_core::lua::{evaluate_package_directory_script_with_host_bindings, LuaPackageError}; +use getter_core::repository::{RepositoryLoadError, RepositoryPackageDirectoryLayout}; +use getter_core::{PackageId, RepositoryId, UpdateArtifact, UpdateCandidate}; +use getter_providers::{ + github_release_update_candidates, GithubAssetFilter, GithubProviderError, GithubRelease, + GithubReleaseCandidateOptions, +}; +use getter_storage::{CacheDb, MainDb, StorageError, StoredRepository}; +use mlua::{Lua, Table, Value as LuaValue}; +use serde::Deserialize; +use serde_json::{json, Value}; +use std::cell::RefCell; +use std::path::{Path, PathBuf}; +use std::rc::Rc; + +#[derive(Debug, thiserror::Error)] +pub enum LuaProviderHostOperationError { + #[error("invalid Lua provider-host request: {0}")] + InvalidRequest(String), + #[error("storage operation failed: {0}")] + Storage(#[from] StorageError), + #[error("repository operation failed: {0}")] + Repository(#[from] RepositoryLoadError), + #[error("package evaluation failed: {0}")] + PackageEval(#[from] LuaPackageError), + #[error("GitHub provider operation failed: {0}")] + Github(#[from] GithubReleaseOperationError), + #[error("GitHub provider normalization failed: {0}")] + GithubProvider(#[from] GithubProviderError), + #[error("Lua provider-host response serialization failed: {0}")] + Serialization(#[from] serde_json::Error), +} + +/// Evaluates a package with a fixture-backed GitHub release host binding. +/// +/// This is a development/provider-host tracer, not the stable package-eval or +/// product update-check API. Repository packages can exercise repository-local +/// `luaclass/` modules that call `getter_dev.github_release_candidates { ... }`, +/// while release parsing/cache behavior stays in `getter-operations`. +pub fn github_package_eval_json( + data_dir: &Path, + request_json: &str, +) -> Result { + let request: GithubPackageEvalRequest = serde_json::from_str(request_json) + .map_err(|source| LuaProviderHostOperationError::InvalidRequest(source.to_string()))?; + let mode = provider_cache_mode(request.mode.as_deref())?; + let api_base_url = request + .api_base_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()); + let main_db = MainDb::open(data_dir.join("main.db"))?; + let repository = find_repository(&main_db, &request.repository_id)?; + let repository_path = repo_path(&repository)?; + let layout = RepositoryPackageDirectoryLayout::load(&repository_path)?; + let package_directory = layout.package(&request.package_id).ok_or_else(|| { + LuaProviderHostOperationError::InvalidRequest(format!( + "package '{}' was not found in repository '{}'", + request.package_id, request.repository_id + )) + })?; + let metadata = layout.package_metadata(package_directory)?; + let script = layout.unambiguous_version_script(package_directory)?; + let provider_calls = Rc::new(RefCell::new(Vec::new())); + let cache_db_path = data_dir.join("cache.db"); + let default_include_prereleases = request.include_prereleases; + let releases_json = request.releases_json; + let package = evaluate_package_directory_script_with_host_bindings( + &repository.id, + package_directory, + &metadata, + script, + { + let provider_calls = Rc::clone(&provider_calls); + move |lua| { + install_github_dev_host( + lua, + GithubDevHostConfig { + cache_db_path, + api_base_url, + mode, + releases_json, + default_include_prereleases, + provider_calls, + }, + ) + } + }, + )?; + let package = serde_json::to_value(package)?; + + Ok(json!({ + "operation": "github.package_eval.fixture", + "package": package, + "provider_calls": provider_calls.borrow().clone(), + })) +} + +#[derive(Debug, Deserialize)] +struct GithubPackageEvalRequest { + repository_id: RepositoryId, + package_id: PackageId, + #[serde(default)] + api_base_url: Option, + #[serde(default)] + mode: Option, + #[serde(default)] + releases_json: Option, + #[serde(default)] + include_prereleases: bool, +} + +struct GithubDevHostConfig { + cache_db_path: PathBuf, + api_base_url: String, + mode: ProviderCacheMode, + releases_json: Option, + default_include_prereleases: bool, + provider_calls: Rc>>, +} + +fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Result<()> { + let host = lua.create_table()?; + host.set( + "github_release_candidates", + lua.create_function(move |lua, spec: Table| { + let request = GithubReleaseHostRequest::from_lua_table(&spec)?; + let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; + let provider_config = GithubReleaseConfig { + api_base_url: config.api_base_url.clone(), + owner: request.owner, + repo: request.repo, + }; + let refresh_fixture = config.releases_json.clone(); + let result = read_or_refresh_github_releases(&db, provider_config, config.mode, || { + refresh_fixture.ok_or_else(|| { + "fixture-backed GitHub release host requires releases_json".to_owned() + }) + }) + .map_err(mlua::Error::external)?; + let options = GithubReleaseCandidateOptions { + include_prereleases: request + .include_prereleases + .unwrap_or(config.default_include_prereleases), + asset_filter: request.asset_filter, + }; + let candidates = github_release_update_candidates(&result.releases, &options) + .map_err(mlua::Error::external)?; + let mut diagnostics = result + .diagnostics + .iter() + .map(provider_diagnostic_json) + .collect::>(); + if candidates.is_empty() + && github_eligible_release_count(&result.releases, &options) > 0 + { + diagnostics.push(json!({ + "code": GITHUB_ASSET_NOT_FOUND, + "message": "no GitHub release assets matched the requested filters", + "provider": GITHUB_PROVIDER_ID, + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + })); + } + config.provider_calls.borrow_mut().push(json!({ + "provider": GITHUB_PROVIDER_ID, + "request": "releases", + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "diagnostics": diagnostics, + })); + update_candidates_to_lua(lua, &candidates) + })?, + )?; + lua.globals().set("getter_dev", host) +} + +struct GithubReleaseHostRequest { + owner: String, + repo: String, + asset_filter: GithubAssetFilter, + include_prereleases: Option, +} + +impl GithubReleaseHostRequest { + fn from_lua_table(table: &Table) -> mlua::Result { + Ok(Self { + owner: required_lua_string(table, "owner")?, + repo: required_lua_string(table, "repo")?, + asset_filter: asset_filter_from_lua(table.get("asset")?)?, + include_prereleases: optional_lua_bool(table, "include_prereleases")?, + }) + } +} + +fn provider_cache_mode( + mode: Option<&str>, +) -> Result { + match mode { + Some("force_refresh") => Ok(ProviderCacheMode::ForceRefresh), + Some("use_cached") | None => Ok(ProviderCacheMode::UseCached), + Some(other) => Err(LuaProviderHostOperationError::InvalidRequest(format!( + "unknown mode '{other}'" + ))), + } +} + +fn find_repository( + db: &MainDb, + repository_id: &RepositoryId, +) -> Result { + db.repositories()? + .into_iter() + .find(|repository| &repository.id == repository_id) + .ok_or_else(|| { + LuaProviderHostOperationError::InvalidRequest(format!( + "repository '{repository_id}' is not registered" + )) + }) +} + +fn repo_path(repository: &StoredRepository) -> Result { + repository.path.as_ref().map(PathBuf::from).ok_or_else(|| { + LuaProviderHostOperationError::InvalidRequest(format!( + "repository '{}' has no path", + repository.id + )) + }) +} + +fn required_lua_string(table: &Table, field: &'static str) -> mlua::Result { + let value: Option = table.get(field)?; + value + .filter(|value| !value.trim().is_empty()) + .ok_or_else(|| { + mlua::Error::external(format!( + "getter_dev.github_release_candidates missing {field}" + )) + }) +} + +fn optional_lua_bool(table: &Table, field: &'static str) -> mlua::Result> { + match table.get::(field)? { + LuaValue::Nil => Ok(None), + LuaValue::Boolean(value) => Ok(Some(value)), + _ => Err(mlua::Error::external(format!( + "getter_dev.github_release_candidates {field} must be a boolean" + ))), + } +} + +fn asset_filter_from_lua(value: LuaValue) -> mlua::Result { + match value { + LuaValue::Nil => Ok(GithubAssetFilter::default()), + LuaValue::Table(table) => Ok(GithubAssetFilter { + include: optional_lua_string(&table, "include")?, + exclude: optional_lua_string(&table, "exclude")?, + }), + _ => Err(mlua::Error::external( + "getter_dev.github_release_candidates asset must be a table", + )), + } +} + +fn optional_lua_string(table: &Table, field: &'static str) -> mlua::Result> { + match table.get::(field)? { + LuaValue::Nil => Ok(None), + LuaValue::String(value) => Ok(Some(value.to_str()?.to_owned())), + _ => Err(mlua::Error::external(format!( + "getter_dev.github_release_candidates asset.{field} must be a string" + ))), + } +} + +fn github_eligible_release_count( + releases: &[GithubRelease], + options: &GithubReleaseCandidateOptions, +) -> usize { + releases + .iter() + .filter(|release| !release.draft) + .filter(|release| options.include_prereleases || !release.prerelease) + .count() +} + +fn update_candidates_to_lua(lua: &Lua, candidates: &[UpdateCandidate]) -> mlua::Result { + if candidates.is_empty() { + return Ok(LuaValue::Nil); + } + + let table = lua.create_table()?; + for (index, candidate) in candidates.iter().enumerate() { + table.raw_set(index + 1, update_candidate_to_lua(lua, candidate)?)?; + } + Ok(LuaValue::Table(table)) +} + +fn update_candidate_to_lua(lua: &Lua, candidate: &UpdateCandidate) -> mlua::Result
{ + let table = lua.create_table()?; + table.set("version", candidate.version.as_str())?; + if let Some(version_code) = candidate.version_code { + table.set("version_code", version_code)?; + } + if let Some(channel) = candidate.channel.as_ref() { + table.set("channel", channel.as_str())?; + } + if let Some(source) = candidate.source.as_ref() { + table.set("source", source.as_str())?; + } + let artifacts = lua.create_table()?; + for (index, artifact) in candidate.artifacts.iter().enumerate() { + artifacts.raw_set(index + 1, update_artifact_to_lua(lua, artifact)?)?; + } + table.set("artifacts", artifacts)?; + Ok(table) +} + +fn update_artifact_to_lua(lua: &Lua, artifact: &UpdateArtifact) -> mlua::Result
{ + let table = lua.create_table()?; + table.set("name", artifact.name.as_str())?; + table.set("url", artifact.url.as_str())?; + if let Some(file_name) = artifact.file_name.as_ref() { + table.set("file_name", file_name.as_str())?; + } + if let Some(sha256) = artifact.sha256.as_ref() { + table.set("sha256", sha256.as_str())?; + } + if let Some(size) = artifact.size { + table.set("size", size)?; + } + Ok(table) +} + +fn provider_source_json(source: ProviderCacheSource) -> &'static str { + match source { + ProviderCacheSource::Cache => "cache", + ProviderCacheSource::Refreshed => "refreshed", + ProviderCacheSource::Stale => "stale", + } +} + +fn provider_diagnostic_json(diagnostic: &ProviderCacheDiagnostic) -> Value { + json!({ + "code": diagnostic.code, + "message": diagnostic.message, + "cache_key": diagnostic.cache_key, + "provider": diagnostic.provider, + "stale_fetched_at_unix": diagnostic.stale_fetched_at_unix, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; + use getter_core::{RepositoryId, RepositoryPriority}; + use getter_storage::CacheDb; + use serde_json::json; + use std::fs; + + const GITHUB_RELEASES_FIXTURE: &str = r#"[ + { + "tag_name": "v1.20.0", + "name": "F-Droid 1.20.0", + "draft": false, + "prerelease": false, + "published_at": "2026-06-01T00:00:00Z", + "assets": [ + { + "name": "F-Droid.apk", + "browser_download_url": "https://github.com/f-droid/fdroidclient/releases/download/v1.20.0/F-Droid.apk", + "content_type": "application/vnd.android.package-archive", + "size": 12345, + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } +]"#; + + #[test] + fn github_package_eval_uses_repository_luaclass_and_provider_cache() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_github_package_fixture(data_dir, "[.]apk$"); + + let first = github_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(first["operation"], "github.package_eval.fixture"); + assert_eq!(first["provider_calls"][0]["source"], "refreshed"); + assert_eq!(first["package"]["name"], "F-Droid"); + assert_eq!(first["package"]["source_priority"], json!(["github"])); + assert_eq!(first["package"]["updates"][0]["version"], "v1.20.0"); + assert_eq!(first["package"]["updates"][0]["source"], "github"); + assert_eq!( + first["package"]["updates"][0]["artifacts"][0]["sha256"], + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ); + let cache_key = first["provider_calls"][0]["cache_key"].as_str().unwrap(); + assert!(CacheDb::open(data_dir.join("cache.db")) + .unwrap() + .provider_response(cache_key) + .unwrap() + .is_some()); + + let second = github_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(second["provider_calls"][0]["source"], "cache"); + assert_eq!(second["package"]["updates"][0]["version"], "v1.20.0"); + } + + #[test] + fn github_package_eval_reports_asset_filter_diagnostics() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_github_package_fixture(data_dir, "[.]zip$"); + + let result = github_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["package"]["updates"], json!([])); + assert_eq!( + result["provider_calls"][0]["diagnostics"][0]["code"], + GITHUB_ASSET_NOT_FOUND + ); + assert_eq!( + result["provider_calls"][0]["diagnostics"][0]["provider"], + GITHUB_PROVIDER_ID + ); + } + + fn write_github_package_fixture(data_dir: &std::path::Path, asset_include: &str) { + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(repo_root.join("luaclass")).unwrap(); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + repo_root.join("luaclass/github_android_apk.lua"), + r#" +local github_android = {} + +function github_android.package(spec) + return package_version { + name = spec.name, + source_priority = { "github" }, + updates = getter_dev.github_release_candidates { + owner = spec.owner, + repo = spec.repo, + asset = spec.asset, + }, + } +end + +return github_android +"#, + ) + .unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + let version_script = r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + owner = "f-droid", + repo = "fdroidclient", + asset = { include = "__ASSET_INCLUDE__" }, +} +"# + .replace("__ASSET_INCLUDE__", asset_include); + fs::write(package_dir.join("9999.lua"), version_script).unwrap(); + let main_db = MainDb::open(data_dir.join("main.db")).unwrap(); + main_db + .upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::DEFAULT, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&repo_root), + None, + ) + .unwrap(); + } +} From 0f74313b52e3cc6083223125376dfa2a3b122a80 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 20:40:11 +0800 Subject: [PATCH 47/71] feat(lua): add F-Droid dev provider host eval --- .../src/lua_provider_host.rs | 334 +++++++++++++++++- 1 file changed, 331 insertions(+), 3 deletions(-) diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 0d3b2b4..ee16e1a 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -6,6 +6,10 @@ //! provider cache/storage crates and without defining the final stable Lua //! provider API. +use crate::fdroid_catalog::{ + read_or_refresh_fdroid_catalog, FdroidCatalogOperationError, FdroidEndpointConfig, + DEFAULT_FDROID_ENDPOINT_ID, DEFAULT_FDROID_ENDPOINT_URL, FDROID_PROVIDER_ID, +}; use crate::github_releases::{ read_or_refresh_github_releases, GithubReleaseConfig, GithubReleaseOperationError, DEFAULT_GITHUB_API_BASE_URL, GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, @@ -15,8 +19,8 @@ use getter_core::lua::{evaluate_package_directory_script_with_host_bindings, Lua use getter_core::repository::{RepositoryLoadError, RepositoryPackageDirectoryLayout}; use getter_core::{PackageId, RepositoryId, UpdateArtifact, UpdateCandidate}; use getter_providers::{ - github_release_update_candidates, GithubAssetFilter, GithubProviderError, GithubRelease, - GithubReleaseCandidateOptions, + github_release_update_candidates, FdroidEndpoint, GithubAssetFilter, GithubProviderError, + GithubRelease, GithubReleaseCandidateOptions, }; use getter_storage::{CacheDb, MainDb, StorageError, StoredRepository}; use mlua::{Lua, Table, Value as LuaValue}; @@ -36,6 +40,8 @@ pub enum LuaProviderHostOperationError { Repository(#[from] RepositoryLoadError), #[error("package evaluation failed: {0}")] PackageEval(#[from] LuaPackageError), + #[error("F-Droid provider operation failed: {0}")] + Fdroid(#[from] FdroidCatalogOperationError), #[error("GitHub provider operation failed: {0}")] Github(#[from] GithubReleaseOperationError), #[error("GitHub provider normalization failed: {0}")] @@ -44,6 +50,98 @@ pub enum LuaProviderHostOperationError { Serialization(#[from] serde_json::Error), } +const FDROID_PACKAGE_NOT_FOUND: &str = "provider.fdroid.package_not_found"; + +/// Evaluates a package with a fixture-backed F-Droid catalog host binding. +/// +/// This is a development/provider-host tracer, not the stable package-eval or +/// product update-check API. Repository packages can exercise repository-local +/// `luaclass/` modules that call `getter_dev.fdroid_update_candidates { ... }`, +/// while catalog parsing/cache behavior stays in `getter-operations`. +pub fn fdroid_package_eval_json( + data_dir: &Path, + request_json: &str, +) -> Result { + let request: FdroidPackageEvalRequest = serde_json::from_str(request_json) + .map_err(|source| LuaProviderHostOperationError::InvalidRequest(source.to_string()))?; + let mode = provider_cache_mode(request.mode.as_deref())?; + let endpoint = FdroidEndpointConfig { + endpoint_id: request + .endpoint_id + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_ID.to_owned()), + endpoint_url: request + .endpoint_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), + }; + let main_db = MainDb::open(data_dir.join("main.db"))?; + let repository = find_repository(&main_db, &request.repository_id)?; + let repository_path = repo_path(&repository)?; + let layout = RepositoryPackageDirectoryLayout::load(&repository_path)?; + let package_directory = layout.package(&request.package_id).ok_or_else(|| { + LuaProviderHostOperationError::InvalidRequest(format!( + "package '{}' was not found in repository '{}'", + request.package_id, request.repository_id + )) + })?; + let metadata = layout.package_metadata(package_directory)?; + let script = layout.unambiguous_version_script(package_directory)?; + let provider_calls = Rc::new(RefCell::new(Vec::new())); + let cache_db_path = data_dir.join("cache.db"); + let index_xml = request.index_xml; + let package = evaluate_package_directory_script_with_host_bindings( + &repository.id, + package_directory, + &metadata, + script, + { + let provider_calls = Rc::clone(&provider_calls); + move |lua| { + install_fdroid_dev_host( + lua, + FdroidDevHostConfig { + cache_db_path, + endpoint, + mode, + index_xml, + provider_calls, + }, + ) + } + }, + )?; + let package = serde_json::to_value(package)?; + + Ok(json!({ + "operation": "fdroid.package_eval.fixture", + "package": package, + "provider_calls": provider_calls.borrow().clone(), + })) +} + +#[derive(Debug, Deserialize)] +struct FdroidPackageEvalRequest { + repository_id: RepositoryId, + package_id: PackageId, + #[serde(default)] + endpoint_id: Option, + #[serde(default)] + endpoint_url: Option, + #[serde(default)] + mode: Option, + #[serde(default)] + index_xml: Option, +} + +struct FdroidDevHostConfig { + cache_db_path: PathBuf, + endpoint: FdroidEndpointConfig, + mode: ProviderCacheMode, + index_xml: Option, + provider_calls: Rc>>, +} + /// Evaluates a package with a fixture-backed GitHub release host binding. /// /// This is a development/provider-host tracer, not the stable package-eval or @@ -131,6 +229,82 @@ struct GithubDevHostConfig { provider_calls: Rc>>, } +fn install_fdroid_dev_host(lua: &Lua, config: FdroidDevHostConfig) -> mlua::Result<()> { + let host = lua.create_table()?; + host.set( + "fdroid_update_candidates", + lua.create_function(move |lua, spec: Table| { + let request = FdroidUpdateHostRequest::from_lua_table(&spec)?; + let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; + let endpoint = config.endpoint.clone(); + let refresh_fixture = config.index_xml.clone(); + let result = read_or_refresh_fdroid_catalog(&db, endpoint, config.mode, || { + refresh_fixture.ok_or_else(|| { + "fixture-backed F-Droid catalog host requires index_xml".to_owned() + }) + }) + .map_err(mlua::Error::external)?; + let mut diagnostics = result + .diagnostics + .iter() + .map(provider_diagnostic_json) + .collect::>(); + let candidates = result + .app(&request.package_name) + .map(|app| { + let endpoint = FdroidEndpoint { + name: result.catalog.endpoint.name.clone(), + url: Some(result.endpoint.endpoint_url.clone()), + timestamp: result.catalog.endpoint.timestamp.clone(), + }; + app.update_candidates(&endpoint) + }) + .unwrap_or_else(|| { + diagnostics.push(json!({ + "code": FDROID_PACKAGE_NOT_FOUND, + "message": format!("F-Droid package '{}' was not found in endpoint '{}'", request.package_name, result.endpoint.endpoint_id), + "provider": FDROID_PROVIDER_ID, + "endpoint_id": result.endpoint.endpoint_id, + "package_name": request.package_name, + "cache_key": result.cache_key, + })); + Vec::new() + }); + config.provider_calls.borrow_mut().push(json!({ + "provider": FDROID_PROVIDER_ID, + "request": "catalog", + "endpoint_id": result.endpoint.endpoint_id, + "endpoint_url": result.endpoint.endpoint_url, + "package_name": request.package_name, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "diagnostics": diagnostics, + })); + update_candidates_to_lua(lua, &candidates) + })?, + )?; + lua.globals().set("getter_dev", host) +} + +struct FdroidUpdateHostRequest { + package_name: String, +} + +impl FdroidUpdateHostRequest { + fn from_lua_table(table: &Table) -> mlua::Result { + let package_name: Option = table.get("package_name")?; + Ok(Self { + package_name: package_name + .filter(|value| !value.trim().is_empty()) + .ok_or_else(|| { + mlua::Error::external( + "getter_dev.fdroid_update_candidates missing package_name", + ) + })?, + }) + } +} + fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Result<()> { let host = lua.create_table()?; host.set( @@ -373,6 +547,23 @@ mod tests { use serde_json::json; use std::fs; + const FDROID_INDEX_FIXTURE: &str = r#" + + + + F-Droid + App repository client + + 1.20.0 + 1020000 + org.fdroid.fdroid_1020000.apk + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 1234567 + + + +"#; + const GITHUB_RELEASES_FIXTURE: &str = r#"[ { "tag_name": "v1.20.0", @@ -392,6 +583,96 @@ mod tests { } ]"#; + #[test] + fn fdroid_package_eval_uses_repository_luaclass_and_provider_cache() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_fdroid_package_fixture(data_dir, "org.fdroid.fdroid"); + + let first = fdroid_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(first["operation"], "fdroid.package_eval.fixture"); + assert_eq!(first["provider_calls"][0]["source"], "refreshed"); + assert_eq!(first["provider_calls"][0]["endpoint_id"], "official"); + assert_eq!( + first["provider_calls"][0]["package_name"], + "org.fdroid.fdroid" + ); + assert_eq!(first["package"]["name"], "F-Droid"); + assert_eq!(first["package"]["source_priority"], json!(["fdroid"])); + assert_eq!(first["package"]["updates"][0]["version"], "1.20.0"); + assert_eq!(first["package"]["updates"][0]["version_code"], 1020000); + assert_eq!(first["package"]["updates"][0]["source"], "fdroid"); + assert_eq!( + first["package"]["updates"][0]["artifacts"][0]["url"], + "https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk" + ); + assert_eq!( + first["package"]["updates"][0]["artifacts"][0]["sha256"], + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ); + let cache_key = first["provider_calls"][0]["cache_key"].as_str().unwrap(); + assert!(CacheDb::open(data_dir.join("cache.db")) + .unwrap() + .provider_response(cache_key) + .unwrap() + .is_some()); + + let second = fdroid_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(second["provider_calls"][0]["source"], "cache"); + assert_eq!(second["package"]["updates"][0]["version"], "1.20.0"); + } + + #[test] + fn fdroid_package_eval_reports_package_not_found_diagnostics() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_fdroid_package_fixture(data_dir, "missing.package"); + + let result = fdroid_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["package"]["updates"], json!([])); + assert_eq!( + result["provider_calls"][0]["diagnostics"][0]["code"], + FDROID_PACKAGE_NOT_FOUND + ); + assert_eq!( + result["provider_calls"][0]["diagnostics"][0]["provider"], + FDROID_PROVIDER_ID + ); + assert_eq!( + result["provider_calls"][0]["diagnostics"][0]["package_name"], + "missing.package" + ); + } + #[test] fn github_package_eval_uses_repository_luaclass_and_provider_cache() { let temp = tempfile::tempdir().unwrap(); @@ -468,6 +749,49 @@ mod tests { ); } + fn write_fdroid_package_fixture(data_dir: &std::path::Path, package_name: &str) { + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join("android/f-droid/app/org.fdroid.fdroid"); + fs::create_dir_all(repo_root.join("luaclass")).unwrap(); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + repo_root.join("luaclass/fdroid_android.lua"), + r#" +local fdroid = {} + +function fdroid.package(spec) + return package_version { + source_priority = { "fdroid" }, + updates = getter_dev.fdroid_update_candidates { + package_name = spec.package_name, + }, + } +end + +return fdroid +"#, + ) + .unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "display_name": "F-Droid", + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + let version_script = r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { + package_name = "__PACKAGE_NAME__", +} +"# + .replace("__PACKAGE_NAME__", package_name); + fs::write(package_dir.join("9999.lua"), version_script).unwrap(); + register_official_repository(data_dir, &repo_root); + } + fn write_github_package_fixture(data_dir: &std::path::Path, asset_include: &str) { let repo_root = data_dir.join("repo/official"); let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); @@ -513,6 +837,10 @@ return github_android.package { "# .replace("__ASSET_INCLUDE__", asset_include); fs::write(package_dir.join("9999.lua"), version_script).unwrap(); + register_official_repository(data_dir, &repo_root); + } + + fn register_official_repository(data_dir: &std::path::Path, repo_root: &std::path::Path) { let main_db = MainDb::open(data_dir.join("main.db")).unwrap(); main_db .upsert_repository( @@ -522,7 +850,7 @@ return github_android.package { priority: RepositoryPriority::DEFAULT, api_version: REPO_API_VERSION_V1.to_owned(), }, - Some(&repo_root), + Some(repo_root), None, ) .unwrap(); From 517bbcf43d4244b93bf8b0f49077905aaf3b630f Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 21:56:44 +0800 Subject: [PATCH 48/71] feat(lua): add builtin luaclass fallback --- crates/getter-core/src/lua.rs | 160 ++++++++++++++------ crates/getter-core/src/luaclass/android.lua | 12 ++ 2 files changed, 123 insertions(+), 49 deletions(-) create mode 100644 crates/getter-core/src/luaclass/android.lua diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index cc1b084..baad1ce 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -8,11 +8,14 @@ use crate::{ InstalledTarget, PackageId, PackagePermissions, RepositoryId, ResolvedPackage, UpdateArtifact, UpdateCandidate, }; -use mlua::{Lua, Table, Value}; +use mlua::{Function, Lua, Table, Value}; use serde_json::{Map, Number, Value as JsonValue}; use std::fs; use std::path::{Path, PathBuf}; +const BUILTIN_LUACLASS_MODULES: &[(&str, &str)] = + &[("android", include_str!("luaclass/android.lua"))]; + #[derive(Debug, thiserror::Error)] pub enum LuaPackageError { #[error("failed to read Lua package file {path}: {source}")] @@ -158,7 +161,8 @@ fn configure_package_path( package: package_dir, } => { package.set("path", "")?; - install_luaclass_prefix_searcher( + install_builtin_luaclass_searcher(lua, &package)?; + install_repository_luaclass_searcher( lua, &package, package_directory_repository_root(package_dir).join(REPOSITORY_LUACLASS_DIR), @@ -198,62 +202,85 @@ fn remove_unsafe_globals(lua: &Lua) -> mlua::Result<()> { Ok(()) } -fn install_luaclass_prefix_searcher( +fn install_repository_luaclass_searcher( lua: &Lua, package: &Table, luaclass_dir: PathBuf, ) -> mlua::Result<()> { - install_prefixed_file_searcher( + install_searcher( lua, package, - "luaclass.", - luaclass_dir, - "constrained repository luaclass searcher only handles luaclass.* modules", + lua.create_function(move |lua, module: String| { + let Some(module) = module.strip_prefix("luaclass.") else { + return lua + .create_string("\n\tconstrained repository luaclass searcher only handles luaclass.* modules") + .map(Value::String); + }; + + let Some(relative_module) = module_to_relative_path(module) else { + return lua + .create_string(format!("\n\tinvalid repository module name 'luaclass.{module}'")) + .map(Value::String); + }; + + let module_path = luaclass_dir.join(&relative_module).with_extension("lua"); + let init_path = luaclass_dir.join(&relative_module).join("init.lua"); + for candidate in [&module_path, &init_path] { + if candidate.is_file() { + let source = fs::read_to_string(candidate).map_err(mlua::Error::external)?; + let chunk = lua + .load(&source) + .set_name(candidate.to_string_lossy().as_ref()); + return chunk.into_function().map(Value::Function); + } + } + + lua.create_string(format!( + "\n\tno repository module 'luaclass.{module}' in {}", + luaclass_dir.display() + )) + .map(Value::String) + })?, ) } -fn install_prefixed_file_searcher( - lua: &Lua, - package: &Table, - prefix: &'static str, - module_dir: PathBuf, - wrong_prefix_message: &'static str, -) -> mlua::Result<()> { - let searchers = package_searchers(package)?; - let searcher = lua.create_function(move |lua, module: String| { - let Some(module) = module.strip_prefix(prefix) else { - return lua - .create_string(format!("\n\t{wrong_prefix_message}")) - .map(Value::String); - }; - - let Some(relative_module) = module_to_relative_path(module) else { - return lua - .create_string(format!( - "\n\tinvalid repository module name '{prefix}{module}'" - )) - .map(Value::String); - }; - - let module_path = module_dir.join(&relative_module).with_extension("lua"); - let init_path = module_dir.join(&relative_module).join("init.lua"); - for candidate in [&module_path, &init_path] { - if candidate.is_file() { - let source = fs::read_to_string(candidate).map_err(mlua::Error::external)?; - let chunk = lua - .load(&source) - .set_name(candidate.to_string_lossy().as_ref()); - return chunk.into_function().map(Value::Function); +fn install_builtin_luaclass_searcher(lua: &Lua, package: &Table) -> mlua::Result<()> { + install_searcher( + lua, + package, + lua.create_function(move |lua, module: String| { + let Some(module) = module.strip_prefix("luaclass.") else { + return lua + .create_string("\n\tbuiltin luaclass searcher only handles luaclass.* modules") + .map(Value::String); + }; + if module_to_relative_path(module).is_none() { + return lua + .create_string(format!( + "\n\tinvalid builtin luaclass module name 'luaclass.{module}'" + )) + .map(Value::String); } - } - - lua.create_string(format!( - "\n\tno repository module '{prefix}{module}' in {}", - module_dir.display() - )) - .map(Value::String) - })?; + let Some((_, source)) = BUILTIN_LUACLASS_MODULES + .iter() + .find(|(name, _)| *name == module) + else { + return lua + .create_string(format!( + "\n\tno builtin luaclass module 'luaclass.{module}'" + )) + .map(Value::String); + }; + lua.load(*source) + .set_name(format!("@builtin/luaclass/{module}.lua")) + .into_function() + .map(Value::Function) + })?, + ) +} +fn install_searcher(_lua: &Lua, package: &Table, searcher: Function) -> mlua::Result<()> { + let searchers = package_searchers(package)?; let len = searchers.raw_len(); for index in (2..=len).rev() { let value: Value = searchers.raw_get(index)?; @@ -984,7 +1011,7 @@ return package_version { id = "android/app/com.example.autogen" } } #[test] - fn package_directory_can_load_luaclass_modules() { + fn package_directory_can_load_repository_luaclass_modules() { let temp = tempfile::tempdir().unwrap(); let package_dir = temp.path().join("android/app/com.example.autogen"); fs::create_dir_all(&package_dir).unwrap(); @@ -995,6 +1022,7 @@ return package_version { id = "android/app/com.example.autogen" } return { package_version = function(input) return { + name = "repository module", installed = input.installed, updates = input.updates, } @@ -1037,7 +1065,41 @@ return android.package_version { ) .unwrap(); - assert_eq!(package.name, "Example Autogen"); + assert_eq!(package.name, "repository module"); + assert_eq!(package.installed.len(), 1); + } + + #[test] + fn package_directory_can_load_builtin_luaclass_modules() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local android = require("luaclass.android") +return android.package_version { + name = "builtin module", + installed = { + { kind = "android_package", package_name = "com.example.autogen" }, + }, +} +"#, + ) + .unwrap(); + + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + + assert_eq!(package.name, "builtin module"); assert_eq!(package.installed.len(), 1); } diff --git a/crates/getter-core/src/luaclass/android.lua b/crates/getter-core/src/luaclass/android.lua new file mode 100644 index 0000000..05ee382 --- /dev/null +++ b/crates/getter-core/src/luaclass/android.lua @@ -0,0 +1,12 @@ +local android = {} + +function android.package_version(input) + return package_version { + name = input.name, + installed = input.installed, + source_priority = input.source_priority, + updates = input.updates, + } +end + +return android From 759e7b8e1269b851383d57d2fea93916f2d161f7 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 22:39:47 +0800 Subject: [PATCH 49/71] feat(lua): add HTTP host seam --- crates/getter-core/src/lua.rs | 302 +++++++++++++++++++++++++++++++++- 1 file changed, 296 insertions(+), 6 deletions(-) diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index baad1ce..28857c9 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -10,6 +10,7 @@ use crate::{ }; use mlua::{Function, Lua, Table, Value}; use serde_json::{Map, Number, Value as JsonValue}; +use std::collections::BTreeMap; use std::fs; use std::path::{Path, PathBuf}; @@ -345,17 +346,137 @@ fn install_package_file_helpers(lua: &Lua, package: &PackageDirectory) -> mlua:: })?; let globals = lua.globals(); - let getter_builtin = match globals.get::("getter_builtin")? { - Value::Table(table) => table, + let getter_builtin = getter_builtin_table(lua)?; + getter_builtin.set("read_package_file", read_package_file.clone())?; + globals.set("read_package_file", read_package_file) +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LuaHttpGetRequest { + pub url: String, + pub headers: BTreeMap, + pub cache: bool, +} + +/// Installs the Lua `http_get` host seam for a caller-provided transport. +/// +/// Plain package evaluation does not install this function. Higher-level +/// getter operations/runtime code must deliberately provide a handler and own +/// provider policy, permission checks, Manifest validation, and cache behavior. +pub fn install_http_get_host(lua: &Lua, handler: F) -> mlua::Result<()> +where + F: Fn(LuaHttpGetRequest) -> mlua::Result> + 'static, +{ + let http_get = lua.create_function(move |lua, args: mlua::Variadic| { + let request = lua_http_get_request_from_args(args)?; + let bytes = handler(request)?; + lua.create_string(&bytes) + })?; + let globals = lua.globals(); + let getter_builtin = getter_builtin_table(lua)?; + getter_builtin.set("http_get", http_get.clone())?; + globals.set("http_get", http_get) +} + +fn getter_builtin_table(lua: &Lua) -> mlua::Result
{ + let globals = lua.globals(); + match globals.get::("getter_builtin")? { + Value::Table(table) => Ok(table), Value::Nil => { let table = lua.create_table()?; globals.set("getter_builtin", table.clone())?; - table + Ok(table) } - _ => return Err(mlua::Error::external("getter_builtin must be a table")), + _ => Err(mlua::Error::external("getter_builtin must be a table")), + } +} + +fn lua_http_get_request_from_args(args: mlua::Variadic) -> mlua::Result { + if args.is_empty() || args.len() > 2 { + return Err(mlua::Error::external( + "http_get expects url and optional options table", + )); + } + let url = match &args[0] { + Value::String(value) => value.to_str()?.to_owned(), + _ => return Err(mlua::Error::external("http_get url must be a string")), }; - getter_builtin.set("read_package_file", read_package_file.clone())?; - globals.set("read_package_file", read_package_file) + if url.trim().is_empty() { + return Err(mlua::Error::external("http_get url must not be empty")); + } + let mut request = LuaHttpGetRequest { + url, + headers: BTreeMap::new(), + cache: false, + }; + match args.get(1) { + None | Some(Value::Nil) => {} + Some(Value::Table(options)) => apply_http_get_options(&mut request, options)?, + Some(_) => return Err(mlua::Error::external("http_get options must be a table")), + } + Ok(request) +} + +fn apply_http_get_options(request: &mut LuaHttpGetRequest, options: &Table) -> mlua::Result<()> { + for pair in options.clone().pairs::() { + let (key, _) = pair?; + match key { + Value::String(value) if matches!(value.to_str()?.as_ref(), "cache" | "headers") => {} + Value::String(value) => { + return Err(mlua::Error::external(format!( + "http_get unsupported option '{}'", + value.to_str()? + ))) + } + _ => { + return Err(mlua::Error::external( + "http_get option keys must be strings", + )) + } + } + } + + match options.get::("cache")? { + Value::Nil => {} + Value::Boolean(value) => request.cache = value, + _ => { + return Err(mlua::Error::external( + "http_get options.cache must be a boolean", + )) + } + } + + match options.get::("headers")? { + Value::Nil => {} + Value::Table(headers) => { + for pair in headers.pairs::() { + let (key, value) = pair?; + let key = match key { + Value::String(value) => value.to_str()?.to_owned(), + _ => { + return Err(mlua::Error::external( + "http_get headers keys must be strings", + )) + } + }; + let value = match value { + Value::String(value) => value.to_str()?.to_owned(), + _ => { + return Err(mlua::Error::external( + "http_get headers values must be strings", + )) + } + }; + request.headers.insert(key, value); + } + } + _ => { + return Err(mlua::Error::external( + "http_get options.headers must be a table", + )) + } + } + Ok(()) } fn package_file_relative_path(path: &str) -> mlua::Result { @@ -802,6 +923,21 @@ mod tests { ) } + fn write_simple_android_package(root: &Path, script_source: &str) { + let package_dir = root.join("android/app/com.example.autogen"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "display_name": "Example Autogen", + "android": { "package_name": "com.example.autogen" } +}"#, + ) + .unwrap(); + fs::write(package_dir.join("9999.lua"), script_source).unwrap(); + } + #[test] fn evaluates_json_like_lua_package_table() { let temp = tempfile::tempdir().unwrap(); @@ -1393,6 +1529,160 @@ return github_android.package { ); } + #[test] + fn package_directory_http_get_is_not_installed_by_plain_eval() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +local builtin_http_get = getter_builtin and getter_builtin.http_get +return package_version { name = (http_get == nil and builtin_http_get == nil) and "not installed" or "installed" } +"#, + ); + + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + + assert_eq!(package.name, "not installed"); + } + + #[test] + fn package_directory_http_get_host_parses_request_and_defaults_cache_false() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +local first = http_get("https://example.invalid/a") +local second = http_get("https://example.invalid/b", { + headers = { Accept = "application/json", ["X-Test"] = "yes" }, + cache = true, +}) +return package_version { name = first .. "|" .. second } +"#, + ); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + let requests = std::rc::Rc::new(std::cell::RefCell::new(Vec::new())); + + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("autogen").unwrap(), + package_dir, + &metadata, + script, + { + let requests = std::rc::Rc::clone(&requests); + move |lua| { + install_http_get_host(lua, move |request| { + let body = if request.url.ends_with("/a") { + b"first".to_vec() + } else { + b"second".to_vec() + }; + requests.borrow_mut().push(request); + Ok(body) + }) + } + }, + ) + .unwrap(); + + assert_eq!(package.name, "first|second"); + let requests = requests.borrow(); + assert_eq!(requests.len(), 2); + assert_eq!(requests[0].url, "https://example.invalid/a"); + assert!(!requests[0].cache); + assert!(requests[0].headers.is_empty()); + assert_eq!(requests[1].url, "https://example.invalid/b"); + assert!(requests[1].cache); + assert_eq!( + requests[1].headers.get("Accept").map(String::as_str), + Some("application/json") + ); + assert_eq!( + requests[1].headers.get("X-Test").map(String::as_str), + Some("yes") + ); + } + + #[test] + fn package_directory_http_get_rejects_unsupported_options() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +local extra_ok = pcall(http_get, "https://example.invalid/a", { method = "POST" }) +local header_ok = pcall(http_get, "https://example.invalid/b", { headers = { Accept = 1 } }) +return package_version { name = (not extra_ok and not header_ok) and "rejected" or "accepted" } +"#, + ); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("autogen").unwrap(), + package_dir, + &metadata, + script, + |lua| install_http_get_host(lua, |_| Ok(Vec::new())), + ) + .unwrap(); + + assert_eq!(package.name, "rejected"); + } + + #[test] + fn getter_builtin_exposes_http_get_for_lua_wrappers() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +local upstream_http_get = getter_builtin.http_get +function http_get(url, opts) + opts = opts or {} + opts.headers = opts.headers or {} + opts.headers["X-Rewritten"] = "yes" + return upstream_http_get(url .. "?mirror=1", opts) +end +return package_version { name = http_get("https://example.invalid/file", { cache = true }) } +"#, + ); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + let requests = std::rc::Rc::new(std::cell::RefCell::new(Vec::new())); + + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("autogen").unwrap(), + package_dir, + &metadata, + script, + { + let requests = std::rc::Rc::clone(&requests); + move |lua| { + install_http_get_host(lua, move |request| { + requests.borrow_mut().push(request); + Ok(b"wrapped".to_vec()) + }) + } + }, + ) + .unwrap(); + + assert_eq!(package.name, "wrapped"); + let requests = requests.borrow(); + assert_eq!(requests.len(), 1); + assert_eq!(requests[0].url, "https://example.invalid/file?mirror=1"); + assert!(requests[0].cache); + assert_eq!( + requests[0].headers.get("X-Rewritten").map(String::as_str), + Some("yes") + ); + } + #[test] fn package_directory_can_read_package_local_files() { let temp = tempfile::tempdir().unwrap(); From 939c6de0be97b8f0ace143e53e6d3ff9dc76e30e Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sat, 27 Jun 2026 23:25:31 +0800 Subject: [PATCH 50/71] feat(lua): add dev provider luaclass modules --- crates/getter-core/Cargo.toml | 1 + crates/getter-core/src/lua.rs | 111 +++++++++++++++++- .../src/luaclass/fdroid_android.lua | 32 +++++ .../src/luaclass/github_android_apk.lua | 65 ++++++++++ crates/getter-operations/Cargo.toml | 2 +- .../src/lua_provider_host.rs | 84 +++++++------ 6 files changed, 259 insertions(+), 36 deletions(-) create mode 100644 crates/getter-core/src/luaclass/fdroid_android.lua create mode 100644 crates/getter-core/src/luaclass/github_android_apk.lua diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml index 4b1f9c5..5cb34ef 100644 --- a/crates/getter-core/Cargo.toml +++ b/crates/getter-core/Cargo.toml @@ -6,6 +6,7 @@ edition.workspace = true [features] default = ["lua"] lua = ["dep:mlua"] +provider-luaclass-dev = ["lua"] [dependencies] json_comments = "0.2" diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 28857c9..f00f014 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -14,8 +14,19 @@ use std::collections::BTreeMap; use std::fs; use std::path::{Path, PathBuf}; -const BUILTIN_LUACLASS_MODULES: &[(&str, &str)] = - &[("android", include_str!("luaclass/android.lua"))]; +const BUILTIN_LUACLASS_MODULES: &[(&str, &str)] = &[ + ("android", include_str!("luaclass/android.lua")), + #[cfg(feature = "provider-luaclass-dev")] + ( + "fdroid_android", + include_str!("luaclass/fdroid_android.lua"), + ), + #[cfg(feature = "provider-luaclass-dev")] + ( + "github_android_apk", + include_str!("luaclass/github_android_apk.lua"), + ), +]; #[derive(Debug, thiserror::Error)] pub enum LuaPackageError { @@ -1239,6 +1250,102 @@ return android.package_version { assert_eq!(package.installed.len(), 1); } + #[cfg(not(feature = "provider-luaclass-dev"))] + #[test] + fn provider_luaclass_dev_modules_are_not_default_builtins() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/f-droid/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { package_name = "org.fdroid.fdroid" } +"#, + ) + .unwrap(); + + let err = evaluate_single_package_directory(temp.path(), "official").unwrap_err(); + + let message = err.to_string(); + assert!(matches!(err, LuaPackageError::Runtime { .. })); + assert!(message.contains("no builtin luaclass module 'luaclass.fdroid_android'")); + } + + #[cfg(feature = "provider-luaclass-dev")] + #[test] + fn provider_luaclass_dev_builtin_can_call_injected_host() { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/f-droid/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { package_name = "org.fdroid.fdroid" } +"#, + ) + .unwrap(); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("official").unwrap(), + package_dir, + &metadata, + script, + |lua| { + let getter_dev = lua.create_table()?; + getter_dev.set( + "fdroid_update_candidates", + lua.create_function(|lua, spec: Table| { + let package_name: String = spec.get("package_name")?; + if package_name != "org.fdroid.fdroid" { + return Err(mlua::Error::external("F-Droid package mismatch")); + } + let artifact = lua.create_table()?; + artifact.set("name", "org.fdroid.fdroid.apk")?; + artifact.set("url", "https://f-droid.org/repo/org.fdroid.fdroid.apk")?; + let artifacts = lua.create_table()?; + artifacts.raw_set(1, artifact)?; + let candidate = lua.create_table()?; + candidate.set("version", "1.20.0")?; + candidate.set("source", "fdroid")?; + candidate.set("artifacts", artifacts)?; + let candidates = lua.create_table()?; + candidates.raw_set(1, candidate)?; + Ok(candidates) + })?, + )?; + lua.globals().set("getter_dev", getter_dev) + }, + ) + .unwrap(); + + assert_eq!(package.name, "android/f-droid/app/org.fdroid.fdroid"); + assert_eq!(package.source_priority, vec!["fdroid"]); + assert_eq!(package.updates[0].version, "1.20.0"); + assert_eq!(package.updates[0].source.as_deref(), Some("fdroid")); + } + #[test] fn package_directory_can_use_repository_local_fdroid_luaclass_shape() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-core/src/luaclass/fdroid_android.lua b/crates/getter-core/src/luaclass/fdroid_android.lua new file mode 100644 index 0000000..7a1237b --- /dev/null +++ b/crates/getter-core/src/luaclass/fdroid_android.lua @@ -0,0 +1,32 @@ +local fdroid = {} + +local function require_string(table, field) + local value = table[field] + if type(value) ~= "string" or value == "" then + error("fdroid.package requires string field '" .. field .. "'") + end + return value +end + +local function fdroid_update_candidates() + if type(getter_dev) ~= "table" or type(getter_dev.fdroid_update_candidates) ~= "function" then + error("luaclass.fdroid_android requires operation-installed getter_dev.fdroid_update_candidates") + end + return getter_dev.fdroid_update_candidates +end + +function fdroid.package(spec) + if type(spec) ~= "table" then + error("fdroid.package expects a table") + end + local package_name = require_string(spec, "package_name") + local update_candidates = fdroid_update_candidates() + return package_version { + source_priority = { "fdroid" }, + updates = update_candidates { + package_name = package_name, + }, + } +end + +return fdroid diff --git a/crates/getter-core/src/luaclass/github_android_apk.lua b/crates/getter-core/src/luaclass/github_android_apk.lua new file mode 100644 index 0000000..33d8791 --- /dev/null +++ b/crates/getter-core/src/luaclass/github_android_apk.lua @@ -0,0 +1,65 @@ +local github_android = {} + +local function require_string(table, field) + local value = table[field] + if type(value) ~= "string" or value == "" then + error("github_android.package requires string field '" .. field .. "'") + end + return value +end + +local function optional_table(table, field) + local value = table[field] + if value == nil then + return nil + end + if type(value) ~= "table" then + error("github_android.package field '" .. field .. "' must be a table") + end + return value +end + +local function optional_boolean(table, field) + local value = table[field] + if value == nil then + return nil + end + if type(value) ~= "boolean" then + error("github_android.package field '" .. field .. "' must be a boolean") + end + return value +end + +local function github_release_candidates() + if type(getter_dev) ~= "table" or type(getter_dev.github_release_candidates) ~= "function" then + error("luaclass.github_android_apk requires operation-installed getter_dev.github_release_candidates") + end + return getter_dev.github_release_candidates +end + +function github_android.package(spec) + if type(spec) ~= "table" then + error("github_android.package expects a table") + end + local owner = require_string(spec, "owner") + local repo = require_string(spec, "repo") + local release_candidates = github_release_candidates() + local result = { + name = spec.name, + source_priority = { "github" }, + updates = release_candidates { + owner = owner, + repo = repo, + asset = optional_table(spec, "asset"), + include_prereleases = optional_boolean(spec, "include_prereleases"), + }, + } + if spec.android_package ~= nil then + result.installed = { + { kind = "android_package", package_name = require_string(spec, "android_package") }, + } + end + return package_version(result) +end + +return github_android diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index 81c2e82..e81dab9 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true [features] default = [] lua = ["getter-core/lua", "dep:mlua"] -lua-provider-host-dev = ["lua"] +lua-provider-host-dev = ["lua", "getter-core/provider-luaclass-dev"] [dependencies] json_comments = "0.2" diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index ee16e1a..8e697af 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -584,7 +584,7 @@ mod tests { ]"#; #[test] - fn fdroid_package_eval_uses_repository_luaclass_and_provider_cache() { + fn fdroid_package_eval_uses_dev_builtin_luaclass_and_provider_cache() { let temp = tempfile::tempdir().unwrap(); let data_dir = temp.path(); write_fdroid_package_fixture(data_dir, "org.fdroid.fdroid"); @@ -674,7 +674,7 @@ mod tests { } #[test] - fn github_package_eval_uses_repository_luaclass_and_provider_cache() { + fn github_package_eval_uses_dev_builtin_luaclass_and_provider_cache() { let temp = tempfile::tempdir().unwrap(); let data_dir = temp.path(); write_github_package_fixture(data_dir, "[.]apk$"); @@ -749,21 +749,33 @@ mod tests { ); } - fn write_fdroid_package_fixture(data_dir: &std::path::Path, package_name: &str) { - let repo_root = data_dir.join("repo/official"); - let package_dir = repo_root.join("android/f-droid/app/org.fdroid.fdroid"); - fs::create_dir_all(repo_root.join("luaclass")).unwrap(); - fs::create_dir_all(&package_dir).unwrap(); + #[test] + fn provider_package_eval_prefers_repository_luaclass_over_dev_builtin() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_fdroid_package_fixture(data_dir, "org.fdroid.fdroid"); + let repo_luaclass = data_dir.join("repo/official/luaclass"); + fs::create_dir_all(&repo_luaclass).unwrap(); fs::write( - repo_root.join("luaclass/fdroid_android.lua"), + repo_luaclass.join("fdroid_android.lua"), r#" local fdroid = {} function fdroid.package(spec) + if spec.package_name ~= "org.fdroid.fdroid" then + error("repository override package_name mismatch") + end return package_version { - source_priority = { "fdroid" }, - updates = getter_dev.fdroid_update_candidates { - package_name = spec.package_name, + name = "repository override", + source_priority = { "repository-override" }, + updates = { + { + version = "9.9.9", + source = "repository-override", + artifacts = { + { name = "override.apk", url = "https://example.invalid/override.apk" }, + }, + }, }, } end @@ -772,6 +784,34 @@ return fdroid "#, ) .unwrap(); + + let result = fdroid_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"], json!([])); + assert_eq!(result["package"]["name"], "repository override"); + assert_eq!( + result["package"]["source_priority"], + json!(["repository-override"]) + ); + assert_eq!(result["package"]["updates"][0]["version"], "9.9.9"); + assert_eq!( + result["package"]["updates"][0]["source"], + "repository-override" + ); + } + + fn write_fdroid_package_fixture(data_dir: &std::path::Path, package_name: &str) { + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join("android/f-droid/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); fs::write( package_dir.join("metadata.jsonc"), r#"{ @@ -795,29 +835,7 @@ return fdroid.package { fn write_github_package_fixture(data_dir: &std::path::Path, asset_include: &str) { let repo_root = data_dir.join("repo/official"); let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); - fs::create_dir_all(repo_root.join("luaclass")).unwrap(); fs::create_dir_all(&package_dir).unwrap(); - fs::write( - repo_root.join("luaclass/github_android_apk.lua"), - r#" -local github_android = {} - -function github_android.package(spec) - return package_version { - name = spec.name, - source_priority = { "github" }, - updates = getter_dev.github_release_candidates { - owner = spec.owner, - repo = spec.repo, - asset = spec.asset, - }, - } -end - -return github_android -"#, - ) - .unwrap(); fs::write( package_dir.join("metadata.jsonc"), r#"{ From 03e81cc7b413b424c486f349f86358c2e19d3ba8 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 00:25:32 +0800 Subject: [PATCH 51/71] feat(lua): add HTTP policy harness --- crates/getter-operations/src/lib.rs | 3 + .../getter-operations/src/lua_http_policy.rs | 399 ++++++++++++++++++ 2 files changed, 402 insertions(+) create mode 100644 crates/getter-operations/src/lua_http_policy.rs diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index ae19c2d..50684dc 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -11,6 +11,9 @@ pub mod fdroid_catalog; pub mod github_latest_commit; pub mod github_releases; pub mod legacy_room; +#[cfg(feature = "lua")] +#[doc(hidden)] +pub mod lua_http_policy; #[cfg(feature = "lua-provider-host-dev")] #[doc(hidden)] pub mod lua_provider_host; diff --git a/crates/getter-operations/src/lua_http_policy.rs b/crates/getter-operations/src/lua_http_policy.rs new file mode 100644 index 0000000..cb7116a --- /dev/null +++ b/crates/getter-operations/src/lua_http_policy.rs @@ -0,0 +1,399 @@ +//! Operation-owned Lua HTTP policy harness. +//! +//! This module installs getter-core's `http_get` seam for package evaluation +//! using fixture/in-memory responses. It proves the operation/runtime side owns +//! permission and package Manifest policy while `getter-core` remains transport +//! and storage agnostic. It intentionally does not perform live HTTP, cache +//! persistence/revalidation, auth, hook loading, or public CLI/native exposure. + +use getter_core::lua::{ + evaluate_package_directory_script_with_host_bindings, install_http_get_host, LuaHttpGetRequest, + LuaPackageError, +}; +use getter_core::repository::{ + PackageDirectory, PackageDirectoryMetadata, PackageLuaPermission, PackageVersionScript, + PACKAGE_MANIFEST_FILE, +}; +use getter_core::{RepositoryId, ResolvedPackage}; +use sha2::{Digest, Sha512}; +use std::cell::RefCell; +use std::collections::{BTreeMap, BTreeSet}; +use std::fs; +use std::path::{Path, PathBuf}; +use std::rc::Rc; + +pub const HTTP_RESPONSE_NOT_IN_MANIFEST: &str = "package.http.response_not_in_manifest"; +pub const HTTP_FIXTURE_NOT_FOUND: &str = "package.http.fixture_not_found"; + +#[derive(Debug, thiserror::Error)] +pub enum LuaHttpPolicyError { + #[error("failed to read package Manifest at {path}: {source}")] + ReadManifest { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("invalid package Manifest at {path}: {reason}")] + InvalidManifest { path: PathBuf, reason: String }, + #[error("package evaluation failed: {0}")] + PackageEval(#[from] LuaPackageError), +} + +#[derive(Debug, Clone, PartialEq)] +pub struct LuaHttpPolicyEvaluation { + pub package: ResolvedPackage, + pub http_requests: Vec, +} + +/// Evaluates a package version script with fixture-backed `http_get`. +/// +/// This is an internal harness for operation/runtime policy tests. The caller +/// supplies URL-to-body fixtures instead of a live transport. For scripts that +/// do not declare `allow_free_network`, every returned response body must have +/// a SHA-512 digest listed in the package `Manifest`. Missing `Manifest` is an +/// empty allow-list. Scripts that declare `allow_free_network` bypass Manifest +/// membership, but requests are still captured for diagnostics/validation. +pub fn evaluate_package_directory_script_with_fixture_http( + repository_id: &RepositoryId, + package: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, + responses: BTreeMap>, +) -> Result { + let allow_free_network = metadata + .permissions_for(&script.file_name) + .contains(&PackageLuaPermission::AllowFreeNetwork); + let manifest = if allow_free_network { + None + } else { + Some(PackageManifest::load( + package.path.join(PACKAGE_MANIFEST_FILE), + )?) + }; + let responses = Rc::new(responses); + let http_requests = Rc::new(RefCell::new(Vec::new())); + + let resolved = evaluate_package_directory_script_with_host_bindings( + repository_id, + package, + metadata, + script, + { + let http_requests = Rc::clone(&http_requests); + move |lua| { + install_http_get_host(lua, { + let responses = Rc::clone(&responses); + let http_requests = Rc::clone(&http_requests); + let manifest = manifest.clone(); + move |request| { + let body = responses.get(&request.url).cloned().ok_or_else(|| { + mlua::Error::external(format!( + "{HTTP_FIXTURE_NOT_FOUND}: no fixture response for {}", + request.url + )) + })?; + if let Some(manifest) = manifest.as_ref() { + let digest = sha512_hex(&body); + if !manifest.contains(&digest) { + return Err(mlua::Error::external(format!( + "{HTTP_RESPONSE_NOT_IN_MANIFEST}: response body SHA-512 {digest} for {} is not listed in package Manifest", + request.url + ))); + } + } + http_requests.borrow_mut().push(request); + Ok(body) + } + }) + } + }, + )?; + + let http_requests = http_requests.borrow().clone(); + Ok(LuaHttpPolicyEvaluation { + package: resolved, + http_requests, + }) +} + +#[derive(Debug, Clone)] +struct PackageManifest { + sha512: BTreeSet, +} + +impl PackageManifest { + fn load(path: impl AsRef) -> Result { + let path = path.as_ref(); + let source = match fs::read_to_string(path) { + Ok(source) => source, + Err(source) if source.kind() == std::io::ErrorKind::NotFound => { + return Ok(Self { + sha512: BTreeSet::new(), + }) + } + Err(source) => { + return Err(LuaHttpPolicyError::ReadManifest { + path: path.to_path_buf(), + source, + }) + } + }; + let mut sha512 = BTreeSet::new(); + for (line_index, raw_line) in source.lines().enumerate() { + let line = raw_line.trim(); + if line.is_empty() { + continue; + } + let hash = line + .split_whitespace() + .next() + .expect("non-empty line has token"); + if !is_sha512_hex(hash) { + return Err(LuaHttpPolicyError::InvalidManifest { + path: path.to_path_buf(), + reason: format!( + "line {} must start with a 128-character SHA-512 hex digest", + line_index + 1 + ), + }); + } + sha512.insert(hash.to_ascii_lowercase()); + } + Ok(Self { sha512 }) + } + + fn contains(&self, digest: &str) -> bool { + self.sha512.contains(digest) + } +} + +fn is_sha512_hex(value: &str) -> bool { + value.len() == 128 && value.bytes().all(|byte| byte.is_ascii_hexdigit()) +} + +fn sha512_hex(body: &[u8]) -> String { + let mut hasher = Sha512::new(); + hasher.update(body); + format!("{:x}", hasher.finalize()) +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::repository::{RepositoryPackageDirectoryLayout, LUA_API_SHEBANG_V1}; + use serde_json::json; + use std::fs; + + const URL: &str = "https://example.invalid/body"; + + #[test] + fn non_free_network_script_accepts_manifest_listed_response_body() { + let fixture = package_fixture(PackageFixture { + body: b"manifest body", + manifest_body: Some(b"manifest body"), + allow_free_network: false, + cache: false, + }); + + let result = fixture.evaluate().unwrap(); + + assert_eq!(result.package.name, "manifest body"); + assert_eq!(result.http_requests.len(), 1); + assert!(!result.http_requests[0].cache); + } + + #[test] + fn non_free_network_script_rejects_missing_manifest_response_body() { + let fixture = package_fixture(PackageFixture { + body: b"unlisted body", + manifest_body: None, + allow_free_network: false, + cache: false, + }); + + let err = fixture.evaluate().unwrap_err(); + + let message = err.to_string(); + assert!(matches!(err, LuaHttpPolicyError::PackageEval(_))); + assert!(message.contains(HTTP_RESPONSE_NOT_IN_MANIFEST)); + } + + #[test] + fn non_free_network_script_rejects_empty_manifest_response_body() { + let fixture = package_fixture(PackageFixture { + body: b"unlisted body", + manifest_body: Some(b"unused"), + allow_free_network: false, + cache: false, + }); + fs::write(fixture.package_dir.join(PACKAGE_MANIFEST_FILE), "").unwrap(); + + let err = fixture.evaluate().unwrap_err(); + + let message = err.to_string(); + assert!(matches!(err, LuaHttpPolicyError::PackageEval(_))); + assert!(message.contains(HTTP_RESPONSE_NOT_IN_MANIFEST)); + } + + #[test] + fn non_free_network_script_rejects_mismatched_manifest_response_body() { + let fixture = package_fixture(PackageFixture { + body: b"actual body", + manifest_body: Some(b"different body"), + allow_free_network: false, + cache: false, + }); + + let err = fixture.evaluate().unwrap_err(); + + let message = err.to_string(); + assert!(message.contains(HTTP_RESPONSE_NOT_IN_MANIFEST)); + assert!(message.contains(URL)); + } + + #[test] + fn allow_free_network_script_bypasses_manifest_membership() { + let fixture = package_fixture(PackageFixture { + body: b"free network body", + manifest_body: None, + allow_free_network: true, + cache: false, + }); + + let result = fixture.evaluate().unwrap(); + + assert_eq!(result.package.name, "free network body"); + assert!(result.package.permissions.free_network); + } + + #[test] + fn cache_true_is_forwarded_without_cache_persistence_behavior() { + let fixture = package_fixture(PackageFixture { + body: b"cache body", + manifest_body: Some(b"cache body"), + allow_free_network: false, + cache: true, + }); + + let result = fixture.evaluate().unwrap(); + + assert_eq!(result.package.name, "cache body"); + assert_eq!(result.http_requests.len(), 1); + assert!(result.http_requests[0].cache); + assert_eq!( + result.http_requests[0] + .headers + .get("Accept") + .map(String::as_str), + Some("text/plain") + ); + } + + #[test] + fn invalid_manifest_fails_before_lua_evaluation() { + let fixture = package_fixture(PackageFixture { + body: b"unused", + manifest_body: Some(b"unused"), + allow_free_network: false, + cache: false, + }); + fs::write( + fixture.package_dir.join(PACKAGE_MANIFEST_FILE), + "not-a-sha512 body", + ) + .unwrap(); + + let err = fixture.evaluate().unwrap_err(); + + assert!(matches!(err, LuaHttpPolicyError::InvalidManifest { .. })); + assert!(err.to_string().contains("line 1")); + } + + struct PackageFixture { + body: &'static [u8], + manifest_body: Option<&'static [u8]>, + allow_free_network: bool, + cache: bool, + } + + struct WrittenPackageFixture { + temp: tempfile::TempDir, + package_dir: PathBuf, + responses: BTreeMap>, + } + + impl WrittenPackageFixture { + fn evaluate(&self) -> Result { + let layout = RepositoryPackageDirectoryLayout::load(self.temp.path()).unwrap(); + let package = layout + .package(&"android/app/org.example".parse().unwrap()) + .unwrap(); + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + evaluate_package_directory_script_with_fixture_http( + &RepositoryId::new("official").unwrap(), + package, + &metadata, + script, + self.responses.clone(), + ) + } + } + + fn package_fixture(fixture: PackageFixture) -> WrittenPackageFixture { + let temp = tempfile::tempdir().unwrap(); + let package_dir = temp.path().join("android/app/org.example"); + fs::create_dir_all(&package_dir).unwrap(); + let mut metadata = json!({ + "type": "android:app", + "android": { "package_name": "org.example" } + }); + if fixture.allow_free_network { + metadata["lua"] = json!({ + "9999.lua": { "permission": ["allow_free_network"] } + }); + } + fs::write( + package_dir.join("metadata.jsonc"), + serde_json::to_string_pretty(&metadata).unwrap(), + ) + .unwrap(); + if let Some(manifest_body) = fixture.manifest_body { + fs::write( + package_dir.join(PACKAGE_MANIFEST_FILE), + format!("{} fixture-body\n", sha512_hex(manifest_body)), + ) + .unwrap(); + } + fs::write(package_dir.join("9999.lua"), version_script(fixture.cache)).unwrap(); + let mut responses = BTreeMap::new(); + responses.insert(URL.to_owned(), fixture.body.to_vec()); + WrittenPackageFixture { + temp, + package_dir, + responses, + } + } + + fn version_script(cache: bool) -> String { + format!( + r#"{LUA_API_SHEBANG_V1} +local body = http_get("{URL}", {{ + headers = {{ Accept = "text/plain" }}, + cache = {cache}, +}}) +return package_version {{ + name = body, + updates = {{ + {{ + version = "1.0.0", + artifacts = {{ + {{ name = "fixture", url = "https://example.invalid/artifact" }}, + }}, + }}, + }}, +}} +"# + ) + } +} From 25c510acdeea043095bb44951b63bdce88bd76e5 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 01:01:03 +0800 Subject: [PATCH 52/71] feat(lua): add runtime hook harness --- crates/getter-operations/src/lib.rs | 3 + .../getter-operations/src/lua_http_policy.rs | 206 +++++++++++++++++- .../src/lua_runtime_hooks.rs | 81 +++++++ 3 files changed, 286 insertions(+), 4 deletions(-) create mode 100644 crates/getter-operations/src/lua_runtime_hooks.rs diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 50684dc..f3ddcb5 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -17,6 +17,9 @@ pub mod lua_http_policy; #[cfg(feature = "lua-provider-host-dev")] #[doc(hidden)] pub mod lua_provider_host; +#[cfg(feature = "lua")] +#[doc(hidden)] +pub mod lua_runtime_hooks; pub mod provider_cache; pub mod read_model; pub mod runtime; diff --git a/crates/getter-operations/src/lua_http_policy.rs b/crates/getter-operations/src/lua_http_policy.rs index cb7116a..da254ce 100644 --- a/crates/getter-operations/src/lua_http_policy.rs +++ b/crates/getter-operations/src/lua_http_policy.rs @@ -4,8 +4,9 @@ //! using fixture/in-memory responses. It proves the operation/runtime side owns //! permission and package Manifest policy while `getter-core` remains transport //! and storage agnostic. It intentionally does not perform live HTTP, cache -//! persistence/revalidation, auth, hook loading, or public CLI/native exposure. +//! persistence/revalidation, auth, or public CLI/native exposure. +use crate::lua_runtime_hooks::load_runtime_hooks; use getter_core::lua::{ evaluate_package_directory_script_with_host_bindings, install_http_get_host, LuaHttpGetRequest, LuaPackageError, @@ -43,6 +44,7 @@ pub enum LuaHttpPolicyError { pub struct LuaHttpPolicyEvaluation { pub package: ResolvedPackage, pub http_requests: Vec, + pub runtime_hooks: Vec, } /// Evaluates a package version script with fixture-backed `http_get`. @@ -59,6 +61,26 @@ pub fn evaluate_package_directory_script_with_fixture_http( metadata: &PackageDirectoryMetadata, script: &PackageVersionScript, responses: BTreeMap>, +) -> Result { + evaluate_package_directory_script_with_fixture_http_and_runtime_hooks( + repository_id, + package, + metadata, + script, + responses, + None, + ) +} + +/// Evaluates a package version script with fixture-backed `http_get` and +/// optional runtime hooks from `/rc/hook/*.lua`. +pub fn evaluate_package_directory_script_with_fixture_http_and_runtime_hooks( + repository_id: &RepositoryId, + package: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, + responses: BTreeMap>, + data_dir: Option<&Path>, ) -> Result { let allow_free_network = metadata .permissions_for(&script.file_name) @@ -72,6 +94,8 @@ pub fn evaluate_package_directory_script_with_fixture_http( }; let responses = Rc::new(responses); let http_requests = Rc::new(RefCell::new(Vec::new())); + let runtime_hooks = Rc::new(RefCell::new(Vec::new())); + let data_dir = data_dir.map(Path::to_path_buf); let resolved = evaluate_package_directory_script_with_host_bindings( repository_id, @@ -80,6 +104,7 @@ pub fn evaluate_package_directory_script_with_fixture_http( script, { let http_requests = Rc::clone(&http_requests); + let runtime_hooks = Rc::clone(&runtime_hooks); move |lua| { install_http_get_host(lua, { let responses = Rc::clone(&responses); @@ -104,15 +129,21 @@ pub fn evaluate_package_directory_script_with_fixture_http( http_requests.borrow_mut().push(request); Ok(body) } - }) + })?; + if let Some(data_dir) = data_dir.as_deref() { + *runtime_hooks.borrow_mut() = load_runtime_hooks(lua, data_dir)?; + } + Ok(()) } }, )?; let http_requests = http_requests.borrow().clone(); + let runtime_hooks = runtime_hooks.borrow().clone(); Ok(LuaHttpPolicyEvaluation { package: resolved, http_requests, + runtime_hooks, }) } @@ -309,6 +340,148 @@ mod tests { assert!(err.to_string().contains("line 1")); } + #[test] + fn missing_runtime_hook_directory_is_noop() { + let fixture = package_fixture(PackageFixture { + body: b"manifest body", + manifest_body: Some(b"manifest body"), + allow_free_network: false, + cache: false, + }); + + let result = fixture.evaluate_with_hooks().unwrap(); + + assert_eq!(result.package.name, "manifest body"); + assert!(result.runtime_hooks.is_empty()); + } + + #[test] + fn dot_prefixed_runtime_hook_is_ignored() { + let fixture = package_fixture(PackageFixture { + body: b"manifest body", + manifest_body: Some(b"manifest body"), + allow_free_network: false, + cache: false, + }); + fixture.write_hook(".10-fail.lua", r#"error("ignored hook should not run")"#); + + let result = fixture.evaluate_with_hooks().unwrap(); + + assert_eq!(result.package.name, "manifest body"); + assert!(result.runtime_hooks.is_empty()); + } + + #[test] + fn runtime_hooks_load_in_deterministic_filename_order() { + let fixture = package_fixture(PackageFixture { + body: b"manifest body", + manifest_body: Some(b"manifest body"), + allow_free_network: false, + cache: false, + }); + fixture.write_hook("20-second.lua", r#"hook_order = (hook_order or "") .. "b""#); + fixture.write_hook("10-first.lua", r#"hook_order = (hook_order or "") .. "a""#); + + let result = fixture.evaluate_with_hooks().unwrap(); + + assert_eq!(result.package.name, "manifest body"); + let loaded = result + .runtime_hooks + .iter() + .map(|path| path.file_name().unwrap().to_str().unwrap().to_owned()) + .collect::>(); + assert_eq!(loaded, vec!["10-first.lua", "20-second.lua"]); + assert_eq!( + result.http_requests[0] + .headers + .get("X-Hook-Order") + .map(String::as_str), + Some("ab") + ); + } + + #[test] + fn runtime_hook_can_wrap_http_get_and_rewrite_request() { + let mut fixture = package_fixture(PackageFixture { + body: b"unused original body", + manifest_body: Some(b"mirror body"), + allow_free_network: false, + cache: false, + }); + fixture.add_response("https://mirror.invalid/body", b"mirror body"); + fixture.write_hook( + "10-http-rewrite.lua", + r#" +local original_http_get = getter_builtin.http_get +function http_get(url, options) + options = options or {} + options.headers = options.headers or {} + options.headers["X-Original-Url"] = url + options.cache = true + return original_http_get("https://mirror.invalid/body", options) +end +"#, + ); + + let result = fixture.evaluate_with_hooks().unwrap(); + + assert_eq!(result.package.name, "mirror body"); + assert_eq!(result.http_requests.len(), 1); + assert_eq!(result.http_requests[0].url, "https://mirror.invalid/body"); + assert!(result.http_requests[0].cache); + assert_eq!( + result.http_requests[0] + .headers + .get("X-Original-Url") + .map(String::as_str), + Some(URL) + ); + } + + #[test] + fn runtime_hook_rewrite_still_enforces_manifest_on_returned_body() { + let mut fixture = package_fixture(PackageFixture { + body: b"unused original body", + manifest_body: Some(b"unused original body"), + allow_free_network: false, + cache: false, + }); + fixture.add_response("https://mirror.invalid/body", b"unlisted mirror body"); + fixture.write_hook( + "10-http-rewrite.lua", + r#" +local original_http_get = getter_builtin.http_get +function http_get(url, options) + return original_http_get("https://mirror.invalid/body", options) +end +"#, + ); + + let err = fixture.evaluate_with_hooks().unwrap_err(); + + let message = err.to_string(); + assert!(message.contains(HTTP_RESPONSE_NOT_IN_MANIFEST)); + assert!(message.contains("https://mirror.invalid/body")); + } + + #[test] + fn runtime_hook_failure_fails_closed() { + let fixture = package_fixture(PackageFixture { + body: b"manifest body", + manifest_body: Some(b"manifest body"), + allow_free_network: false, + cache: false, + }); + fixture.write_hook("10-fail.lua", r#"error("hook failed closed")"#); + + let err = fixture.evaluate_with_hooks().unwrap_err(); + + let message = err.to_string(); + assert!(matches!(err, LuaHttpPolicyError::PackageEval(_))); + assert!(message.contains("failed to execute runtime hook")); + assert!(message.contains("hook failed closed")); + } + struct PackageFixture { body: &'static [u8], manifest_body: Option<&'static [u8]>, @@ -324,20 +497,42 @@ mod tests { impl WrittenPackageFixture { fn evaluate(&self) -> Result { + self.evaluate_with_data_dir(None) + } + + fn evaluate_with_hooks(&self) -> Result { + self.evaluate_with_data_dir(Some(self.temp.path())) + } + + fn evaluate_with_data_dir( + &self, + data_dir: Option<&Path>, + ) -> Result { let layout = RepositoryPackageDirectoryLayout::load(self.temp.path()).unwrap(); let package = layout .package(&"android/app/org.example".parse().unwrap()) .unwrap(); let metadata = layout.package_metadata(package).unwrap(); let script = layout.unambiguous_version_script(package).unwrap(); - evaluate_package_directory_script_with_fixture_http( + evaluate_package_directory_script_with_fixture_http_and_runtime_hooks( &RepositoryId::new("official").unwrap(), package, &metadata, script, self.responses.clone(), + data_dir, ) } + + fn write_hook(&self, file_name: &str, source: &str) { + let hook_dir = self.temp.path().join("rc/hook"); + fs::create_dir_all(&hook_dir).unwrap(); + fs::write(hook_dir.join(file_name), source).unwrap(); + } + + fn add_response(&mut self, url: &str, body: &'static [u8]) { + self.responses.insert(url.to_owned(), body.to_vec()); + } } fn package_fixture(fixture: PackageFixture) -> WrittenPackageFixture { @@ -379,7 +574,10 @@ mod tests { format!( r#"{LUA_API_SHEBANG_V1} local body = http_get("{URL}", {{ - headers = {{ Accept = "text/plain" }}, + headers = {{ + Accept = "text/plain", + ["X-Hook-Order"] = hook_order or "", + }}, cache = {cache}, }}) return package_version {{ diff --git a/crates/getter-operations/src/lua_runtime_hooks.rs b/crates/getter-operations/src/lua_runtime_hooks.rs new file mode 100644 index 0000000..e73ed86 --- /dev/null +++ b/crates/getter-operations/src/lua_runtime_hooks.rs @@ -0,0 +1,81 @@ +//! Operation-owned runtime/local Lua hook loading. +//! +//! This module is an internal harness for ADR-0012 `rc/hook/*.lua` runtime +//! policy. It only loads local hook source into an already prepared Lua +//! environment; callers still decide which host functions exist and which +//! operation policy applies. It intentionally does not expose hooks through +//! CLI/native/Flutter surfaces or make hook policy part of `getter-core`. + +use mlua::Lua; +use std::fs; +use std::path::{Path, PathBuf}; + +const RUNTIME_HOOK_DIR: &str = "rc/hook"; + +/// Loads enabled runtime hooks from `/rc/hook/*.lua`. +/// +/// Hooks are discovered from the filesystem only, dot-prefixed Lua basenames +/// are ignored, and the remaining hooks are loaded in deterministic path order. +/// Any read or runtime error fails closed by returning an error to the caller. +pub fn load_runtime_hooks(lua: &Lua, data_dir: &Path) -> mlua::Result> { + let hook_dir = data_dir.join(RUNTIME_HOOK_DIR); + let entries = match fs::read_dir(&hook_dir) { + Ok(entries) => entries, + Err(source) if source.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()), + Err(source) => { + return Err(mlua::Error::external(format!( + "failed to read runtime hook directory {}: {source}", + hook_dir.display() + ))) + } + }; + + let mut hooks = Vec::new(); + for entry in entries { + let entry = entry.map_err(|source| { + mlua::Error::external(format!( + "failed to read runtime hook directory entry in {}: {source}", + hook_dir.display() + )) + })?; + let path = entry.path(); + if !is_enabled_lua_hook(&path) { + continue; + } + hooks.push(path); + } + hooks.sort(); + + for hook in &hooks { + let source = fs::read_to_string(hook).map_err(|source| { + mlua::Error::external(format!( + "failed to read runtime hook {}: {source}", + hook.display() + )) + })?; + lua.load(&source) + .set_name(hook.to_string_lossy().as_ref()) + .exec() + .map_err(|source| { + mlua::Error::external(format!( + "failed to execute runtime hook {}: {source}", + hook.display() + )) + })?; + } + + Ok(hooks) +} + +fn is_enabled_lua_hook(path: &Path) -> bool { + if !path.is_file() { + return false; + } + if path.extension().and_then(|extension| extension.to_str()) != Some("lua") { + return false; + } + let Some(file_name) = path.file_name().and_then(|file_name| file_name.to_str()) else { + return false; + }; + !file_name.starts_with('.') +} From bb5b7032554b76d01d164772e4490698ee315d44 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 10:47:34 +0800 Subject: [PATCH 53/71] feat(lua): add stable provider host harness --- .../src/lua_provider_host.rs | 1214 ++++++++++++++--- 1 file changed, 1056 insertions(+), 158 deletions(-) diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 8e697af..15cb3b1 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -3,8 +3,8 @@ //! This module is an implementation bridge toward ADR-0012 provider-backed Lua //! modules. It exercises higher-level getter operations installing host //! functions for package evaluation without making `getter-core` depend on -//! provider cache/storage crates and without defining the final stable Lua -//! provider API. +//! provider cache/storage crates. The stable `getter.provider.*` harness here +//! is still fixture-backed, dev-feature-gated, and hidden from product APIs. use crate::fdroid_catalog::{ read_or_refresh_fdroid_catalog, FdroidCatalogOperationError, FdroidEndpointConfig, @@ -14,10 +14,14 @@ use crate::github_releases::{ read_or_refresh_github_releases, GithubReleaseConfig, GithubReleaseOperationError, DEFAULT_GITHUB_API_BASE_URL, GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, }; +use crate::lua_runtime_hooks::load_runtime_hooks; use crate::provider_cache::{ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheSource}; use getter_core::lua::{evaluate_package_directory_script_with_host_bindings, LuaPackageError}; -use getter_core::repository::{RepositoryLoadError, RepositoryPackageDirectoryLayout}; -use getter_core::{PackageId, RepositoryId, UpdateArtifact, UpdateCandidate}; +use getter_core::repository::{ + PackageDirectory, PackageDirectoryMetadata, PackageLuaPermission, PackageVersionScript, + RepositoryLoadError, RepositoryPackageDirectoryLayout, PACKAGE_MANIFEST_FILE, +}; +use getter_core::{PackageId, RepositoryId, ResolvedPackage}; use getter_providers::{ github_release_update_candidates, FdroidEndpoint, GithubAssetFilter, GithubProviderError, GithubRelease, GithubReleaseCandidateOptions, @@ -25,8 +29,10 @@ use getter_providers::{ use getter_storage::{CacheDb, MainDb, StorageError, StoredRepository}; use mlua::{Lua, Table, Value as LuaValue}; use serde::Deserialize; -use serde_json::{json, Value}; +use serde_json::{json, Map, Value}; +use sha2::{Digest, Sha512}; use std::cell::RefCell; +use std::collections::BTreeSet; use std::path::{Path, PathBuf}; use std::rc::Rc; @@ -48,9 +54,19 @@ pub enum LuaProviderHostOperationError { GithubProvider(#[from] GithubProviderError), #[error("Lua provider-host response serialization failed: {0}")] Serialization(#[from] serde_json::Error), + #[error("failed to read package Manifest at {path}: {source}")] + ReadManifest { + path: PathBuf, + #[source] + source: std::io::Error, + }, + #[error("invalid package Manifest at {path}: {reason}")] + InvalidManifest { path: PathBuf, reason: String }, } const FDROID_PACKAGE_NOT_FOUND: &str = "provider.fdroid.package_not_found"; +const PROVIDER_RESPONSE_NOT_IN_MANIFEST: &str = "package.provider.response_not_in_manifest"; +const PROVIDER_CACHE_PROVENANCE_MISSING: &str = "package.provider.cache_provenance_missing"; /// Evaluates a package with a fixture-backed F-Droid catalog host binding. /// @@ -142,6 +158,15 @@ struct FdroidDevHostConfig { provider_calls: Rc>>, } +#[derive(Clone)] +struct FdroidProviderHostConfig { + cache_db_path: PathBuf, + endpoint: FdroidEndpointConfig, + mode: ProviderCacheMode, + index_xml: Option, + manifest: Option, +} + /// Evaluates a package with a fixture-backed GitHub release host binding. /// /// This is a development/provider-host tracer, not the stable package-eval or @@ -206,6 +231,153 @@ pub fn github_package_eval_json( })) } +/// Evaluates a package with the fixture-backed stable `getter.provider.*` host. +/// +/// This is an internal Slice 1 harness for ADR-0012 provider host API v1. It +/// installs the stable namespace and runtime hooks, but still uses fixture data +/// and remains hidden from CLI/native/Flutter product APIs. +pub fn stable_provider_package_eval_json( + data_dir: &Path, + request_json: &str, +) -> Result { + let request: StableProviderPackageEvalRequest = serde_json::from_str(request_json) + .map_err(|source| LuaProviderHostOperationError::InvalidRequest(source.to_string()))?; + let mode = provider_cache_mode(request.mode.as_deref())?; + let endpoint = FdroidEndpointConfig { + endpoint_id: request + .fdroid_endpoint_id + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_ID.to_owned()), + endpoint_url: request + .fdroid_endpoint_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), + }; + let github_api_base_url = request + .github_api_base_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()); + let main_db = MainDb::open(data_dir.join("main.db"))?; + let repository = find_repository(&main_db, &request.repository_id)?; + let repository_path = repo_path(&repository)?; + let layout = RepositoryPackageDirectoryLayout::load(&repository_path)?; + let package_directory = layout.package(&request.package_id).ok_or_else(|| { + LuaProviderHostOperationError::InvalidRequest(format!( + "package '{}' was not found in repository '{}'", + request.package_id, request.repository_id + )) + })?; + let metadata = layout.package_metadata(package_directory)?; + let script = layout.unambiguous_version_script(package_directory)?; + let provider_calls = Rc::new(RefCell::new(Vec::new())); + let runtime_hooks = Rc::new(RefCell::new(Vec::new())); + let fdroid_index_xml = request.fdroid_index_xml; + let github_releases_json = request.github_releases_json; + let default_include_prereleases = request.github_include_prereleases; + let manifest = if metadata + .permissions_for(&script.file_name) + .contains(&PackageLuaPermission::AllowFreeNetwork) + { + None + } else { + Some(PackageManifest::load( + package_directory.path.join(PACKAGE_MANIFEST_FILE), + )?) + }; + // Parsed provider cache currently has no source-body digest provenance. + // Non-free scripts therefore refetch/revalidate fixture bodies instead of + // accepting cache hits that cannot prove Manifest compatibility. + let provider_mode = if manifest.is_some() { + ProviderCacheMode::ForceRefresh + } else { + mode + }; + let package = evaluate_with_stable_provider_host( + data_dir, + &repository.id, + package_directory, + &metadata, + script, + StableProviderHostEvalConfig { + fdroid: FdroidProviderHostConfig { + cache_db_path: data_dir.join("cache.db"), + endpoint, + mode: provider_mode, + index_xml: fdroid_index_xml, + manifest: manifest.clone(), + }, + github: GithubProviderHostConfig { + cache_db_path: data_dir.join("cache.db"), + api_base_url: github_api_base_url, + mode: provider_mode, + releases_json: github_releases_json, + default_include_prereleases, + manifest, + }, + provider_calls: Rc::clone(&provider_calls), + runtime_hooks: Rc::clone(&runtime_hooks), + }, + )?; + let package = serde_json::to_value(package)?; + + Ok(json!({ + "operation": "provider.package_eval.fixture", + "package": package, + "provider_calls": provider_calls.borrow().clone(), + "runtime_hooks": runtime_hooks.borrow().clone(), + })) +} + +#[derive(Debug, Deserialize)] +struct StableProviderPackageEvalRequest { + repository_id: RepositoryId, + package_id: PackageId, + #[serde(default)] + mode: Option, + #[serde(default)] + fdroid_endpoint_id: Option, + #[serde(default)] + fdroid_endpoint_url: Option, + #[serde(default)] + fdroid_index_xml: Option, + #[serde(default)] + github_api_base_url: Option, + #[serde(default)] + github_releases_json: Option, + #[serde(default)] + github_include_prereleases: bool, +} + +struct StableProviderHostEvalConfig { + fdroid: FdroidProviderHostConfig, + github: GithubProviderHostConfig, + provider_calls: Rc>>, + runtime_hooks: Rc>>, +} + +fn evaluate_with_stable_provider_host( + data_dir: &Path, + repository_id: &RepositoryId, + package: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, + config: StableProviderHostEvalConfig, +) -> Result { + evaluate_package_directory_script_with_host_bindings( + repository_id, + package, + metadata, + script, + move |lua| { + install_stable_provider_host(lua, config.fdroid, config.github)?; + *config.runtime_hooks.borrow_mut() = load_runtime_hooks(lua, data_dir)?; + install_provider_call_trace(lua, Rc::clone(&config.provider_calls))?; + Ok(()) + }, + ) + .map_err(Into::into) +} + #[derive(Debug, Deserialize)] struct GithubPackageEvalRequest { repository_id: RepositoryId, @@ -229,58 +401,37 @@ struct GithubDevHostConfig { provider_calls: Rc>>, } +#[derive(Clone)] +struct GithubProviderHostConfig { + cache_db_path: PathBuf, + api_base_url: String, + mode: ProviderCacheMode, + releases_json: Option, + default_include_prereleases: bool, + manifest: Option, +} + fn install_fdroid_dev_host(lua: &Lua, config: FdroidDevHostConfig) -> mlua::Result<()> { let host = lua.create_table()?; host.set( "fdroid_update_candidates", lua.create_function(move |lua, spec: Table| { - let request = FdroidUpdateHostRequest::from_lua_table(&spec)?; - let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; - let endpoint = config.endpoint.clone(); - let refresh_fixture = config.index_xml.clone(); - let result = read_or_refresh_fdroid_catalog(&db, endpoint, config.mode, || { - refresh_fixture.ok_or_else(|| { - "fixture-backed F-Droid catalog host requires index_xml".to_owned() - }) - }) - .map_err(mlua::Error::external)?; - let mut diagnostics = result - .diagnostics - .iter() - .map(provider_diagnostic_json) - .collect::>(); - let candidates = result - .app(&request.package_name) - .map(|app| { - let endpoint = FdroidEndpoint { - name: result.catalog.endpoint.name.clone(), - url: Some(result.endpoint.endpoint_url.clone()), - timestamp: result.catalog.endpoint.timestamp.clone(), - }; - app.update_candidates(&endpoint) - }) - .unwrap_or_else(|| { - diagnostics.push(json!({ - "code": FDROID_PACKAGE_NOT_FOUND, - "message": format!("F-Droid package '{}' was not found in endpoint '{}'", request.package_name, result.endpoint.endpoint_id), - "provider": FDROID_PROVIDER_ID, - "endpoint_id": result.endpoint.endpoint_id, - "package_name": request.package_name, - "cache_key": result.cache_key, - })); - Vec::new() - }); - config.provider_calls.borrow_mut().push(json!({ - "provider": FDROID_PROVIDER_ID, - "request": "catalog", - "endpoint_id": result.endpoint.endpoint_id, - "endpoint_url": result.endpoint.endpoint_url, - "package_name": request.package_name, - "cache_key": result.cache_key, - "source": provider_source_json(result.source), - "diagnostics": diagnostics, - })); - update_candidates_to_lua(lua, &candidates) + let result = fdroid_update_candidates_envelope( + lua, + spec, + FdroidProviderHostConfig { + cache_db_path: config.cache_db_path.clone(), + endpoint: config.endpoint.clone(), + mode: config.mode, + index_xml: config.index_xml.clone(), + manifest: None, + }, + )?; + config + .provider_calls + .borrow_mut() + .push(provider_call_from_envelope(&result)?); + envelope_candidates(result) })?, )?; lua.globals().set("getter_dev", host) @@ -297,9 +448,7 @@ impl FdroidUpdateHostRequest { package_name: package_name .filter(|value| !value.trim().is_empty()) .ok_or_else(|| { - mlua::Error::external( - "getter_dev.fdroid_update_candidates missing package_name", - ) + mlua::Error::external("F-Droid provider host missing package_name") })?, }) } @@ -310,60 +459,105 @@ fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Resu host.set( "github_release_candidates", lua.create_function(move |lua, spec: Table| { - let request = GithubReleaseHostRequest::from_lua_table(&spec)?; - let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; - let provider_config = GithubReleaseConfig { - api_base_url: config.api_base_url.clone(), - owner: request.owner, - repo: request.repo, - }; - let refresh_fixture = config.releases_json.clone(); - let result = read_or_refresh_github_releases(&db, provider_config, config.mode, || { - refresh_fixture.ok_or_else(|| { - "fixture-backed GitHub release host requires releases_json".to_owned() - }) - }) - .map_err(mlua::Error::external)?; - let options = GithubReleaseCandidateOptions { - include_prereleases: request - .include_prereleases - .unwrap_or(config.default_include_prereleases), - asset_filter: request.asset_filter, - }; - let candidates = github_release_update_candidates(&result.releases, &options) - .map_err(mlua::Error::external)?; - let mut diagnostics = result - .diagnostics - .iter() - .map(provider_diagnostic_json) - .collect::>(); - if candidates.is_empty() - && github_eligible_release_count(&result.releases, &options) > 0 - { - diagnostics.push(json!({ - "code": GITHUB_ASSET_NOT_FOUND, - "message": "no GitHub release assets matched the requested filters", - "provider": GITHUB_PROVIDER_ID, - "owner": result.config.owner, - "repo": result.config.repo, - "cache_key": result.cache_key, - })); - } - config.provider_calls.borrow_mut().push(json!({ - "provider": GITHUB_PROVIDER_ID, - "request": "releases", - "owner": result.config.owner, - "repo": result.config.repo, - "cache_key": result.cache_key, - "source": provider_source_json(result.source), - "diagnostics": diagnostics, - })); - update_candidates_to_lua(lua, &candidates) + let result = github_release_candidates_envelope( + lua, + spec, + GithubProviderHostConfig { + cache_db_path: config.cache_db_path.clone(), + api_base_url: config.api_base_url.clone(), + mode: config.mode, + releases_json: config.releases_json.clone(), + default_include_prereleases: config.default_include_prereleases, + manifest: None, + }, + )?; + config + .provider_calls + .borrow_mut() + .push(provider_call_from_envelope(&result)?); + envelope_candidates(result) })?, )?; lua.globals().set("getter_dev", host) } +fn install_stable_provider_host( + lua: &Lua, + fdroid: FdroidProviderHostConfig, + github: GithubProviderHostConfig, +) -> mlua::Result<()> { + let fdroid_update_candidates = lua.create_function(move |lua, spec: Table| { + fdroid_update_candidates_envelope(lua, spec, fdroid.clone()) + })?; + let github_release_candidates = lua.create_function(move |lua, spec: Table| { + github_release_candidates_envelope(lua, spec, github.clone()) + })?; + + let globals = lua.globals(); + let getter = nested_table(lua, globals.clone(), "getter")?; + let provider = nested_table(lua, getter, "provider")?; + let fdroid_table = nested_table(lua, provider.clone(), "fdroid")?; + fdroid_table.set("update_candidates", fdroid_update_candidates.clone())?; + let github_table = nested_table(lua, provider, "github")?; + github_table.set("release_candidates", github_release_candidates.clone())?; + + let getter_builtin = nested_table(lua, globals, "getter_builtin")?; + let builtin_provider = nested_table(lua, getter_builtin, "provider")?; + let builtin_fdroid_table = nested_table(lua, builtin_provider.clone(), "fdroid")?; + builtin_fdroid_table.set("update_candidates", fdroid_update_candidates)?; + let builtin_github_table = nested_table(lua, builtin_provider, "github")?; + builtin_github_table.set("release_candidates", github_release_candidates) +} + +fn nested_table(lua: &Lua, parent: Table, key: &str) -> mlua::Result
{ + match parent.get::(key)? { + LuaValue::Table(table) => Ok(table), + LuaValue::Nil => { + let table = lua.create_table()?; + parent.set(key, table.clone())?; + Ok(table) + } + _ => Err(mlua::Error::external(format!("{key} must be a table"))), + } +} + +fn install_provider_call_trace( + lua: &Lua, + provider_calls: Rc>>, +) -> mlua::Result<()> { + let globals = lua.globals(); + let getter: Table = globals.get("getter")?; + let provider: Table = getter.get("provider")?; + let fdroid_table: Table = provider.get("fdroid")?; + let original_fdroid: mlua::Function = fdroid_table.get("update_candidates")?; + fdroid_table.set( + "update_candidates", + lua.create_function({ + let provider_calls = Rc::clone(&provider_calls); + move |_, spec: Table| { + let result: Table = original_fdroid.call(spec)?; + provider_calls + .borrow_mut() + .push(provider_call_from_envelope(&result)?); + Ok(result) + } + })?, + )?; + + let github_table: Table = provider.get("github")?; + let original_github: mlua::Function = github_table.get("release_candidates")?; + github_table.set( + "release_candidates", + lua.create_function(move |_, spec: Table| { + let result: Table = original_github.call(spec)?; + provider_calls + .borrow_mut() + .push(provider_call_from_envelope(&result)?); + Ok(result) + })?, + ) +} + struct GithubReleaseHostRequest { owner: String, repo: String, @@ -382,6 +576,353 @@ impl GithubReleaseHostRequest { } } +fn fdroid_update_candidates_envelope( + lua: &Lua, + spec: Table, + config: FdroidProviderHostConfig, +) -> mlua::Result
{ + let request = FdroidUpdateHostRequest::from_lua_table(&spec)?; + let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; + let endpoint = config.endpoint; + let refresh_fixture = config.index_xml; + let manifest = config.manifest.clone(); + let result = read_or_refresh_fdroid_catalog(&db, endpoint, config.mode, || { + let xml = refresh_fixture + .ok_or_else(|| "fixture-backed F-Droid catalog host requires index_xml".to_owned())?; + require_manifest_body(&manifest, FDROID_PROVIDER_ID, &xml) + .map_err(|source| source.to_string())?; + Ok(xml) + }) + .map_err(mlua::Error::external)?; + reject_unproven_manifest_cache(&manifest, FDROID_PROVIDER_ID, result.source)?; + let mut diagnostics = result + .diagnostics + .iter() + .map(provider_diagnostic_json) + .collect::>(); + let candidates = result + .app(&request.package_name) + .map(|app| { + let endpoint = FdroidEndpoint { + name: result.catalog.endpoint.name.clone(), + url: Some(result.endpoint.endpoint_url.clone()), + timestamp: result.catalog.endpoint.timestamp.clone(), + }; + app.update_candidates(&endpoint) + }) + .unwrap_or_else(|| { + diagnostics.push(json!({ + "code": FDROID_PACKAGE_NOT_FOUND, + "message": format!("F-Droid package '{}' was not found in endpoint '{}'", request.package_name, result.endpoint.endpoint_id), + "provider": FDROID_PROVIDER_ID, + "endpoint_id": result.endpoint.endpoint_id, + "package_name": request.package_name, + "cache_key": result.cache_key, + })); + Vec::new() + }); + let envelope = provider_envelope_to_lua( + lua, + json!({ + "provider": FDROID_PROVIDER_ID, + "request": "catalog", + "endpoint_id": result.endpoint.endpoint_id, + "endpoint_url": result.endpoint.endpoint_url, + "package_name": request.package_name, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "candidates": candidates, + "diagnostics": diagnostics, + }), + )?; + Ok(envelope) +} + +fn github_release_candidates_envelope( + lua: &Lua, + spec: Table, + config: GithubProviderHostConfig, +) -> mlua::Result
{ + let request = GithubReleaseHostRequest::from_lua_table(&spec)?; + let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; + let provider_config = GithubReleaseConfig { + api_base_url: config.api_base_url, + owner: request.owner, + repo: request.repo, + }; + let refresh_fixture = config.releases_json; + let manifest = config.manifest.clone(); + let result = read_or_refresh_github_releases(&db, provider_config, config.mode, || { + let releases = refresh_fixture.ok_or_else(|| { + "fixture-backed GitHub release host requires releases_json".to_owned() + })?; + require_manifest_body(&manifest, GITHUB_PROVIDER_ID, &releases) + .map_err(|source| source.to_string())?; + Ok(releases) + }) + .map_err(mlua::Error::external)?; + reject_unproven_manifest_cache(&manifest, GITHUB_PROVIDER_ID, result.source)?; + let options = GithubReleaseCandidateOptions { + include_prereleases: request + .include_prereleases + .unwrap_or(config.default_include_prereleases), + asset_filter: request.asset_filter, + }; + let candidates = github_release_update_candidates(&result.releases, &options) + .map_err(mlua::Error::external)?; + let mut diagnostics = result + .diagnostics + .iter() + .map(provider_diagnostic_json) + .collect::>(); + if candidates.is_empty() && github_eligible_release_count(&result.releases, &options) > 0 { + diagnostics.push(json!({ + "code": GITHUB_ASSET_NOT_FOUND, + "message": "no GitHub release assets matched the requested filters", + "provider": GITHUB_PROVIDER_ID, + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + })); + } + let envelope = provider_envelope_to_lua( + lua, + json!({ + "provider": GITHUB_PROVIDER_ID, + "request": "releases", + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "candidates": candidates, + "diagnostics": diagnostics, + }), + )?; + Ok(envelope) +} + +fn provider_envelope_to_lua(lua: &Lua, mut value: Value) -> mlua::Result
{ + if value + .get("candidates") + .and_then(Value::as_array) + .is_some_and(Vec::is_empty) + { + if let Some(object) = value.as_object_mut() { + object.remove("candidates"); + } + } + json_object_to_lua(lua, value) +} + +fn provider_call_from_envelope(envelope: &Table) -> mlua::Result { + let mut object = Map::new(); + for key in [ + "provider", + "request", + "endpoint_id", + "endpoint_url", + "package_name", + "owner", + "repo", + "cache_key", + "source", + "diagnostics", + ] { + let value: LuaValue = envelope.get(key)?; + if !matches!(value, LuaValue::Nil) { + object.insert(key.to_owned(), lua_value_to_json(value)?); + } + } + Ok(Value::Object(object)) +} + +fn envelope_candidates(envelope: Table) -> mlua::Result { + envelope.get("candidates") +} + +fn json_object_to_lua(lua: &Lua, value: Value) -> mlua::Result
{ + match json_value_to_lua(lua, value)? { + LuaValue::Table(table) => Ok(table), + _ => Err(mlua::Error::external( + "provider host envelope must be an object", + )), + } +} + +fn json_value_to_lua(lua: &Lua, value: Value) -> mlua::Result { + match value { + Value::Null => Ok(LuaValue::Nil), + Value::Bool(value) => Ok(LuaValue::Boolean(value)), + Value::Number(number) => { + if let Some(value) = number.as_i64() { + return Ok(LuaValue::Integer(value)); + } + if let Some(value) = number.as_u64().and_then(|value| i64::try_from(value).ok()) { + return Ok(LuaValue::Integer(value)); + } + Ok(LuaValue::Number(number.as_f64().ok_or_else(|| { + mlua::Error::external(format!( + "provider host number {number} cannot be represented" + )) + })?)) + } + Value::String(value) => lua.create_string(value).map(LuaValue::String), + Value::Array(values) => { + let table = lua.create_table()?; + for (index, value) in values.into_iter().enumerate() { + table.raw_set(index + 1, json_value_to_lua(lua, value)?)?; + } + Ok(LuaValue::Table(table)) + } + Value::Object(values) => { + let table = lua.create_table()?; + for (key, value) in values { + if !value.is_null() { + table.set(key, json_value_to_lua(lua, value)?)?; + } + } + Ok(LuaValue::Table(table)) + } + } +} + +fn lua_value_to_json(value: LuaValue) -> mlua::Result { + match value { + LuaValue::Nil => Ok(Value::Null), + LuaValue::Boolean(value) => Ok(Value::Bool(value)), + LuaValue::Integer(value) => Ok(json!(value)), + LuaValue::Number(value) => Ok(json!(value)), + LuaValue::String(value) => Ok(Value::String(value.to_str()?.to_owned())), + LuaValue::Table(table) => lua_table_to_json_value(table), + _ => Err(mlua::Error::external( + "provider host trace contains unsupported Lua value", + )), + } +} + +fn lua_table_to_json_value(table: Table) -> mlua::Result { + let mut len = 0; + for pair in table.clone().pairs::() { + let (key, _) = pair?; + if let LuaValue::Integer(index) = key { + if index > 0 { + len = len.max(index as usize); + continue; + } + } + let mut object = Map::new(); + for pair in table.pairs::() { + let (key, value) = pair?; + let key = match key { + LuaValue::String(value) => value.to_str()?.to_owned(), + LuaValue::Integer(value) => value.to_string(), + _ => { + return Err(mlua::Error::external( + "provider host trace table keys must be strings or positive integers", + )) + } + }; + object.insert(key, lua_value_to_json(value)?); + } + return Ok(Value::Object(object)); + } + let mut array = Vec::with_capacity(len); + for index in 1..=len { + array.push(lua_value_to_json(table.raw_get(index)?)?); + } + Ok(Value::Array(array)) +} + +#[derive(Debug, Clone)] +struct PackageManifest { + sha512: BTreeSet, +} + +impl PackageManifest { + fn load(path: impl AsRef) -> Result { + let path = path.as_ref(); + let source = match std::fs::read_to_string(path) { + Ok(source) => source, + Err(source) if source.kind() == std::io::ErrorKind::NotFound => { + return Ok(Self { + sha512: BTreeSet::new(), + }) + } + Err(source) => { + return Err(LuaProviderHostOperationError::ReadManifest { + path: path.to_path_buf(), + source, + }) + } + }; + let mut sha512 = BTreeSet::new(); + for (line_index, raw_line) in source.lines().enumerate() { + let line = raw_line.trim(); + if line.is_empty() { + continue; + } + let hash = line + .split_whitespace() + .next() + .expect("non-empty line has token"); + if !is_sha512_hex(hash) { + return Err(LuaProviderHostOperationError::InvalidManifest { + path: path.to_path_buf(), + reason: format!( + "line {} must start with a 128-character SHA-512 hex digest", + line_index + 1 + ), + }); + } + sha512.insert(hash.to_ascii_lowercase()); + } + Ok(Self { sha512 }) + } + + fn allows_body(&self, body: &str) -> bool { + self.sha512.contains(&sha512_hex(body.as_bytes())) + } +} + +fn is_sha512_hex(value: &str) -> bool { + value.len() == 128 && value.bytes().all(|byte| byte.is_ascii_hexdigit()) +} + +fn sha512_hex(body: &[u8]) -> String { + let mut hasher = Sha512::new(); + hasher.update(body); + format!("{:x}", hasher.finalize()) +} + +fn require_manifest_body( + manifest: &Option, + provider: &str, + body: &str, +) -> mlua::Result<()> { + let Some(manifest) = manifest else { + return Ok(()); + }; + if manifest.allows_body(body) { + return Ok(()); + } + Err(mlua::Error::external(format!( + "{PROVIDER_RESPONSE_NOT_IN_MANIFEST}: {provider} provider response body is not listed in package Manifest" + ))) +} + +fn reject_unproven_manifest_cache( + manifest: &Option, + provider: &str, + source: ProviderCacheSource, +) -> mlua::Result<()> { + if manifest.is_none() || source == ProviderCacheSource::Refreshed { + return Ok(()); + } + Err(mlua::Error::external(format!( + "{PROVIDER_CACHE_PROVENANCE_MISSING}: {provider} provider cache lacks Manifest-compatible source response provenance" + ))) +} + fn provider_cache_mode( mode: Option<&str>, ) -> Result { @@ -422,9 +963,7 @@ fn required_lua_string(table: &Table, field: &'static str) -> mlua::Result mlua::Result Ok(None), LuaValue::Boolean(value) => Ok(Some(value)), _ => Err(mlua::Error::external(format!( - "getter_dev.github_release_candidates {field} must be a boolean" + "GitHub release provider host {field} must be a boolean" ))), } } @@ -446,7 +985,7 @@ fn asset_filter_from_lua(value: LuaValue) -> mlua::Result { exclude: optional_lua_string(&table, "exclude")?, }), _ => Err(mlua::Error::external( - "getter_dev.github_release_candidates asset must be a table", + "GitHub release provider host asset must be a table", )), } } @@ -456,7 +995,7 @@ fn optional_lua_string(table: &Table, field: &'static str) -> mlua::Result Ok(None), LuaValue::String(value) => Ok(Some(value.to_str()?.to_owned())), _ => Err(mlua::Error::external(format!( - "getter_dev.github_release_candidates asset.{field} must be a string" + "GitHub release provider host asset.{field} must be a string" ))), } } @@ -472,54 +1011,6 @@ fn github_eligible_release_count( .count() } -fn update_candidates_to_lua(lua: &Lua, candidates: &[UpdateCandidate]) -> mlua::Result { - if candidates.is_empty() { - return Ok(LuaValue::Nil); - } - - let table = lua.create_table()?; - for (index, candidate) in candidates.iter().enumerate() { - table.raw_set(index + 1, update_candidate_to_lua(lua, candidate)?)?; - } - Ok(LuaValue::Table(table)) -} - -fn update_candidate_to_lua(lua: &Lua, candidate: &UpdateCandidate) -> mlua::Result
{ - let table = lua.create_table()?; - table.set("version", candidate.version.as_str())?; - if let Some(version_code) = candidate.version_code { - table.set("version_code", version_code)?; - } - if let Some(channel) = candidate.channel.as_ref() { - table.set("channel", channel.as_str())?; - } - if let Some(source) = candidate.source.as_ref() { - table.set("source", source.as_str())?; - } - let artifacts = lua.create_table()?; - for (index, artifact) in candidate.artifacts.iter().enumerate() { - artifacts.raw_set(index + 1, update_artifact_to_lua(lua, artifact)?)?; - } - table.set("artifacts", artifacts)?; - Ok(table) -} - -fn update_artifact_to_lua(lua: &Lua, artifact: &UpdateArtifact) -> mlua::Result
{ - let table = lua.create_table()?; - table.set("name", artifact.name.as_str())?; - table.set("url", artifact.url.as_str())?; - if let Some(file_name) = artifact.file_name.as_ref() { - table.set("file_name", file_name.as_str())?; - } - if let Some(sha256) = artifact.sha256.as_ref() { - table.set("sha256", sha256.as_str())?; - } - if let Some(size) = artifact.size { - table.set("size", size)?; - } - Ok(table) -} - fn provider_source_json(source: ProviderCacheSource) -> &'static str { match source { ProviderCacheSource::Cache => "cache", @@ -749,6 +1240,341 @@ mod tests { ); } + #[test] + fn stable_provider_host_exists_only_in_provider_backed_operation() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local provider_type = type(getter and getter.provider) +local builtin_provider_type = type(getter_builtin and getter_builtin.provider) +return package_version { + name = provider_type .. ":" .. builtin_provider_type, + updates = { + { + version = "1.0.0", + artifacts = { + { name = "placeholder", url = "https://example.invalid/placeholder.apk" }, + }, + }, + }, +} +"#, + ) + .unwrap(); + register_official_repository(data_dir, &repo_root); + let layout = RepositoryPackageDirectoryLayout::load(&repo_root).unwrap(); + let package = layout + .package(&"android/app/org.fdroid.fdroid".parse().unwrap()) + .unwrap(); + let metadata = layout.package_metadata(package).unwrap(); + let script = layout.unambiguous_version_script(package).unwrap(); + let plain = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("official").unwrap(), + package, + &metadata, + script, + |_| Ok(()), + ) + .unwrap(); + + assert_eq!(plain.name, "nil:nil"); + + let stable = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(stable["package"]["name"], "table:table"); + } + + #[test] + fn stable_provider_host_evaluates_fdroid_release_envelope() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + Some(FDROID_INDEX_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["operation"], "provider.package_eval.fixture"); + assert_eq!(result["provider_calls"][0]["provider"], FDROID_PROVIDER_ID); + assert_eq!(result["provider_calls"][0]["request"], "catalog"); + assert_eq!(result["provider_calls"][0]["source"], "refreshed"); + assert_eq!(result["package"]["updates"][0]["version"], "1.20.0"); + assert_eq!(result["package"]["updates"][0]["version_code"], 1020000); + assert_eq!( + result["package"]["updates"][0]["artifacts"][0]["sha256"], + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ); + assert!(result["package"]["updates"].is_array()); + assert_eq!(result["runtime_hooks"], json!([])); + assert!(CacheDb::open(data_dir.join("cache.db")) + .unwrap() + .provider_response(result["provider_calls"][0]["cache_key"].as_str().unwrap()) + .unwrap() + .is_some()); + } + + #[test] + fn stable_provider_host_evaluates_github_release_envelope() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/app/org.fdroid.fdroid", + stable_github_script("[.]apk$"), + Some(GITHUB_RELEASES_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "github_releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["provider"], GITHUB_PROVIDER_ID); + assert_eq!(result["provider_calls"][0]["request"], "releases"); + assert_eq!(result["package"]["updates"][0]["version"], "v1.20.0"); + assert_eq!(result["package"]["updates"][0]["source"], "github"); + assert_eq!( + result["package"]["updates"][0]["artifacts"][0]["file_name"], + "F-Droid.apk" + ); + } + + #[test] + fn stable_provider_host_omits_no_candidate_updates() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("missing.package"), + Some(FDROID_INDEX_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["package"]["updates"], json!([])); + assert_eq!( + result["provider_calls"][0]["diagnostics"][0]["code"], + FDROID_PACKAGE_NOT_FOUND + ); + } + + #[test] + fn stable_provider_host_does_not_install_latest_commit_shape() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local latest_commit = getter.provider.github.latest_commit +return package_version { + name = type(latest_commit), + updates = { + { + version = "1.0.0", + artifacts = { + { name = "placeholder", url = "https://example.invalid/placeholder.apk" }, + }, + }, + }, +} +"#, + None, + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["package"]["name"], "nil"); + } + + #[test] + fn stable_provider_hook_wraps_provider_function_and_calls_builtin_original() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + Some(FDROID_INDEX_FIXTURE), + false, + ); + write_hook( + data_dir, + "10-provider-tag.lua", + r#" +local original = getter_builtin.provider.fdroid.update_candidates +function getter.provider.fdroid.update_candidates(spec) + local result = original(spec) + result.source = "hooked-" .. result.source + return result +end +"#, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["source"], "hooked-refreshed"); + assert_eq!(result["package"]["updates"][0]["version"], "1.20.0"); + assert_eq!(result["runtime_hooks"].as_array().unwrap().len(), 1); + } + + #[test] + fn stable_provider_host_requires_manifest_listed_fixture_body() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + None, + false, + ); + + let err = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap_err(); + + assert!(err.to_string().contains(PROVIDER_RESPONSE_NOT_IN_MANIFEST)); + } + + #[test] + fn stable_provider_host_rejects_non_free_cache_hit_without_provenance() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + Some(FDROID_INDEX_FIXTURE), + false, + ); + stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + let err = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap_err(); + + assert!(err.to_string().contains(PROVIDER_CACHE_PROVENANCE_MISSING)); + } + + #[test] + fn stable_provider_host_manifest_check_does_not_apply_to_free_network_script() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + None, + true, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["package"]["updates"][0]["version"], "1.20.0"); + } + #[test] fn provider_package_eval_prefers_repository_luaclass_over_dev_builtin() { let temp = tempfile::tempdir().unwrap(); @@ -858,6 +1684,78 @@ return github_android.package { register_official_repository(data_dir, &repo_root); } + fn write_stable_provider_package_fixture( + data_dir: &std::path::Path, + package_id: &str, + version_script: impl AsRef, + manifest_body: Option<&str>, + allow_free_network: bool, + ) { + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join(package_id); + fs::create_dir_all(&package_dir).unwrap(); + let mut metadata = json!({ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } + }); + if allow_free_network { + metadata["lua"] = json!({ + "9999.lua": { "permission": ["allow_free_network"] } + }); + } + fs::write( + package_dir.join("metadata.jsonc"), + serde_json::to_string_pretty(&metadata).unwrap(), + ) + .unwrap(); + if let Some(manifest_body) = manifest_body { + fs::write( + package_dir.join(PACKAGE_MANIFEST_FILE), + format!("{} fixture-body\n", sha512_hex(manifest_body.as_bytes())), + ) + .unwrap(); + } + fs::write(package_dir.join("9999.lua"), version_script.as_ref()).unwrap(); + register_official_repository(data_dir, &repo_root); + } + + fn stable_fdroid_script(package_name: &str) -> String { + format!( + r#"#!/bin/upa-lua v1 +local result = getter.provider.fdroid.update_candidates {{ + package_name = "{package_name}", +}} +return package_version {{ + source_priority = {{ "fdroid" }}, + updates = result.candidates, +}} +"# + ) + } + + fn stable_github_script(asset_include: &str) -> String { + format!( + r#"#!/bin/upa-lua v1 +local result = getter.provider.github.release_candidates {{ + owner = "f-droid", + repo = "fdroidclient", + asset = {{ include = "{asset_include}" }}, +}} +return package_version {{ + name = "F-Droid", + source_priority = {{ "github" }}, + updates = result.candidates, +}} +"# + ) + } + + fn write_hook(data_dir: &std::path::Path, file_name: &str, source: &str) { + let hook_dir = data_dir.join("rc/hook"); + fs::create_dir_all(&hook_dir).unwrap(); + fs::write(hook_dir.join(file_name), source).unwrap(); + } + fn register_official_repository(data_dir: &std::path::Path, repo_root: &std::path::Path) { let main_db = MainDb::open(data_dir.join("main.db")).unwrap(); main_db From 6a83a2baa31efa96d7e7fd9d252b36ef29a9cf0f Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 12:04:07 +0800 Subject: [PATCH 54/71] feat(cache): store provider response provenance --- .../getter-operations/src/fdroid_catalog.rs | 23 ++- .../src/github_latest_commit.rs | 23 ++- .../getter-operations/src/github_releases.rs | 26 ++- .../src/lua_provider_host.rs | 142 ++++++++++++++-- .../getter-operations/src/provider_cache.rs | 82 +++++++-- crates/getter-storage/src/lib.rs | 156 +++++++++++++++++- 6 files changed, 403 insertions(+), 49 deletions(-) diff --git a/crates/getter-operations/src/fdroid_catalog.rs b/crates/getter-operations/src/fdroid_catalog.rs index 3a301f5..d65f29d 100644 --- a/crates/getter-operations/src/fdroid_catalog.rs +++ b/crates/getter-operations/src/fdroid_catalog.rs @@ -6,8 +6,9 @@ //! task state. use crate::provider_cache::{ - read_or_refresh_provider_response, ProviderCacheDiagnostic, ProviderCacheMode, - ProviderCacheOperationError, ProviderCacheRequest, ProviderCacheSource, + read_or_refresh_provider_response_with_provenance, source_response_sha512, + ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheOperationError, ProviderCacheRequest, + ProviderCacheSource, ProviderResponseRefresh, }; use getter_providers::{parse_fdroid_index_xml, FdroidApp, FdroidCatalog, FdroidCatalogError}; use getter_storage::{CacheDb, StorageError}; @@ -51,6 +52,8 @@ pub struct FdroidCatalogResult { pub cache_key: String, pub catalog: FdroidCatalog, pub source: ProviderCacheSource, + pub source_response_sha512: Vec, + pub provenance_schema_version: Option, pub diagnostics: Vec, } @@ -84,7 +87,7 @@ where F: FnOnce() -> Result, { let cache_key = endpoint.cache_key(); - let cache_result = read_or_refresh_provider_response( + let cache_result = read_or_refresh_provider_response_with_provenance( db, ProviderCacheRequest { cache_key: &cache_key, @@ -93,17 +96,27 @@ where }, || { let xml = refresh_xml()?; + let source_digest = source_response_sha512(xml.as_bytes()); let catalog = parse_fdroid_index_xml(&xml).map_err(|source| source.to_string())?; - serde_json::to_value(catalog).map_err(|source| source.to_string()) + let response_json = + serde_json::to_value(catalog).map_err(|source| source.to_string())?; + Ok(ProviderResponseRefresh { + response_json, + source_response_sha512: vec![source_digest], + freshness_json: json!({}), + }) }, )?; - let catalog = serde_json::from_value(cache_result.response.response_json)?; + let response = cache_result.response; + let catalog = serde_json::from_value(response.response_json)?; Ok(FdroidCatalogResult { endpoint, cache_key, catalog, source: cache_result.source, + source_response_sha512: response.source_response_sha512, + provenance_schema_version: response.provenance_schema_version, diagnostics: cache_result.diagnostics, }) } diff --git a/crates/getter-operations/src/github_latest_commit.rs b/crates/getter-operations/src/github_latest_commit.rs index 15e8b7e..114c7e1 100644 --- a/crates/getter-operations/src/github_latest_commit.rs +++ b/crates/getter-operations/src/github_latest_commit.rs @@ -6,8 +6,9 @@ //! actions, downloads, installer handoffs, or Flutter/Kotlin provider parsing. use crate::provider_cache::{ - read_or_refresh_provider_response, ProviderCacheDiagnostic, ProviderCacheMode, - ProviderCacheOperationError, ProviderCacheRequest, ProviderCacheSource, + read_or_refresh_provider_response_with_provenance, source_response_sha512, + ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheOperationError, ProviderCacheRequest, + ProviderCacheSource, ProviderResponseRefresh, }; use getter_providers::{ github_latest_commit_live_revision, parse_github_commit_json, GithubCommit, GithubLiveRevision, @@ -50,6 +51,8 @@ pub struct GithubLatestCommitResult { pub commit: GithubCommit, pub live_revision: GithubLiveRevision, pub source: ProviderCacheSource, + pub source_response_sha512: Vec, + pub provenance_schema_version: Option, pub diagnostics: Vec, } @@ -77,7 +80,7 @@ where F: FnOnce() -> Result, { let cache_key = config.cache_key(); - let cache_result = read_or_refresh_provider_response( + let cache_result = read_or_refresh_provider_response_with_provenance( db, ProviderCacheRequest { cache_key: &cache_key, @@ -86,12 +89,20 @@ where }, || { let json = refresh_json()?; + let source_digest = source_response_sha512(json.as_bytes()); let commit = parse_github_commit_json(&json).map_err(|source| source.to_string())?; github_latest_commit_live_revision(&commit).map_err(|source| source.to_string())?; - serde_json::to_value(commit).map_err(|source| source.to_string()) + let response_json = + serde_json::to_value(commit).map_err(|source| source.to_string())?; + Ok(ProviderResponseRefresh { + response_json, + source_response_sha512: vec![source_digest], + freshness_json: serde_json::json!({}), + }) }, )?; - let commit = serde_json::from_value(cache_result.response.response_json)?; + let response = cache_result.response; + let commit = serde_json::from_value(response.response_json)?; let live_revision = github_latest_commit_live_revision(&commit)?; Ok(GithubLatestCommitResult { @@ -100,6 +111,8 @@ where commit, live_revision, source: cache_result.source, + source_response_sha512: response.source_response_sha512, + provenance_schema_version: response.provenance_schema_version, diagnostics: cache_result.diagnostics, }) } diff --git a/crates/getter-operations/src/github_releases.rs b/crates/getter-operations/src/github_releases.rs index 9f151c2..cdac38b 100644 --- a/crates/getter-operations/src/github_releases.rs +++ b/crates/getter-operations/src/github_releases.rs @@ -6,8 +6,9 @@ //! live HTTP, Flutter/Kotlin provider parsing, downloader, or installer logic. use crate::provider_cache::{ - read_or_refresh_provider_response, ProviderCacheDiagnostic, ProviderCacheMode, - ProviderCacheOperationError, ProviderCacheRequest, ProviderCacheSource, + read_or_refresh_provider_response_with_provenance, source_response_sha512, + ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheOperationError, ProviderCacheRequest, + ProviderCacheSource, ProviderResponseRefresh, }; use getter_providers::{ github_release_update_candidates, parse_github_releases_json, GithubAssetFilter, @@ -47,6 +48,8 @@ pub struct GithubReleaseResult { pub cache_key: String, pub releases: Vec, pub source: ProviderCacheSource, + pub source_response_sha512: Vec, + pub provenance_schema_version: Option, pub diagnostics: Vec, } @@ -74,7 +77,7 @@ where F: FnOnce() -> Result, { let cache_key = config.cache_key(); - let cache_result = read_or_refresh_provider_response( + let cache_result = read_or_refresh_provider_response_with_provenance( db, ProviderCacheRequest { cache_key: &cache_key, @@ -83,18 +86,28 @@ where }, || { let json = refresh_json()?; + let source_digest = source_response_sha512(json.as_bytes()); let releases = parse_github_releases_json(&json).map_err(|source| source.to_string())?; - serde_json::to_value(releases).map_err(|source| source.to_string()) + let response_json = + serde_json::to_value(releases).map_err(|source| source.to_string())?; + Ok(ProviderResponseRefresh { + response_json, + source_response_sha512: vec![source_digest], + freshness_json: serde_json::json!({}), + }) }, )?; - let releases = serde_json::from_value(cache_result.response.response_json)?; + let response = cache_result.response; + let releases = serde_json::from_value(response.response_json)?; Ok(GithubReleaseResult { config, cache_key, releases, source: cache_result.source, + source_response_sha512: response.source_response_sha512, + provenance_schema_version: response.provenance_schema_version, diagnostics: cache_result.diagnostics, }) } @@ -361,6 +374,9 @@ mod tests { cache_key, provider: GITHUB_PROVIDER_ID.to_owned(), response_json: serde_json::from_str(GITHUB_RELEASES_FIXTURE).unwrap(), + source_response_sha512: Vec::new(), + provenance_schema_version: None, + freshness_json: serde_json::json!({}), }) .unwrap(); diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 15cb3b1..24101c7 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -15,7 +15,10 @@ use crate::github_releases::{ DEFAULT_GITHUB_API_BASE_URL, GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, }; use crate::lua_runtime_hooks::load_runtime_hooks; -use crate::provider_cache::{ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheSource}; +use crate::provider_cache::{ + ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheSource, + PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1, +}; use getter_core::lua::{evaluate_package_directory_script_with_host_bindings, LuaPackageError}; use getter_core::repository::{ PackageDirectory, PackageDirectoryMetadata, PackageLuaPermission, PackageVersionScript, @@ -284,14 +287,6 @@ pub fn stable_provider_package_eval_json( package_directory.path.join(PACKAGE_MANIFEST_FILE), )?) }; - // Parsed provider cache currently has no source-body digest provenance. - // Non-free scripts therefore refetch/revalidate fixture bodies instead of - // accepting cache hits that cannot prove Manifest compatibility. - let provider_mode = if manifest.is_some() { - ProviderCacheMode::ForceRefresh - } else { - mode - }; let package = evaluate_with_stable_provider_host( data_dir, &repository.id, @@ -302,14 +297,14 @@ pub fn stable_provider_package_eval_json( fdroid: FdroidProviderHostConfig { cache_db_path: data_dir.join("cache.db"), endpoint, - mode: provider_mode, + mode, index_xml: fdroid_index_xml, manifest: manifest.clone(), }, github: GithubProviderHostConfig { cache_db_path: data_dir.join("cache.db"), api_base_url: github_api_base_url, - mode: provider_mode, + mode, releases_json: github_releases_json, default_include_prereleases, manifest, @@ -594,7 +589,13 @@ fn fdroid_update_candidates_envelope( Ok(xml) }) .map_err(mlua::Error::external)?; - reject_unproven_manifest_cache(&manifest, FDROID_PROVIDER_ID, result.source)?; + reject_unproven_manifest_cache( + &manifest, + FDROID_PROVIDER_ID, + result.source, + &result.source_response_sha512, + result.provenance_schema_version.as_deref(), + )?; let mut diagnostics = result .diagnostics .iter() @@ -661,7 +662,13 @@ fn github_release_candidates_envelope( Ok(releases) }) .map_err(mlua::Error::external)?; - reject_unproven_manifest_cache(&manifest, GITHUB_PROVIDER_ID, result.source)?; + reject_unproven_manifest_cache( + &manifest, + GITHUB_PROVIDER_ID, + result.source, + &result.source_response_sha512, + result.provenance_schema_version.as_deref(), + )?; let options = GithubReleaseCandidateOptions { include_prereleases: request .include_prereleases @@ -880,7 +887,11 @@ impl PackageManifest { } fn allows_body(&self, body: &str) -> bool { - self.sha512.contains(&sha512_hex(body.as_bytes())) + self.allows_sha512(&sha512_hex(body.as_bytes())) + } + + fn allows_sha512(&self, digest: &str) -> bool { + is_sha512_hex(digest) && self.sha512.contains(&digest.to_ascii_lowercase()) } } @@ -914,12 +925,30 @@ fn reject_unproven_manifest_cache( manifest: &Option, provider: &str, source: ProviderCacheSource, + source_response_sha512: &[String], + provenance_schema_version: Option<&str>, ) -> mlua::Result<()> { - if manifest.is_none() || source == ProviderCacheSource::Refreshed { + let Some(manifest) = manifest else { + return Ok(()); + }; + if source == ProviderCacheSource::Refreshed { + return Ok(()); + } + if provenance_schema_version != Some(PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1) + || source_response_sha512.is_empty() + { + return Err(mlua::Error::external(format!( + "{PROVIDER_CACHE_PROVENANCE_MISSING}: {provider} provider cache lacks Manifest-compatible source response provenance" + ))); + } + if source_response_sha512 + .iter() + .all(|digest| manifest.allows_sha512(digest)) + { return Ok(()); } Err(mlua::Error::external(format!( - "{PROVIDER_CACHE_PROVENANCE_MISSING}: {provider} provider cache lacks Manifest-compatible source response provenance" + "{PROVIDER_RESPONSE_NOT_IN_MANIFEST}: {provider} provider cache source response digest is not listed in package Manifest" ))) } @@ -1034,7 +1063,7 @@ mod tests { use super::*; use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; use getter_core::{RepositoryId, RepositoryPriority}; - use getter_storage::CacheDb; + use getter_storage::{CacheDb, ProviderResponseUpsert}; use serde_json::json; use std::fs; @@ -1515,7 +1544,7 @@ end } #[test] - fn stable_provider_host_rejects_non_free_cache_hit_without_provenance() { + fn stable_provider_host_uses_manifest_compatible_cache_provenance() { let temp = tempfile::tempdir().unwrap(); let data_dir = temp.path(); write_stable_provider_package_fixture( @@ -1536,6 +1565,33 @@ end ) .unwrap(); + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["source"], "cache"); + assert_eq!(result["package"]["updates"][0]["version"], "1.20.0"); + } + + #[test] + fn stable_provider_host_rejects_non_free_cache_hit_without_provenance() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + Some(FDROID_INDEX_FIXTURE), + false, + ); + write_fdroid_cache_response(data_dir, Vec::new(), None); + let err = stable_provider_package_eval_json( data_dir, &json!({ @@ -1549,6 +1605,36 @@ end assert!(err.to_string().contains(PROVIDER_CACHE_PROVENANCE_MISSING)); } + #[test] + fn stable_provider_host_rejects_cache_provenance_digest_not_in_manifest() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + stable_fdroid_script("org.fdroid.fdroid"), + Some(FDROID_INDEX_FIXTURE), + false, + ); + write_fdroid_cache_response( + data_dir, + vec![sha512_hex(b"different provider body")], + Some(PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1.to_owned()), + ); + + let err = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid" + }) + .to_string(), + ) + .unwrap_err(); + + assert!(err.to_string().contains(PROVIDER_RESPONSE_NOT_IN_MANIFEST)); + } + #[test] fn stable_provider_host_manifest_check_does_not_apply_to_free_network_script() { let temp = tempfile::tempdir().unwrap(); @@ -1750,6 +1836,26 @@ return package_version {{ ) } + fn write_fdroid_cache_response( + data_dir: &std::path::Path, + source_response_sha512: Vec, + provenance_schema_version: Option, + ) { + let endpoint = FdroidEndpointConfig::default(); + let catalog = getter_providers::parse_fdroid_index_xml(FDROID_INDEX_FIXTURE).unwrap(); + CacheDb::open(data_dir.join("cache.db")) + .unwrap() + .upsert_provider_response(&ProviderResponseUpsert { + cache_key: endpoint.cache_key(), + provider: FDROID_PROVIDER_ID.to_owned(), + response_json: serde_json::to_value(catalog).unwrap(), + source_response_sha512, + provenance_schema_version, + freshness_json: json!({}), + }) + .unwrap(); + } + fn write_hook(data_dir: &std::path::Path, file_name: &str, source: &str) { let hook_dir = data_dir.join("rc/hook"); fs::create_dir_all(&hook_dir).unwrap(); diff --git a/crates/getter-operations/src/provider_cache.rs b/crates/getter-operations/src/provider_cache.rs index 771cd81..89179e3 100644 --- a/crates/getter-operations/src/provider_cache.rs +++ b/crates/getter-operations/src/provider_cache.rs @@ -6,10 +6,12 @@ //! diagnostics. use getter_storage::{CacheDb, ProviderResponseUpsert, StorageError, StoredProviderResponse}; -use serde_json::Value; +use serde_json::{json, Value}; +use sha2::{Digest, Sha512}; pub const CACHE_REFRESH_FAILED: &str = "cache.refresh_failed"; pub const USED_STALE_CACHE: &str = "used_stale_cache"; +pub const PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1: &str = "provider-response-provenance-v1"; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ProviderCacheMode { @@ -31,6 +33,13 @@ pub struct ProviderCacheRequest<'a> { pub mode: ProviderCacheMode, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProviderResponseRefresh { + pub response_json: Value, + pub source_response_sha512: Vec, + pub freshness_json: Value, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProviderCacheDiagnostic { pub code: String, @@ -62,6 +71,23 @@ pub fn read_or_refresh_provider_response( ) -> Result where F: FnOnce() -> Result, +{ + read_or_refresh_provider_response_with_provenance(db, request, || { + refresh().map(|response_json| ProviderResponseRefresh { + response_json, + source_response_sha512: Vec::new(), + freshness_json: json!({}), + }) + }) +} + +pub fn read_or_refresh_provider_response_with_provenance( + db: &CacheDb, + request: ProviderCacheRequest<'_>, + refresh: F, +) -> Result +where + F: FnOnce() -> Result, { let cached = db.provider_response(request.cache_key)?; if request.mode == ProviderCacheMode::UseCached { @@ -75,11 +101,14 @@ where } match refresh() { - Ok(response_json) => { + Ok(refresh) => { let response = db.upsert_provider_response(&ProviderResponseUpsert { cache_key: request.cache_key.to_owned(), provider: request.provider.to_owned(), - response_json, + response_json: refresh.response_json, + source_response_sha512: refresh.source_response_sha512, + provenance_schema_version: Some(PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1.to_owned()), + freshness_json: refresh.freshness_json, })?; Ok(ProviderCacheResult { response, @@ -116,9 +145,16 @@ where } } +pub fn source_response_sha512(body: impl AsRef<[u8]>) -> String { + let mut hasher = Sha512::new(); + hasher.update(body.as_ref()); + format!("{:x}", hasher.finalize()) +} + #[cfg(test)] mod tests { use super::*; + use getter_storage::ProviderResponseUpsert; use serde_json::json; use std::cell::Cell; @@ -129,6 +165,9 @@ mod tests { cache_key: "fdroid:official:index".to_owned(), provider: "fdroid".to_owned(), response_json: json!({ "revision": "cached" }), + source_response_sha512: Vec::new(), + provenance_schema_version: None, + freshness_json: json!({}), }) .unwrap(); let refreshed = Cell::new(false); @@ -157,25 +196,42 @@ mod tests { fn cache_miss_refreshes_and_stores_provider_response() { let db = CacheDb::open_in_memory().unwrap(); - let result = read_or_refresh_provider_response( + let result = read_or_refresh_provider_response_with_provenance( &db, ProviderCacheRequest { cache_key: "github:f-droid/fdroidclient:releases", provider: "github", mode: ProviderCacheMode::UseCached, }, - || Ok(json!({ "etag": "fresh" })), + || { + Ok(ProviderResponseRefresh { + response_json: json!({ "etag": "fresh" }), + source_response_sha512: vec![source_response_sha512("fresh body")], + freshness_json: json!({ "etag": "fresh" }), + }) + }, ) .unwrap(); assert_eq!(result.source, ProviderCacheSource::Refreshed); assert_eq!(result.response.response_json["etag"], "fresh"); assert_eq!( - db.provider_response("github:f-droid/fdroidclient:releases") - .unwrap() - .unwrap() - .response_json["etag"], - "fresh" + result.response.provenance_schema_version.as_deref(), + Some(PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1) + ); + assert_eq!( + result.response.source_response_sha512, + vec![source_response_sha512("fresh body")] + ); + assert_eq!(result.response.freshness_json["etag"], "fresh"); + let cached = db + .provider_response("github:f-droid/fdroidclient:releases") + .unwrap() + .unwrap(); + assert_eq!(cached.response_json["etag"], "fresh"); + assert_eq!( + cached.source_response_sha512, + result.response.source_response_sha512 ); } @@ -186,6 +242,9 @@ mod tests { cache_key: "fdroid:official:index".to_owned(), provider: "fdroid".to_owned(), response_json: json!({ "revision": "old" }), + source_response_sha512: Vec::new(), + provenance_schema_version: None, + freshness_json: json!({}), }) .unwrap(); @@ -218,6 +277,9 @@ mod tests { cache_key: "github:f-droid/fdroidclient:releases".to_owned(), provider: "github".to_owned(), response_json: json!({ "etag": "old" }), + source_response_sha512: Vec::new(), + provenance_schema_version: None, + freshness_json: json!({}), }) .unwrap(); diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index 2af994e..e2b2c7e 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -875,32 +875,94 @@ CREATE TABLE IF NOT EXISTS provider_responses ( cache_key TEXT PRIMARY KEY, provider TEXT NOT NULL, response_json TEXT NOT NULL, + source_response_sha512_json TEXT NOT NULL DEFAULT '[]', + provenance_schema_version TEXT, + freshness_json TEXT NOT NULL DEFAULT '{}', fetched_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) ); "#, )?; + self.ensure_column( + "provider_responses", + "source_response_sha512_json", + "TEXT NOT NULL DEFAULT '[]'", + )?; + self.ensure_column("provider_responses", "provenance_schema_version", "TEXT")?; + self.ensure_column( + "provider_responses", + "freshness_json", + "TEXT NOT NULL DEFAULT '{}'", + )?; self.conn.execute( "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('cache-v1')", [], )?; + self.conn.execute( + "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('cache-provider-provenance-v1')", + [], + )?; + Ok(()) + } + + fn ensure_column( + &self, + table: &'static str, + column: &'static str, + definition: &'static str, + ) -> Result<(), StorageError> { + let columns = self.table_columns(table)?; + if !columns.iter().any(|existing| existing == column) { + self.conn.execute_batch(&format!( + "ALTER TABLE {table} ADD COLUMN {column} {definition};" + ))?; + } Ok(()) } + fn table_columns(&self, table: &'static str) -> Result, StorageError> { + let mut stmt = self.conn.prepare(&format!("PRAGMA table_info({table})"))?; + let rows = stmt.query_map([], |row| row.get::<_, String>(1))?; + let mut columns = Vec::new(); + for row in rows { + columns.push(row?); + } + Ok(columns) + } + pub fn upsert_provider_response( &self, response: &ProviderResponseUpsert, ) -> Result { let response_json = serde_json::to_string(&response.response_json)?; + let source_response_sha512_json = serde_json::to_string(&response.source_response_sha512)?; + let freshness_json = serde_json::to_string(&response.freshness_json)?; self.conn.execute( r#" -INSERT INTO provider_responses(cache_key, provider, response_json) -VALUES (?1, ?2, ?3) +INSERT INTO provider_responses( + cache_key, + provider, + response_json, + source_response_sha512_json, + provenance_schema_version, + freshness_json +) +VALUES (?1, ?2, ?3, ?4, ?5, ?6) ON CONFLICT(cache_key) DO UPDATE SET provider = excluded.provider, response_json = excluded.response_json, + source_response_sha512_json = excluded.source_response_sha512_json, + provenance_schema_version = excluded.provenance_schema_version, + freshness_json = excluded.freshness_json, fetched_at_unix = unixepoch() "#, - params![response.cache_key, response.provider, response_json], + params![ + response.cache_key, + response.provider, + response_json, + source_response_sha512_json, + response.provenance_schema_version, + freshness_json, + ], )?; self.provider_response(&response.cache_key)?.ok_or_else(|| { StorageError::Invariant(format!( @@ -916,7 +978,14 @@ ON CONFLICT(cache_key) DO UPDATE SET ) -> Result, StorageError> { let mut stmt = self.conn.prepare( r#" -SELECT cache_key, provider, response_json, fetched_at_unix +SELECT + cache_key, + provider, + response_json, + source_response_sha512_json, + provenance_schema_version, + freshness_json, + fetched_at_unix FROM provider_responses WHERE cache_key = ?1 "#, @@ -927,16 +996,31 @@ WHERE cache_key = ?1 row.get::<_, String>(0)?, row.get::<_, String>(1)?, row.get::<_, String>(2)?, - row.get::<_, i64>(3)?, + row.get::<_, String>(3)?, + row.get::<_, Option>(4)?, + row.get::<_, String>(5)?, + row.get::<_, i64>(6)?, )) }) .optional()?; row.map( - |(cache_key, provider, response_json, fetched_at_unix)| -> Result<_, StorageError> { + |( + cache_key, + provider, + response_json, + source_response_sha512_json, + provenance_schema_version, + freshness_json, + fetched_at_unix, + )| + -> Result<_, StorageError> { Ok(StoredProviderResponse { cache_key, provider, response_json: serde_json::from_str(&response_json)?, + source_response_sha512: serde_json::from_str(&source_response_sha512_json)?, + provenance_schema_version, + freshness_json: serde_json::from_str(&freshness_json)?, fetched_at_unix, }) }, @@ -1057,6 +1141,9 @@ pub struct ProviderResponseUpsert { pub cache_key: String, pub provider: String, pub response_json: Value, + pub source_response_sha512: Vec, + pub provenance_schema_version: Option, + pub freshness_json: Value, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -1064,6 +1151,9 @@ pub struct StoredProviderResponse { pub cache_key: String, pub provider: String, pub response_json: Value, + pub source_response_sha512: Vec, + pub provenance_schema_version: Option, + pub freshness_json: Value, pub fetched_at_unix: i64, } @@ -1691,6 +1781,39 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); let _db = CacheDb::open_in_memory().unwrap(); } + #[test] + fn cache_db_migrates_legacy_provider_response_rows_without_provenance() { + let temp = tempfile::tempdir().unwrap(); + let db_path = temp.path().join("cache.db"); + { + let conn = rusqlite::Connection::open(&db_path).unwrap(); + conn.execute_batch( + r#" +CREATE TABLE provider_responses ( + cache_key TEXT PRIMARY KEY, + provider TEXT NOT NULL, + response_json TEXT NOT NULL, + fetched_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); +INSERT INTO provider_responses(cache_key, provider, response_json) +VALUES ('fdroid:legacy:index', 'fdroid', '{"revision":"legacy"}'); +"#, + ) + .unwrap(); + } + + let db = CacheDb::open(&db_path).unwrap(); + let stored = db + .provider_response("fdroid:legacy:index") + .unwrap() + .unwrap(); + + assert_eq!(stored.response_json["revision"], "legacy"); + assert_eq!(stored.source_response_sha512, Vec::::new()); + assert_eq!(stored.provenance_schema_version, None); + assert_eq!(stored.freshness_json, serde_json::json!({})); + } + #[test] fn cache_db_round_trips_provider_response_cache_entries() { let db = CacheDb::open_in_memory().unwrap(); @@ -1703,12 +1826,21 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); "endpoint": "official", "packages": ["org.fdroid.fdroid"] }), + source_response_sha512: vec!["a".repeat(128)], + provenance_schema_version: Some("provider-response-provenance-v1".to_owned()), + freshness_json: serde_json::json!({ "etag": "fixture-etag" }), }) .unwrap(); assert_eq!(stored.cache_key, "fdroid:official:index:v1"); assert_eq!(stored.provider, "fdroid"); assert_eq!(stored.response_json["endpoint"], "official"); + assert_eq!(stored.source_response_sha512, vec!["a".repeat(128)]); + assert_eq!( + stored.provenance_schema_version.as_deref(), + Some("provider-response-provenance-v1") + ); + assert_eq!(stored.freshness_json["etag"], "fixture-etag"); assert_eq!( db.provider_response("fdroid:official:index:v1") .unwrap() @@ -1724,6 +1856,9 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); cache_key: "github:f-droid/fdroidclient:releases".to_owned(), provider: "github".to_owned(), response_json: serde_json::json!({ "etag": "old" }), + source_response_sha512: Vec::new(), + provenance_schema_version: None, + freshness_json: serde_json::json!({}), }) .unwrap(); @@ -1732,10 +1867,19 @@ VALUES ('android/org.fdroid.fdroid', '1.2.3'); cache_key: "github:f-droid/fdroidclient:releases".to_owned(), provider: "github".to_owned(), response_json: serde_json::json!({ "etag": "new" }), + source_response_sha512: vec!["b".repeat(128)], + provenance_schema_version: Some("provider-response-provenance-v1".to_owned()), + freshness_json: serde_json::json!({ "etag": "new" }), }) .unwrap(); assert_eq!(stored.response_json["etag"], "new"); + assert_eq!(stored.source_response_sha512, vec!["b".repeat(128)]); + assert_eq!( + stored.provenance_schema_version.as_deref(), + Some("provider-response-provenance-v1") + ); + assert_eq!(stored.freshness_json["etag"], "new"); assert_eq!( db.provider_response("github:f-droid/fdroidclient:releases") .unwrap() From 20f13bfcb30b9f9cfccb57a88220d9d8dadd0cdb Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 14:07:12 +0800 Subject: [PATCH 55/71] feat(lua): promote standard provider luaclasses --- crates/getter-core/Cargo.toml | 1 - crates/getter-core/src/autogen.rs | 4 +- crates/getter-core/src/lua.rs | 53 ++--- .../src/luaclass/fdroid_android.lua | 34 +++- .../src/luaclass/github_android_apk.lua | 47 +++-- crates/getter-core/src/runtime.rs | 14 +- crates/getter-operations/Cargo.toml | 2 +- .../src/lua_provider_host.rs | 189 ++++++++++++++---- 8 files changed, 244 insertions(+), 100 deletions(-) diff --git a/crates/getter-core/Cargo.toml b/crates/getter-core/Cargo.toml index 5cb34ef..4b1f9c5 100644 --- a/crates/getter-core/Cargo.toml +++ b/crates/getter-core/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true [features] default = ["lua"] lua = ["dep:mlua"] -provider-luaclass-dev = ["lua"] [dependencies] json_comments = "0.2" diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs index 6a97c50..c8b7eae 100644 --- a/crates/getter-core/src/autogen.rs +++ b/crates/getter-core/src/autogen.rs @@ -457,10 +457,10 @@ mod tests { assert!(candidate .files .iter() - .any(|file| file.relative_path == PathBuf::from("metadata.jsonc") + .any(|file| file.relative_path == Path::new("metadata.jsonc") && file.content.contains("org.fdroid.fdroid"))); assert!(candidate.files.iter().any(|file| { - file.relative_path == PathBuf::from("9999.lua") + file.relative_path == Path::new("9999.lua") && file.content.starts_with("#!/bin/upa-lua v1\n") && file.content.contains(GENERATED_MARKER) })); diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index f00f014..3bdf60b 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -16,12 +16,10 @@ use std::path::{Path, PathBuf}; const BUILTIN_LUACLASS_MODULES: &[(&str, &str)] = &[ ("android", include_str!("luaclass/android.lua")), - #[cfg(feature = "provider-luaclass-dev")] ( "fdroid_android", include_str!("luaclass/fdroid_android.lua"), ), - #[cfg(feature = "provider-luaclass-dev")] ( "github_android_apk", include_str!("luaclass/github_android_apk.lua"), @@ -1250,9 +1248,8 @@ return android.package_version { assert_eq!(package.installed.len(), 1); } - #[cfg(not(feature = "provider-luaclass-dev"))] #[test] - fn provider_luaclass_dev_modules_are_not_default_builtins() { + fn provider_luaclass_builtin_requires_operation_installed_provider_host() { let temp = tempfile::tempdir().unwrap(); let package_dir = temp.path().join("android/f-droid/app/org.fdroid.fdroid"); fs::create_dir_all(&package_dir).unwrap(); @@ -1277,12 +1274,13 @@ return fdroid.package { package_name = "org.fdroid.fdroid" } let message = err.to_string(); assert!(matches!(err, LuaPackageError::Runtime { .. })); - assert!(message.contains("no builtin luaclass module 'luaclass.fdroid_android'")); + assert!(message.contains( + "luaclass.fdroid_android requires operation-installed getter.provider.fdroid.update_candidates" + )); } - #[cfg(feature = "provider-luaclass-dev")] #[test] - fn provider_luaclass_dev_builtin_can_call_injected_host() { + fn provider_luaclass_builtin_can_call_stable_injected_host() { let temp = tempfile::tempdir().unwrap(); let package_dir = temp.path().join("android/f-droid/app/org.fdroid.fdroid"); fs::create_dir_all(&package_dir).unwrap(); @@ -1313,29 +1311,36 @@ return fdroid.package { package_name = "org.fdroid.fdroid" } &metadata, script, |lua| { - let getter_dev = lua.create_table()?; - getter_dev.set( - "fdroid_update_candidates", - lua.create_function(|lua, spec: Table| { + let candidate = lua.create_table()?; + candidate.set("version", "1.20.0")?; + candidate.set("source", "fdroid")?; + let artifact = lua.create_table()?; + artifact.set("name", "org.fdroid.fdroid.apk")?; + artifact.set("url", "https://f-droid.org/repo/org.fdroid.fdroid.apk")?; + let artifacts = lua.create_table()?; + artifacts.raw_set(1, artifact)?; + candidate.set("artifacts", artifacts)?; + let candidates = lua.create_table()?; + candidates.raw_set(1, candidate)?; + + let fdroid = lua.create_table()?; + fdroid.set( + "update_candidates", + lua.create_function(move |lua, spec: Table| { let package_name: String = spec.get("package_name")?; if package_name != "org.fdroid.fdroid" { return Err(mlua::Error::external("F-Droid package mismatch")); } - let artifact = lua.create_table()?; - artifact.set("name", "org.fdroid.fdroid.apk")?; - artifact.set("url", "https://f-droid.org/repo/org.fdroid.fdroid.apk")?; - let artifacts = lua.create_table()?; - artifacts.raw_set(1, artifact)?; - let candidate = lua.create_table()?; - candidate.set("version", "1.20.0")?; - candidate.set("source", "fdroid")?; - candidate.set("artifacts", artifacts)?; - let candidates = lua.create_table()?; - candidates.raw_set(1, candidate)?; - Ok(candidates) + let envelope = lua.create_table()?; + envelope.set("candidates", candidates.clone())?; + Ok(envelope) })?, )?; - lua.globals().set("getter_dev", getter_dev) + let provider = lua.create_table()?; + provider.set("fdroid", fdroid)?; + let getter = lua.create_table()?; + getter.set("provider", provider)?; + lua.globals().set("getter", getter) }, ) .unwrap(); diff --git a/crates/getter-core/src/luaclass/fdroid_android.lua b/crates/getter-core/src/luaclass/fdroid_android.lua index 7a1237b..7029ffe 100644 --- a/crates/getter-core/src/luaclass/fdroid_android.lua +++ b/crates/getter-core/src/luaclass/fdroid_android.lua @@ -8,24 +8,42 @@ local function require_string(table, field) return value end +local function optional_string(table, field) + local value = table[field] + if value == nil then + return nil + end + if type(value) ~= "string" then + error("fdroid.package field '" .. field .. "' must be a string") + end + if value == "" then + return nil + end + return value +end + local function fdroid_update_candidates() - if type(getter_dev) ~= "table" or type(getter_dev.fdroid_update_candidates) ~= "function" then - error("luaclass.fdroid_android requires operation-installed getter_dev.fdroid_update_candidates") + if type(getter) ~= "table" + or type(getter.provider) ~= "table" + or type(getter.provider.fdroid) ~= "table" + or type(getter.provider.fdroid.update_candidates) ~= "function" then + error("luaclass.fdroid_android requires operation-installed getter.provider.fdroid.update_candidates") end - return getter_dev.fdroid_update_candidates + return getter.provider.fdroid.update_candidates end function fdroid.package(spec) if type(spec) ~= "table" then error("fdroid.package expects a table") end - local package_name = require_string(spec, "package_name") - local update_candidates = fdroid_update_candidates() + local host_spec = { + package_name = require_string(spec, "package_name"), + endpoint_id = optional_string(spec, "endpoint_id"), + } + local result = fdroid_update_candidates()(host_spec) return package_version { source_priority = { "fdroid" }, - updates = update_candidates { - package_name = package_name, - }, + updates = result.candidates, } end diff --git a/crates/getter-core/src/luaclass/github_android_apk.lua b/crates/getter-core/src/luaclass/github_android_apk.lua index 33d8791..753f7f0 100644 --- a/crates/getter-core/src/luaclass/github_android_apk.lua +++ b/crates/getter-core/src/luaclass/github_android_apk.lua @@ -8,6 +8,20 @@ local function require_string(table, field) return value end +local function optional_string(table, field) + local value = table[field] + if value == nil then + return nil + end + if type(value) ~= "string" then + error("github_android.package field '" .. field .. "' must be a string") + end + if value == "" then + return nil + end + return value +end + local function optional_table(table, field) local value = table[field] if value == nil then @@ -31,35 +45,38 @@ local function optional_boolean(table, field) end local function github_release_candidates() - if type(getter_dev) ~= "table" or type(getter_dev.github_release_candidates) ~= "function" then - error("luaclass.github_android_apk requires operation-installed getter_dev.github_release_candidates") + if type(getter) ~= "table" + or type(getter.provider) ~= "table" + or type(getter.provider.github) ~= "table" + or type(getter.provider.github.release_candidates) ~= "function" then + error("luaclass.github_android_apk requires operation-installed getter.provider.github.release_candidates") end - return getter_dev.github_release_candidates + return getter.provider.github.release_candidates end function github_android.package(spec) if type(spec) ~= "table" then error("github_android.package expects a table") end - local owner = require_string(spec, "owner") - local repo = require_string(spec, "repo") - local release_candidates = github_release_candidates() - local result = { + local host_spec = { + owner = require_string(spec, "owner"), + repo = require_string(spec, "repo"), + asset = optional_table(spec, "asset"), + include_prereleases = optional_boolean(spec, "include_prereleases"), + endpoint_id = optional_string(spec, "endpoint_id"), + } + local result = github_release_candidates()(host_spec) + local package = { name = spec.name, source_priority = { "github" }, - updates = release_candidates { - owner = owner, - repo = repo, - asset = optional_table(spec, "asset"), - include_prereleases = optional_boolean(spec, "include_prereleases"), - }, + updates = result.candidates, } if spec.android_package ~= nil then - result.installed = { + package.installed = { { kind = "android_package", package_name = require_string(spec, "android_package") }, } end - return package_version(result) + return package_version(package) end return github_android diff --git a/crates/getter-core/src/runtime.rs b/crates/getter-core/src/runtime.rs index 3dd4b7c..992978f 100644 --- a/crates/getter-core/src/runtime.rs +++ b/crates/getter-core/src/runtime.rs @@ -707,7 +707,7 @@ mod tests { assert_eq!(task.task_id, "task-1"); assert_eq!(task.status, RuntimeTaskStatus::Queued); - assert_eq!(task.capabilities.cancel, true); + assert!(task.capabilities.cancel); let error = runtime.submit_action(&action.action_id).unwrap_err(); assert_eq!(error.code(), "action.not_found"); assert_eq!(notifications.lock().unwrap().len(), 1); @@ -729,9 +729,9 @@ mod tests { TaskPhaseReason::InstallHandoff ) ); - assert_eq!(waiting.capabilities.pause, false); - assert_eq!(waiting.capabilities.resume, false); - assert_eq!(waiting.capabilities.cancel, true); + assert!(!waiting.capabilities.pause); + assert!(!waiting.capabilities.resume); + assert!(waiting.capabilities.cancel); let completed = runtime .user_result(&task_id, UserResult::Accepted, None) @@ -755,7 +755,7 @@ mod tests { .unwrap(); assert_eq!(failed.status, RuntimeTaskStatus::Failed); - assert_eq!(failed.capabilities.retry, true); + assert!(failed.capabilities.retry); assert_eq!( failed.current_diagnostic.as_ref().unwrap().code, "user.rejected" @@ -798,8 +798,8 @@ mod tests { let paused = runtime.pause_task(&task_id).unwrap(); assert_eq!(paused.status, RuntimeTaskStatus::Paused); - assert_eq!(paused.capabilities.resume, true); - assert_eq!(paused.capabilities.cancel, true); + assert!(paused.capabilities.resume); + assert!(paused.capabilities.cancel); let resumed = runtime.resume_task(&task_id).unwrap(); assert_eq!(resumed.status, RuntimeTaskStatus::Running); diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index e81dab9..81c2e82 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true [features] default = [] lua = ["getter-core/lua", "dep:mlua"] -lua-provider-host-dev = ["lua", "getter-core/provider-luaclass-dev"] +lua-provider-host-dev = ["lua"] [dependencies] json_comments = "0.2" diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 24101c7..9ad0aa3 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -75,8 +75,8 @@ const PROVIDER_CACHE_PROVENANCE_MISSING: &str = "package.provider.cache_provenan /// /// This is a development/provider-host tracer, not the stable package-eval or /// product update-check API. Repository packages can exercise repository-local -/// `luaclass/` modules that call `getter_dev.fdroid_update_candidates { ... }`, -/// while catalog parsing/cache behavior stays in `getter-operations`. +/// or built-in `luaclass/` modules while catalog parsing/cache behavior stays +/// in `getter-operations`. pub fn fdroid_package_eval_json( data_dir: &Path, request_json: &str, @@ -174,8 +174,8 @@ struct FdroidProviderHostConfig { /// /// This is a development/provider-host tracer, not the stable package-eval or /// product update-check API. Repository packages can exercise repository-local -/// `luaclass/` modules that call `getter_dev.github_release_candidates { ... }`, -/// while release parsing/cache behavior stays in `getter-operations`. +/// or built-in `luaclass/` modules while release parsing/cache behavior stays +/// in `getter-operations`. pub fn github_package_eval_json( data_dir: &Path, request_json: &str, @@ -407,29 +407,42 @@ struct GithubProviderHostConfig { } fn install_fdroid_dev_host(lua: &Lua, config: FdroidDevHostConfig) -> mlua::Result<()> { + let provider_config = FdroidProviderHostConfig { + cache_db_path: config.cache_db_path.clone(), + endpoint: config.endpoint.clone(), + mode: config.mode, + index_xml: config.index_xml.clone(), + manifest: None, + }; + let provider_calls = Rc::clone(&config.provider_calls); + let update_candidates = lua.create_function(move |lua, spec: Table| { + let result = fdroid_update_candidates_envelope(lua, spec, provider_config.clone())?; + provider_calls + .borrow_mut() + .push(provider_call_from_envelope(&result)?); + Ok(result) + })?; + + let globals = lua.globals(); + let getter = nested_table(lua, globals.clone(), "getter")?; + let provider = nested_table(lua, getter, "provider")?; + let fdroid_table = nested_table(lua, provider, "fdroid")?; + fdroid_table.set("update_candidates", update_candidates.clone())?; + + let getter_builtin = nested_table(lua, globals.clone(), "getter_builtin")?; + let builtin_provider = nested_table(lua, getter_builtin, "provider")?; + let builtin_fdroid = nested_table(lua, builtin_provider, "fdroid")?; + builtin_fdroid.set("update_candidates", update_candidates.clone())?; + let host = lua.create_table()?; host.set( "fdroid_update_candidates", - lua.create_function(move |lua, spec: Table| { - let result = fdroid_update_candidates_envelope( - lua, - spec, - FdroidProviderHostConfig { - cache_db_path: config.cache_db_path.clone(), - endpoint: config.endpoint.clone(), - mode: config.mode, - index_xml: config.index_xml.clone(), - manifest: None, - }, - )?; - config - .provider_calls - .borrow_mut() - .push(provider_call_from_envelope(&result)?); + lua.create_function(move |_, spec: Table| { + let result: Table = update_candidates.call(spec)?; envelope_candidates(result) })?, )?; - lua.globals().set("getter_dev", host) + globals.set("getter_dev", host) } struct FdroidUpdateHostRequest { @@ -450,30 +463,43 @@ impl FdroidUpdateHostRequest { } fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Result<()> { + let provider_config = GithubProviderHostConfig { + cache_db_path: config.cache_db_path.clone(), + api_base_url: config.api_base_url.clone(), + mode: config.mode, + releases_json: config.releases_json.clone(), + default_include_prereleases: config.default_include_prereleases, + manifest: None, + }; + let provider_calls = Rc::clone(&config.provider_calls); + let release_candidates = lua.create_function(move |lua, spec: Table| { + let result = github_release_candidates_envelope(lua, spec, provider_config.clone())?; + provider_calls + .borrow_mut() + .push(provider_call_from_envelope(&result)?); + Ok(result) + })?; + + let globals = lua.globals(); + let getter = nested_table(lua, globals.clone(), "getter")?; + let provider = nested_table(lua, getter, "provider")?; + let github_table = nested_table(lua, provider, "github")?; + github_table.set("release_candidates", release_candidates.clone())?; + + let getter_builtin = nested_table(lua, globals.clone(), "getter_builtin")?; + let builtin_provider = nested_table(lua, getter_builtin, "provider")?; + let builtin_github = nested_table(lua, builtin_provider, "github")?; + builtin_github.set("release_candidates", release_candidates.clone())?; + let host = lua.create_table()?; host.set( "github_release_candidates", - lua.create_function(move |lua, spec: Table| { - let result = github_release_candidates_envelope( - lua, - spec, - GithubProviderHostConfig { - cache_db_path: config.cache_db_path.clone(), - api_base_url: config.api_base_url.clone(), - mode: config.mode, - releases_json: config.releases_json.clone(), - default_include_prereleases: config.default_include_prereleases, - manifest: None, - }, - )?; - config - .provider_calls - .borrow_mut() - .push(provider_call_from_envelope(&result)?); + lua.create_function(move |_, spec: Table| { + let result: Table = release_candidates.call(spec)?; envelope_candidates(result) })?, )?; - lua.globals().set("getter_dev", host) + globals.set("getter_dev", host) } fn install_stable_provider_host( @@ -1104,7 +1130,7 @@ mod tests { ]"#; #[test] - fn fdroid_package_eval_uses_dev_builtin_luaclass_and_provider_cache() { + fn fdroid_package_eval_uses_builtin_luaclass_and_provider_cache() { let temp = tempfile::tempdir().unwrap(); let data_dir = temp.path(); write_fdroid_package_fixture(data_dir, "org.fdroid.fdroid"); @@ -1194,7 +1220,7 @@ mod tests { } #[test] - fn github_package_eval_uses_dev_builtin_luaclass_and_provider_cache() { + fn github_package_eval_uses_builtin_luaclass_and_provider_cache() { let temp = tempfile::tempdir().unwrap(); let data_dir = temp.path(); write_github_package_fixture(data_dir, "[.]apk$"); @@ -1439,6 +1465,85 @@ return package_version { ); } + #[test] + fn stable_provider_builtin_fdroid_luaclass_calls_stable_host() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { + package_name = "org.fdroid.fdroid", +} +"#, + Some(FDROID_INDEX_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["provider"], FDROID_PROVIDER_ID); + assert_eq!(result["provider_calls"][0]["request"], "catalog"); + assert_eq!(result["package"]["source_priority"], json!(["fdroid"])); + assert_eq!(result["package"]["updates"][0]["version"], "1.20.0"); + assert_eq!(result["package"]["updates"][0]["source"], "fdroid"); + } + + #[test] + fn stable_provider_builtin_github_luaclass_calls_stable_host() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + android_package = "org.fdroid.fdroid", + owner = "f-droid", + repo = "fdroidclient", + asset = { include = "[.]apk$" }, +} +"#, + Some(GITHUB_RELEASES_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "github_releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["provider"], GITHUB_PROVIDER_ID); + assert_eq!(result["provider_calls"][0]["request"], "releases"); + assert_eq!(result["package"]["name"], "F-Droid"); + assert_eq!( + result["package"]["installed"][0]["package_name"], + "org.fdroid.fdroid" + ); + assert_eq!(result["package"]["source_priority"], json!(["github"])); + assert_eq!(result["package"]["updates"][0]["version"], "v1.20.0"); + assert_eq!(result["package"]["updates"][0]["source"], "github"); + } + #[test] fn stable_provider_host_does_not_install_latest_commit_shape() { let temp = tempfile::tempdir().unwrap(); @@ -1662,7 +1767,7 @@ end } #[test] - fn provider_package_eval_prefers_repository_luaclass_over_dev_builtin() { + fn provider_package_eval_prefers_repository_luaclass_over_builtin() { let temp = tempfile::tempdir().unwrap(); let data_dir = temp.path(); write_fdroid_package_fixture(data_dir, "org.fdroid.fdroid"); From 05b0a85358b6082c2778736febbfa1a9249fcb14 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 14:39:25 +0800 Subject: [PATCH 56/71] fix(lua): validate provider endpoint ids --- .../src/lua_provider_host.rs | 173 +++++++++++++++++- 1 file changed, 171 insertions(+), 2 deletions(-) diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 9ad0aa3..bf26f73 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -70,6 +70,7 @@ pub enum LuaProviderHostOperationError { const FDROID_PACKAGE_NOT_FOUND: &str = "provider.fdroid.package_not_found"; const PROVIDER_RESPONSE_NOT_IN_MANIFEST: &str = "package.provider.response_not_in_manifest"; const PROVIDER_CACHE_PROVENANCE_MISSING: &str = "package.provider.cache_provenance_missing"; +const DEFAULT_GITHUB_ENDPOINT_ID: &str = "github"; /// Evaluates a package with a fixture-backed F-Droid catalog host binding. /// @@ -183,6 +184,10 @@ pub fn github_package_eval_json( let request: GithubPackageEvalRequest = serde_json::from_str(request_json) .map_err(|source| LuaProviderHostOperationError::InvalidRequest(source.to_string()))?; let mode = provider_cache_mode(request.mode.as_deref())?; + let endpoint_id = request + .endpoint_id + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_ENDPOINT_ID.to_owned()); let api_base_url = request .api_base_url .filter(|value| !value.trim().is_empty()) @@ -215,6 +220,7 @@ pub fn github_package_eval_json( lua, GithubDevHostConfig { cache_db_path, + endpoint_id, api_base_url, mode, releases_json, @@ -256,6 +262,10 @@ pub fn stable_provider_package_eval_json( .filter(|value| !value.trim().is_empty()) .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), }; + let github_endpoint_id = request + .github_endpoint_id + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_ENDPOINT_ID.to_owned()); let github_api_base_url = request .github_api_base_url .filter(|value| !value.trim().is_empty()) @@ -303,6 +313,7 @@ pub fn stable_provider_package_eval_json( }, github: GithubProviderHostConfig { cache_db_path: data_dir.join("cache.db"), + endpoint_id: github_endpoint_id, api_base_url: github_api_base_url, mode, releases_json: github_releases_json, @@ -336,6 +347,8 @@ struct StableProviderPackageEvalRequest { #[serde(default)] fdroid_index_xml: Option, #[serde(default)] + github_endpoint_id: Option, + #[serde(default)] github_api_base_url: Option, #[serde(default)] github_releases_json: Option, @@ -378,6 +391,8 @@ struct GithubPackageEvalRequest { repository_id: RepositoryId, package_id: PackageId, #[serde(default)] + endpoint_id: Option, + #[serde(default)] api_base_url: Option, #[serde(default)] mode: Option, @@ -389,6 +404,7 @@ struct GithubPackageEvalRequest { struct GithubDevHostConfig { cache_db_path: PathBuf, + endpoint_id: String, api_base_url: String, mode: ProviderCacheMode, releases_json: Option, @@ -399,6 +415,7 @@ struct GithubDevHostConfig { #[derive(Clone)] struct GithubProviderHostConfig { cache_db_path: PathBuf, + endpoint_id: String, api_base_url: String, mode: ProviderCacheMode, releases_json: Option, @@ -447,6 +464,7 @@ fn install_fdroid_dev_host(lua: &Lua, config: FdroidDevHostConfig) -> mlua::Resu struct FdroidUpdateHostRequest { package_name: String, + endpoint_id: Option, } impl FdroidUpdateHostRequest { @@ -458,6 +476,7 @@ impl FdroidUpdateHostRequest { .ok_or_else(|| { mlua::Error::external("F-Droid provider host missing package_name") })?, + endpoint_id: optional_lua_string_field(table, "endpoint_id", "F-Droid provider host")?, }) } } @@ -465,6 +484,7 @@ impl FdroidUpdateHostRequest { fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Result<()> { let provider_config = GithubProviderHostConfig { cache_db_path: config.cache_db_path.clone(), + endpoint_id: config.endpoint_id.clone(), api_base_url: config.api_base_url.clone(), mode: config.mode, releases_json: config.releases_json.clone(), @@ -584,6 +604,7 @@ struct GithubReleaseHostRequest { repo: String, asset_filter: GithubAssetFilter, include_prereleases: Option, + endpoint_id: Option, } impl GithubReleaseHostRequest { @@ -593,6 +614,11 @@ impl GithubReleaseHostRequest { repo: required_lua_string(table, "repo")?, asset_filter: asset_filter_from_lua(table.get("asset")?)?, include_prereleases: optional_lua_bool(table, "include_prereleases")?, + endpoint_id: optional_lua_string_field( + table, + "endpoint_id", + "GitHub release provider host", + )?, }) } } @@ -603,6 +629,13 @@ fn fdroid_update_candidates_envelope( config: FdroidProviderHostConfig, ) -> mlua::Result
{ let request = FdroidUpdateHostRequest::from_lua_table(&spec)?; + if let Some(endpoint_id) = &request.endpoint_id { + if endpoint_id != &config.endpoint.endpoint_id { + return Err(mlua::Error::external(format!( + "F-Droid provider host endpoint_id '{endpoint_id}' is not installed" + ))); + } + } let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; let endpoint = config.endpoint; let refresh_fixture = config.index_xml; @@ -671,6 +704,14 @@ fn github_release_candidates_envelope( config: GithubProviderHostConfig, ) -> mlua::Result
{ let request = GithubReleaseHostRequest::from_lua_table(&spec)?; + if let Some(endpoint_id) = &request.endpoint_id { + if endpoint_id != &config.endpoint_id { + return Err(mlua::Error::external(format!( + "GitHub release provider host endpoint_id '{endpoint_id}' is not installed" + ))); + } + } + let endpoint_id = config.endpoint_id.clone(); let db = CacheDb::open(&config.cache_db_path).map_err(mlua::Error::external)?; let provider_config = GithubReleaseConfig { api_base_url: config.api_base_url, @@ -723,6 +764,7 @@ fn github_release_candidates_envelope( json!({ "provider": GITHUB_PROVIDER_ID, "request": "releases", + "endpoint_id": endpoint_id, "owner": result.config.owner, "repo": result.config.repo, "cache_key": result.cache_key, @@ -1032,6 +1074,24 @@ fn optional_lua_bool(table: &Table, field: &'static str) -> mlua::Result mlua::Result> { + match table.get::(field)? { + LuaValue::Nil => Ok(None), + LuaValue::String(value) => { + let value = value.to_str()?; + let value = value.trim(); + Ok((!value.is_empty()).then(|| value.to_owned())) + } + _ => Err(mlua::Error::external(format!( + "{context} {field} must be a string" + ))), + } +} + fn asset_filter_from_lua(value: LuaValue) -> mlua::Result { match value { LuaValue::Nil => Ok(GithubAssetFilter::default()), @@ -1238,6 +1298,7 @@ mod tests { assert_eq!(first["operation"], "github.package_eval.fixture"); assert_eq!(first["provider_calls"][0]["source"], "refreshed"); + assert_eq!(first["provider_calls"][0]["endpoint_id"], "github"); assert_eq!(first["package"]["name"], "F-Droid"); assert_eq!(first["package"]["source_priority"], json!(["github"])); assert_eq!(first["package"]["updates"][0]["version"], "v1.20.0"); @@ -1267,6 +1328,28 @@ mod tests { assert_eq!(second["package"]["updates"][0]["version"], "v1.20.0"); } + #[test] + fn github_package_eval_uses_configured_endpoint_id() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_github_package_fixture_with_endpoint_id(data_dir, "[.]apk$", Some("mirror")); + + let result = github_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "endpoint_id": "mirror", + "releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["endpoint_id"], "mirror"); + assert_eq!(result["package"]["updates"][0]["source"], "github"); + } + #[test] fn github_package_eval_reports_asset_filter_diagnostics() { let temp = tempfile::tempdir().unwrap(); @@ -1495,11 +1578,46 @@ return fdroid.package { assert_eq!(result["provider_calls"][0]["provider"], FDROID_PROVIDER_ID); assert_eq!(result["provider_calls"][0]["request"], "catalog"); + assert_eq!(result["provider_calls"][0]["endpoint_id"], "official"); assert_eq!(result["package"]["source_priority"], json!(["fdroid"])); assert_eq!(result["package"]["updates"][0]["version"], "1.20.0"); assert_eq!(result["package"]["updates"][0]["source"], "fdroid"); } + #[test] + fn stable_provider_builtin_fdroid_rejects_uninstalled_endpoint_id() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { + package_name = "org.fdroid.fdroid", + endpoint_id = "mirror", +} +"#, + Some(FDROID_INDEX_FIXTURE), + false, + ); + + let err = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap_err(); + + assert!(err + .to_string() + .contains("F-Droid provider host endpoint_id 'mirror' is not installed")); + } + #[test] fn stable_provider_builtin_github_luaclass_calls_stable_host() { let temp = tempfile::tempdir().unwrap(); @@ -1534,6 +1652,7 @@ return github_android.package { assert_eq!(result["provider_calls"][0]["provider"], GITHUB_PROVIDER_ID); assert_eq!(result["provider_calls"][0]["request"], "releases"); + assert_eq!(result["provider_calls"][0]["endpoint_id"], "github"); assert_eq!(result["package"]["name"], "F-Droid"); assert_eq!( result["package"]["installed"][0]["package_name"], @@ -1544,6 +1663,44 @@ return github_android.package { assert_eq!(result["package"]["updates"][0]["source"], "github"); } + #[test] + fn stable_provider_builtin_github_rejects_uninstalled_endpoint_id() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + android_package = "org.fdroid.fdroid", + owner = "f-droid", + repo = "fdroidclient", + asset = { include = "[.]apk$" }, + endpoint_id = "github-mirror", +} +"#, + Some(GITHUB_RELEASES_FIXTURE), + false, + ); + + let err = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "github_releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap_err(); + + assert!(err + .to_string() + .contains("GitHub release provider host endpoint_id 'github-mirror' is not installed")); + } + #[test] fn stable_provider_host_does_not_install_latest_commit_shape() { let temp = tempfile::tempdir().unwrap(); @@ -1850,6 +2007,14 @@ return fdroid.package { } fn write_github_package_fixture(data_dir: &std::path::Path, asset_include: &str) { + write_github_package_fixture_with_endpoint_id(data_dir, asset_include, None); + } + + fn write_github_package_fixture_with_endpoint_id( + data_dir: &std::path::Path, + asset_include: &str, + endpoint_id: Option<&str>, + ) { let repo_root = data_dir.join("repo/official"); let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); fs::create_dir_all(&package_dir).unwrap(); @@ -1861,6 +2026,9 @@ return fdroid.package { }"#, ) .unwrap(); + let endpoint_line = endpoint_id + .map(|endpoint_id| format!(" endpoint_id = \"{endpoint_id}\",\n")) + .unwrap_or_default(); let version_script = r#"#!/bin/upa-lua v1 local github_android = require("luaclass.github_android_apk") return github_android.package { @@ -1868,9 +2036,10 @@ return github_android.package { owner = "f-droid", repo = "fdroidclient", asset = { include = "__ASSET_INCLUDE__" }, -} +__ENDPOINT_LINE__} "# - .replace("__ASSET_INCLUDE__", asset_include); + .replace("__ASSET_INCLUDE__", asset_include) + .replace("__ENDPOINT_LINE__", &endpoint_line); fs::write(package_dir.join("9999.lua"), version_script).unwrap(); register_official_repository(data_dir, &repo_root); } From 15ee5362e09b89606b9daa34d08b66f321eb8ab3 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 14:56:23 +0800 Subject: [PATCH 57/71] test(autogen): lock fdroid generated script boundary --- .../getter-operations/src/fdroid_autogen.rs | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/crates/getter-operations/src/fdroid_autogen.rs b/crates/getter-operations/src/fdroid_autogen.rs index 3026de4..c0208b1 100644 --- a/crates/getter-operations/src/fdroid_autogen.rs +++ b/crates/getter-operations/src/fdroid_autogen.rs @@ -454,26 +454,19 @@ mod tests { assert_eq!(record["input"]["endpoint_url"], "https://f-droid.org/repo"); assert_eq!(record["input"]["package_name"], "org.fdroid.fdroid"); let files = candidate["files"].as_array().unwrap(); - assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" - && file["content"] - .as_str() - .unwrap() - .contains("version_code = 1020000"))); - assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" - && file["content"] - .as_str() - .unwrap() - .contains("return fdroid.package"))); - assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" - && file["content"] - .as_str() - .unwrap() - .contains("package_name = \"org.fdroid.fdroid\""))); - assert!(files.iter().any(|file| file["relative_path"] == "9999.lua" - && file["content"] - .as_str() - .unwrap() - .contains("https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk"))); + let version_lua = files + .iter() + .find(|file| file["relative_path"] == "9999.lua") + .and_then(|file| file["content"].as_str()) + .unwrap(); + assert!(version_lua.contains("local catalog_package =")); + assert!(version_lua.contains("local fdroid = {}")); + assert!(version_lua.contains("version_code = 1020000")); + assert!(version_lua.contains("return fdroid.package")); + assert!(version_lua.contains("package_name = \"org.fdroid.fdroid\"")); + assert!(version_lua.contains("https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk")); + assert!(!version_lua.contains("require(\"luaclass.fdroid_android\")")); + assert!(!version_lua.contains("getter.provider")); assert!(files.iter().any(|file| file["relative_path"] == "Manifest" && file["content"].as_str().unwrap().is_empty())); assert_eq!( From 3996cbec98fbcdb0d9b445e54ba030b8a584e92f Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 15:00:13 +0800 Subject: [PATCH 58/71] test(lua): cover configured provider endpoints --- .../src/lua_provider_host.rs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index bf26f73..20f0562 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -1584,6 +1584,40 @@ return fdroid.package { assert_eq!(result["package"]["updates"][0]["source"], "fdroid"); } + #[test] + fn stable_provider_builtin_fdroid_accepts_configured_endpoint_id() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/f-droid/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { + package_name = "org.fdroid.fdroid", + endpoint_id = "mirror", +} +"#, + Some(FDROID_INDEX_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "fdroid_endpoint_id": "mirror", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["endpoint_id"], "mirror"); + assert_eq!(result["package"]["updates"][0]["source"], "fdroid"); + } + #[test] fn stable_provider_builtin_fdroid_rejects_uninstalled_endpoint_id() { let temp = tempfile::tempdir().unwrap(); @@ -1663,6 +1697,44 @@ return github_android.package { assert_eq!(result["package"]["updates"][0]["source"], "github"); } + #[test] + fn stable_provider_builtin_github_accepts_configured_endpoint_id() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + write_stable_provider_package_fixture( + data_dir, + "android/app/org.fdroid.fdroid", + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + android_package = "org.fdroid.fdroid", + owner = "f-droid", + repo = "fdroidclient", + asset = { include = "[.]apk$" }, + endpoint_id = "github-mirror", +} +"#, + Some(GITHUB_RELEASES_FIXTURE), + false, + ); + + let result = stable_provider_package_eval_json( + data_dir, + &json!({ + "repository_id": "official", + "package_id": "android/app/org.fdroid.fdroid", + "github_endpoint_id": "github-mirror", + "github_releases_json": GITHUB_RELEASES_FIXTURE + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(result["provider_calls"][0]["endpoint_id"], "github-mirror"); + assert_eq!(result["package"]["updates"][0]["source"], "github"); + } + #[test] fn stable_provider_builtin_github_rejects_uninstalled_endpoint_id() { let temp = tempfile::tempdir().unwrap(); From 44cf664fa2ae3b98a3d762a40fd30c402233f78b Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 16:52:11 +0800 Subject: [PATCH 59/71] feat(runtime): use provider-backed update checks --- crates/getter-operations/src/lib.rs | 4 +- .../src/lua_provider_host.rs | 227 ++++++--- crates/getter-operations/src/runtime.rs | 456 +++++++++++++++++- 3 files changed, 598 insertions(+), 89 deletions(-) diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index f3ddcb5..8d7fc30 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -14,9 +14,9 @@ pub mod legacy_room; #[cfg(feature = "lua")] #[doc(hidden)] pub mod lua_http_policy; -#[cfg(feature = "lua-provider-host-dev")] +#[cfg(feature = "lua")] #[doc(hidden)] -pub mod lua_provider_host; +mod lua_provider_host; #[cfg(feature = "lua")] #[doc(hidden)] pub mod lua_runtime_hooks; diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 20f0562..36c55fc 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -1,10 +1,10 @@ //! Operation-installed Lua provider host bindings. //! //! This module is an implementation bridge toward ADR-0012 provider-backed Lua -//! modules. It exercises higher-level getter operations installing host -//! functions for package evaluation without making `getter-core` depend on -//! provider cache/storage crates. The stable `getter.provider.*` harness here -//! is still fixture-backed, dev-feature-gated, and hidden from product APIs. +//! modules. It lets higher-level getter operations install host functions for +//! package evaluation without making `getter-core` depend on provider +//! cache/storage crates. The stable `getter.provider.*` bindings are now shared +//! by product runtime update checks and internal fixture-backed harness tests. use crate::fdroid_catalog::{ read_or_refresh_fdroid_catalog, FdroidCatalogOperationError, FdroidEndpointConfig, @@ -20,17 +20,24 @@ use crate::provider_cache::{ PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1, }; use getter_core::lua::{evaluate_package_directory_script_with_host_bindings, LuaPackageError}; +#[cfg(feature = "lua-provider-host-dev")] +use getter_core::repository::RepositoryPackageDirectoryLayout; use getter_core::repository::{ PackageDirectory, PackageDirectoryMetadata, PackageLuaPermission, PackageVersionScript, - RepositoryLoadError, RepositoryPackageDirectoryLayout, PACKAGE_MANIFEST_FILE, + RepositoryLoadError, PACKAGE_MANIFEST_FILE, }; -use getter_core::{PackageId, RepositoryId, ResolvedPackage}; +#[cfg(feature = "lua-provider-host-dev")] +use getter_core::PackageId; +use getter_core::{RepositoryId, ResolvedPackage}; use getter_providers::{ github_release_update_candidates, FdroidEndpoint, GithubAssetFilter, GithubProviderError, GithubRelease, GithubReleaseCandidateOptions, }; -use getter_storage::{CacheDb, MainDb, StorageError, StoredRepository}; +use getter_storage::{CacheDb, StorageError}; +#[cfg(feature = "lua-provider-host-dev")] +use getter_storage::{MainDb, StoredRepository}; use mlua::{Lua, Table, Value as LuaValue}; +#[cfg(feature = "lua-provider-host-dev")] use serde::Deserialize; use serde_json::{json, Map, Value}; use sha2::{Digest, Sha512}; @@ -78,6 +85,7 @@ const DEFAULT_GITHUB_ENDPOINT_ID: &str = "github"; /// product update-check API. Repository packages can exercise repository-local /// or built-in `luaclass/` modules while catalog parsing/cache behavior stays /// in `getter-operations`. +#[cfg(feature = "lua-provider-host-dev")] pub fn fdroid_package_eval_json( data_dir: &Path, request_json: &str, @@ -140,6 +148,7 @@ pub fn fdroid_package_eval_json( })) } +#[cfg(feature = "lua-provider-host-dev")] #[derive(Debug, Deserialize)] struct FdroidPackageEvalRequest { repository_id: RepositoryId, @@ -154,6 +163,7 @@ struct FdroidPackageEvalRequest { index_xml: Option, } +#[cfg(feature = "lua-provider-host-dev")] struct FdroidDevHostConfig { cache_db_path: PathBuf, endpoint: FdroidEndpointConfig, @@ -177,6 +187,7 @@ struct FdroidProviderHostConfig { /// product update-check API. Repository packages can exercise repository-local /// or built-in `luaclass/` modules while release parsing/cache behavior stays /// in `getter-operations`. +#[cfg(feature = "lua-provider-host-dev")] pub fn github_package_eval_json( data_dir: &Path, request_json: &str, @@ -245,6 +256,7 @@ pub fn github_package_eval_json( /// This is an internal Slice 1 harness for ADR-0012 provider host API v1. It /// installs the stable namespace and runtime hooks, but still uses fixture data /// and remains hidden from CLI/native/Flutter product APIs. +#[cfg(feature = "lua-provider-host-dev")] pub fn stable_provider_package_eval_json( data_dir: &Path, request_json: &str, @@ -252,24 +264,6 @@ pub fn stable_provider_package_eval_json( let request: StableProviderPackageEvalRequest = serde_json::from_str(request_json) .map_err(|source| LuaProviderHostOperationError::InvalidRequest(source.to_string()))?; let mode = provider_cache_mode(request.mode.as_deref())?; - let endpoint = FdroidEndpointConfig { - endpoint_id: request - .fdroid_endpoint_id - .filter(|value| !value.trim().is_empty()) - .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_ID.to_owned()), - endpoint_url: request - .fdroid_endpoint_url - .filter(|value| !value.trim().is_empty()) - .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), - }; - let github_endpoint_id = request - .github_endpoint_id - .filter(|value| !value.trim().is_empty()) - .unwrap_or_else(|| DEFAULT_GITHUB_ENDPOINT_ID.to_owned()); - let github_api_base_url = request - .github_api_base_url - .filter(|value| !value.trim().is_empty()) - .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()); let main_db = MainDb::open(data_dir.join("main.db"))?; let repository = find_repository(&main_db, &request.repository_id)?; let repository_path = repo_path(&repository)?; @@ -282,58 +276,34 @@ pub fn stable_provider_package_eval_json( })?; let metadata = layout.package_metadata(package_directory)?; let script = layout.unambiguous_version_script(package_directory)?; - let provider_calls = Rc::new(RefCell::new(Vec::new())); - let runtime_hooks = Rc::new(RefCell::new(Vec::new())); - let fdroid_index_xml = request.fdroid_index_xml; - let github_releases_json = request.github_releases_json; - let default_include_prereleases = request.github_include_prereleases; - let manifest = if metadata - .permissions_for(&script.file_name) - .contains(&PackageLuaPermission::AllowFreeNetwork) - { - None - } else { - Some(PackageManifest::load( - package_directory.path.join(PACKAGE_MANIFEST_FILE), - )?) - }; - let package = evaluate_with_stable_provider_host( + let result = evaluate_provider_backed_package( data_dir, &repository.id, package_directory, &metadata, script, - StableProviderHostEvalConfig { - fdroid: FdroidProviderHostConfig { - cache_db_path: data_dir.join("cache.db"), - endpoint, - mode, - index_xml: fdroid_index_xml, - manifest: manifest.clone(), - }, - github: GithubProviderHostConfig { - cache_db_path: data_dir.join("cache.db"), - endpoint_id: github_endpoint_id, - api_base_url: github_api_base_url, - mode, - releases_json: github_releases_json, - default_include_prereleases, - manifest, - }, - provider_calls: Rc::clone(&provider_calls), - runtime_hooks: Rc::clone(&runtime_hooks), + ProviderBackedPackageEvalConfig { + mode, + fdroid_endpoint_id: request.fdroid_endpoint_id, + fdroid_endpoint_url: request.fdroid_endpoint_url, + fdroid_index_xml: request.fdroid_index_xml, + github_endpoint_id: request.github_endpoint_id, + github_api_base_url: request.github_api_base_url, + github_releases_json: request.github_releases_json, + github_include_prereleases: request.github_include_prereleases, }, )?; - let package = serde_json::to_value(package)?; + let package = serde_json::to_value(result.package)?; Ok(json!({ "operation": "provider.package_eval.fixture", "package": package, - "provider_calls": provider_calls.borrow().clone(), - "runtime_hooks": runtime_hooks.borrow().clone(), + "provider_calls": result.provider_calls, + "runtime_hooks": result.runtime_hooks, })) } +#[cfg(feature = "lua-provider-host-dev")] #[derive(Debug, Deserialize)] struct StableProviderPackageEvalRequest { repository_id: RepositoryId, @@ -386,6 +356,7 @@ fn evaluate_with_stable_provider_host( .map_err(Into::into) } +#[cfg(feature = "lua-provider-host-dev")] #[derive(Debug, Deserialize)] struct GithubPackageEvalRequest { repository_id: RepositoryId, @@ -402,6 +373,7 @@ struct GithubPackageEvalRequest { include_prereleases: bool, } +#[cfg(feature = "lua-provider-host-dev")] struct GithubDevHostConfig { cache_db_path: PathBuf, endpoint_id: String, @@ -423,6 +395,100 @@ struct GithubProviderHostConfig { manifest: Option, } +#[derive(Debug, Clone)] +pub(crate) struct ProviderBackedPackageEvalConfig { + pub(crate) mode: ProviderCacheMode, + pub(crate) fdroid_endpoint_id: Option, + pub(crate) fdroid_endpoint_url: Option, + pub(crate) fdroid_index_xml: Option, + pub(crate) github_endpoint_id: Option, + pub(crate) github_api_base_url: Option, + pub(crate) github_releases_json: Option, + pub(crate) github_include_prereleases: bool, +} + +pub(crate) struct ProviderBackedPackageEvalResult { + pub(crate) package: ResolvedPackage, + pub(crate) provider_calls: Vec, + pub(crate) runtime_hooks: Vec, +} + +pub(crate) fn evaluate_provider_backed_package( + data_dir: &Path, + repository_id: &RepositoryId, + package_directory: &PackageDirectory, + metadata: &PackageDirectoryMetadata, + script: &PackageVersionScript, + config: ProviderBackedPackageEvalConfig, +) -> Result { + let fdroid_endpoint = FdroidEndpointConfig { + endpoint_id: config + .fdroid_endpoint_id + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_ID.to_owned()), + endpoint_url: config + .fdroid_endpoint_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_FDROID_ENDPOINT_URL.to_owned()), + }; + let github_endpoint_id = config + .github_endpoint_id + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_ENDPOINT_ID.to_owned()); + let github_api_base_url = config + .github_api_base_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()); + let manifest = if metadata + .permissions_for(&script.file_name) + .contains(&PackageLuaPermission::AllowFreeNetwork) + { + None + } else { + Some(PackageManifest::load( + package_directory.path.join(PACKAGE_MANIFEST_FILE), + )?) + }; + let provider_calls = Rc::new(RefCell::new(Vec::new())); + let runtime_hooks = Rc::new(RefCell::new(Vec::new())); + let package = evaluate_with_stable_provider_host( + data_dir, + repository_id, + package_directory, + metadata, + script, + StableProviderHostEvalConfig { + fdroid: FdroidProviderHostConfig { + cache_db_path: data_dir.join("cache.db"), + endpoint: fdroid_endpoint, + mode: config.mode, + index_xml: config.fdroid_index_xml, + manifest: manifest.clone(), + }, + github: GithubProviderHostConfig { + cache_db_path: data_dir.join("cache.db"), + endpoint_id: github_endpoint_id, + api_base_url: github_api_base_url, + mode: config.mode, + releases_json: config.github_releases_json, + default_include_prereleases: config.github_include_prereleases, + manifest, + }, + provider_calls: Rc::clone(&provider_calls), + runtime_hooks: Rc::clone(&runtime_hooks), + }, + )?; + + let provider_calls = provider_calls.borrow().clone(); + let runtime_hooks = runtime_hooks.borrow().clone(); + Ok(ProviderBackedPackageEvalResult { + package, + provider_calls, + runtime_hooks, + }) +} + +#[cfg(feature = "lua-provider-host-dev")] fn install_fdroid_dev_host(lua: &Lua, config: FdroidDevHostConfig) -> mlua::Result<()> { let provider_config = FdroidProviderHostConfig { cache_db_path: config.cache_db_path.clone(), @@ -481,6 +547,7 @@ impl FdroidUpdateHostRequest { } } +#[cfg(feature = "lua-provider-host-dev")] fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Result<()> { let provider_config = GithubProviderHostConfig { cache_db_path: config.cache_db_path.clone(), @@ -641,8 +708,9 @@ fn fdroid_update_candidates_envelope( let refresh_fixture = config.index_xml; let manifest = config.manifest.clone(); let result = read_or_refresh_fdroid_catalog(&db, endpoint, config.mode, || { - let xml = refresh_fixture - .ok_or_else(|| "fixture-backed F-Droid catalog host requires index_xml".to_owned())?; + let xml = refresh_fixture.ok_or_else(|| { + "F-Droid provider host has no cached catalog and no refresh source installed".to_owned() + })?; require_manifest_body(&manifest, FDROID_PROVIDER_ID, &xml) .map_err(|source| source.to_string())?; Ok(xml) @@ -722,7 +790,8 @@ fn github_release_candidates_envelope( let manifest = config.manifest.clone(); let result = read_or_refresh_github_releases(&db, provider_config, config.mode, || { let releases = refresh_fixture.ok_or_else(|| { - "fixture-backed GitHub release host requires releases_json".to_owned() + "GitHub release provider host has no cached releases and no refresh source installed" + .to_owned() })?; require_manifest_body(&manifest, GITHUB_PROVIDER_ID, &releases) .map_err(|source| source.to_string())?; @@ -811,6 +880,7 @@ fn provider_call_from_envelope(envelope: &Table) -> mlua::Result { Ok(Value::Object(object)) } +#[cfg(feature = "lua-provider-host-dev")] fn envelope_candidates(envelope: Table) -> mlua::Result { envelope.get("candidates") } @@ -824,16 +894,28 @@ fn json_object_to_lua(lua: &Lua, value: Value) -> mlua::Result
{ } } +fn lua_integer(value: i64, original: &serde_json::Number) -> mlua::Result { + mlua::Integer::try_from(value) + .map(LuaValue::Integer) + .map_err(|_| { + mlua::Error::external(format!( + "provider host integer {original} cannot be represented" + )) + }) +} + fn json_value_to_lua(lua: &Lua, value: Value) -> mlua::Result { match value { Value::Null => Ok(LuaValue::Nil), Value::Bool(value) => Ok(LuaValue::Boolean(value)), Value::Number(number) => { if let Some(value) = number.as_i64() { - return Ok(LuaValue::Integer(value)); + return lua_integer(value, &number); } - if let Some(value) = number.as_u64().and_then(|value| i64::try_from(value).ok()) { - return Ok(LuaValue::Integer(value)); + if let Some(value) = number.as_u64() { + if let Ok(value) = i64::try_from(value) { + return lua_integer(value, &number); + } } Ok(LuaValue::Number(number.as_f64().ok_or_else(|| { mlua::Error::external(format!( @@ -1020,7 +1102,8 @@ fn reject_unproven_manifest_cache( ))) } -fn provider_cache_mode( +#[cfg(feature = "lua-provider-host-dev")] +pub(crate) fn provider_cache_mode( mode: Option<&str>, ) -> Result { match mode { @@ -1032,6 +1115,7 @@ fn provider_cache_mode( } } +#[cfg(feature = "lua-provider-host-dev")] fn find_repository( db: &MainDb, repository_id: &RepositoryId, @@ -1046,6 +1130,7 @@ fn find_repository( }) } +#[cfg(feature = "lua-provider-host-dev")] fn repo_path(repository: &StoredRepository) -> Result { repository.path.as_ref().map(PathBuf::from).ok_or_else(|| { LuaProviderHostOperationError::InvalidRequest(format!( @@ -1144,7 +1229,7 @@ fn provider_diagnostic_json(diagnostic: &ProviderCacheDiagnostic) -> Value { }) } -#[cfg(test)] +#[cfg(all(test, feature = "lua-provider-host-dev"))] mod tests { use super::*; use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 5a3b94e..d68025c 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -7,11 +7,15 @@ //! Android glue. #[cfg(feature = "lua")] -use getter_core::{ - lua::evaluate_package_directory_script, - repository::{ - package_directory_cache_key, RepositoryLoadError, RepositoryPackageDirectoryLayout, - }, +use crate::lua_provider_host::{ + evaluate_provider_backed_package, LuaProviderHostOperationError, + ProviderBackedPackageEvalConfig, +}; +#[cfg(feature = "lua")] +use crate::provider_cache::ProviderCacheMode; +#[cfg(feature = "lua")] +use getter_core::repository::{ + package_directory_cache_key, RepositoryLoadError, RepositoryPackageDirectoryLayout, }; use getter_core::{ runtime::{ @@ -49,6 +53,9 @@ pub enum RuntimeOperationError { #[cfg(feature = "lua")] #[error("package evaluation failed: {0}")] PackageEval(String), + #[cfg(feature = "lua")] + #[error("provider-backed package evaluation failed: {0}")] + ProviderPackageEval(#[from] LuaProviderHostOperationError), #[error("runtime response serialization failed: {0}")] Serialize(String), } @@ -65,6 +72,19 @@ impl RuntimeOperationError { Self::Repository(_) => "repository.error", #[cfg(feature = "lua")] Self::PackageEval(_) => "package.eval_error", + #[cfg(feature = "lua")] + Self::ProviderPackageEval(error) => match error { + LuaProviderHostOperationError::InvalidRequest(_) => "runtime.invalid_request", + LuaProviderHostOperationError::Storage(_) => "storage.error", + LuaProviderHostOperationError::Repository(_) => "repository.error", + LuaProviderHostOperationError::PackageEval(_) => "package.eval_error", + LuaProviderHostOperationError::Fdroid(_) => "provider.fdroid.error", + LuaProviderHostOperationError::Github(_) + | LuaProviderHostOperationError::GithubProvider(_) => "provider.github.error", + LuaProviderHostOperationError::Serialization(_) => "runtime.serialize_error", + LuaProviderHostOperationError::ReadManifest { .. } + | LuaProviderHostOperationError::InvalidManifest { .. } => "package.manifest_error", + }, Self::Serialize(_) => "runtime.serialize_error", } } @@ -79,7 +99,9 @@ impl RuntimeOperationError { #[cfg(feature = "lua")] Self::Repository(_) => "Getter repository operation failed", #[cfg(feature = "lua")] - Self::PackageEval(_) => "Getter package evaluation failed", + Self::PackageEval(_) | Self::ProviderPackageEval(_) => { + "Getter package evaluation failed" + } Self::Serialize(_) => "Getter runtime response serialization failed", } } @@ -95,6 +117,8 @@ impl RuntimeOperationError { Self::Repository(error) => Some(error.to_string()), #[cfg(feature = "lua")] Self::PackageEval(detail) => Some(detail.clone()), + #[cfg(feature = "lua")] + Self::ProviderPackageEval(error) => Some(error.to_string()), } } } @@ -134,14 +158,15 @@ pub fn issue_action_from_offline_update_check_json( #[cfg(feature = "lua")] pub fn issue_action_from_registered_package_json( runtime: &mut GetterRuntime, + data_dir: &std::path::Path, db: &MainDb, request_json: &str, ) -> Result { let request: RegisteredPackageUpdateActionRequest = parse_request(request_json)?; - let (package, dependency_digest) = evaluate_registered_package(db, &request)?; - let candidates = StaticPackageUpdatesProvider.check_updates(&package); + let evaluated = evaluate_registered_package(data_dir, db, &request)?; + let candidates = StaticPackageUpdatesProvider.check_updates(&evaluated.package); let update = check_updates_offline( - package.id.clone(), + evaluated.package.id.clone(), request.installed_version, candidates, UpdateSelectionPolicy { @@ -156,14 +181,16 @@ pub fn issue_action_from_registered_package_json( actions: update.actions.clone(), lua_object: PackageVersionLuaObject { object_id: format!("package-update:{}", update.package_id), - dependency_digest, + dependency_digest: evaluated.dependency_digest, }, })) }; Ok(json!({ - "package": package, + "package": evaluated.package, "update": update, "action": action.map(issued_action_json), + "provider_calls": evaluated.provider_calls, + "runtime_hooks": evaluated.runtime_hooks, })) } @@ -365,11 +392,20 @@ struct CleanTasksRequest { mode: Option, } +#[cfg(feature = "lua")] +struct RegisteredPackageEvaluation { + package: getter_core::ResolvedPackage, + dependency_digest: String, + provider_calls: Vec, + runtime_hooks: Vec, +} + #[cfg(feature = "lua")] fn evaluate_registered_package( + data_dir: &std::path::Path, db: &MainDb, request: &RegisteredPackageUpdateActionRequest, -) -> Result<(getter_core::ResolvedPackage, String), RuntimeOperationError> { +) -> Result { let repositories = db.repositories()?; let mut missing_path = None; for repository in repositories { @@ -391,15 +427,34 @@ fn evaluate_registered_package( }; let metadata = layout.package_metadata(package_directory)?; let script = layout.unambiguous_version_script(package_directory)?; - let package = - evaluate_package_directory_script(&repository.id, package_directory, &metadata, script) - .map_err(|source| RuntimeOperationError::PackageEval(source.to_string()))?; + let provider_eval = evaluate_provider_backed_package( + data_dir, + &repository.id, + package_directory, + &metadata, + script, + ProviderBackedPackageEvalConfig { + mode: ProviderCacheMode::UseCached, + fdroid_endpoint_id: None, + fdroid_endpoint_url: None, + fdroid_index_xml: None, + github_endpoint_id: None, + github_api_base_url: None, + github_releases_json: None, + github_include_prereleases: false, + }, + )?; let cache_key = package_directory_cache_key(&repository.id, package_directory, script)?; let dependency_digest = format!( "repo:{}:package:{}:hash:{}", cache_key.repository_id, cache_key.package_id, cache_key.package_file_hash ); - return Ok((package, dependency_digest)); + return Ok(RegisteredPackageEvaluation { + package: provider_eval.package, + dependency_digest, + provider_calls: provider_eval.provider_calls, + runtime_hooks: provider_eval.runtime_hooks, + }); } let detail = if let Some(repository_id) = request.repository_id.as_ref() { format!( @@ -439,6 +494,14 @@ impl CleanTasksRequest { mod tests { use super::*; #[cfg(feature = "lua")] + use crate::fdroid_catalog::{FdroidEndpointConfig, FDROID_PROVIDER_ID}; + #[cfg(feature = "lua")] + use crate::github_releases::{ + GithubReleaseConfig, DEFAULT_GITHUB_API_BASE_URL, GITHUB_PROVIDER_ID, + }; + #[cfg(feature = "lua")] + use crate::provider_cache::PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1; + #[cfg(feature = "lua")] use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; #[cfg(feature = "lua")] use getter_core::RepositoryPriority; @@ -447,8 +510,52 @@ mod tests { update::OFFLINE_UPDATE_CHECK_VERSION, UpdateAction, UpdateArtifact, UpdateCandidate, }; #[cfg(feature = "lua")] + use getter_providers::{parse_fdroid_index_xml, parse_github_releases_json}; + #[cfg(feature = "lua")] + use getter_storage::{CacheDb, ProviderResponseUpsert}; + #[cfg(feature = "lua")] + use sha2::{Digest, Sha512}; + #[cfg(feature = "lua")] use std::fs; + #[cfg(feature = "lua")] + const FDROID_INDEX_FIXTURE: &str = r#" + + + + F-Droid + App repository client + + 1.20.0 + 1020000 + org.fdroid.fdroid_1020000.apk + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 1234567 + + + +"#; + + #[cfg(feature = "lua")] + const GITHUB_RELEASES_FIXTURE: &str = r#"[ + { + "tag_name": "v1.20.0", + "name": "F-Droid 1.20.0", + "draft": false, + "prerelease": false, + "published_at": "2026-06-01T00:00:00Z", + "assets": [ + { + "name": "F-Droid.apk", + "browser_download_url": "https://github.com/f-droid/fdroidclient/releases/download/v1.20.0/F-Droid.apk", + "content_type": "application/vnd.android.package-archive", + "size": 12345, + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } +]"#; + #[cfg(feature = "lua")] #[test] fn registered_package_update_check_issues_action_from_package_directory_static_updates() { @@ -471,6 +578,7 @@ mod tests { let issued = issue_action_from_registered_package_json( &mut runtime, + temp.path(), &db, &json!({ "package_id": "android/app/com.example.autogen", @@ -483,6 +591,8 @@ mod tests { assert_eq!(issued["package"]["repository"], "autogen"); assert_eq!(issued["package"]["name"], "Example Autogen"); assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!(issued["provider_calls"], json!([])); + assert_eq!(issued["runtime_hooks"], json!([])); let action_id = issued["action"]["action_id"].as_str().unwrap(); let submitted = submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) @@ -512,6 +622,7 @@ mod tests { let issued = issue_action_from_registered_package_json( &mut runtime, + temp.path(), &db, &json!({ "package_id": "android/app/com.example.autogen", @@ -525,6 +636,181 @@ mod tests { assert!(issued["action"].is_null()); } + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_issues_action_from_fdroid_provider_luaclass() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_fdroid_provider_package_repo(&repo_root, Some(FDROID_INDEX_FIXTURE), false); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + seed_fdroid_provider_cache(data_dir, FDROID_INDEX_FIXTURE); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "installed_version": "1.0.0" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["package"]["source_priority"], json!(["fdroid"])); + assert_eq!(issued["provider_calls"][0]["provider"], "fdroid"); + assert_eq!(issued["provider_calls"][0]["source"], "cache"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + "1.20.0" + ); + assert_eq!( + issued["update"]["actions"][0]["url"], + "https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk" + ); + let action_id = issued["action"]["action_id"].as_str().unwrap(); + let submitted = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap(); + assert_eq!( + submitted["package_id"], + "android/f-droid/app/org.fdroid.fdroid" + ); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_issues_action_from_github_provider_luaclass() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_github_provider_package_repo(&repo_root, GITHUB_RELEASES_FIXTURE); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + seed_github_provider_cache(data_dir, GITHUB_RELEASES_FIXTURE); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/app/org.fdroid.fdroid", + "installed_version": "v1.0.0" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["package"]["source_priority"], json!(["github"])); + assert_eq!(issued["provider_calls"][0]["provider"], "github"); + assert_eq!(issued["provider_calls"][0]["source"], "cache"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + "v1.20.0" + ); + assert_eq!(issued["update"]["actions"][0]["file_name"], "F-Droid.apk"); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_does_not_accept_provider_fixture_payload() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_fdroid_provider_package_repo(&repo_root, Some(FDROID_INDEX_FIXTURE), false); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + let mut runtime = GetterRuntime::new(); + + let err = issue_action_from_registered_package_json( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "installed_version": "1.0.0", + "fdroid_index_xml": FDROID_INDEX_FIXTURE + }) + .to_string(), + ) + .unwrap_err(); + + assert_eq!(err.code(), "package.eval_error"); + assert!(err + .to_string() + .contains("F-Droid provider host has no cached catalog")); + assert_eq!(runtime.tasks().len(), 0); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_rejects_unmanifested_provider_cache() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_fdroid_provider_package_repo(&repo_root, None, false); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + seed_fdroid_provider_cache(data_dir, FDROID_INDEX_FIXTURE); + let mut runtime = GetterRuntime::new(); + + let err = issue_action_from_registered_package_json( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "installed_version": "1.0.0" + }) + .to_string(), + ) + .unwrap_err(); + + assert_eq!(err.code(), "package.eval_error"); + assert!(err + .to_string() + .contains("package.provider.response_not_in_manifest")); + assert_eq!(runtime.tasks().len(), 0); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_accepts_manifest_compatible_provider_cache() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_fdroid_provider_package_repo(&repo_root, Some(FDROID_INDEX_FIXTURE), false); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + seed_fdroid_provider_cache(data_dir, FDROID_INDEX_FIXTURE); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "installed_version": "1.0.0" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["provider_calls"][0]["source"], "cache"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + "1.20.0" + ); + } + #[test] fn offline_update_check_issues_getter_owned_action_id() { let mut runtime = GetterRuntime::new(); @@ -650,6 +936,144 @@ mod tests { runtime.submit_action(&action.action_id).unwrap().task_id } + #[cfg(feature = "lua")] + fn seed_fdroid_provider_cache(data_dir: &std::path::Path, fixture_body: &str) { + let endpoint = FdroidEndpointConfig::default(); + let catalog = parse_fdroid_index_xml(fixture_body).unwrap(); + let db = CacheDb::open(data_dir.join("cache.db")).unwrap(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: endpoint.cache_key(), + provider: FDROID_PROVIDER_ID.to_owned(), + response_json: serde_json::to_value(catalog).unwrap(), + source_response_sha512: vec![sha512_hex(fixture_body.as_bytes())], + provenance_schema_version: Some(PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1.to_owned()), + freshness_json: json!({}), + }) + .unwrap(); + } + + #[cfg(feature = "lua")] + fn seed_github_provider_cache(data_dir: &std::path::Path, fixture_body: &str) { + let config = GithubReleaseConfig { + api_base_url: DEFAULT_GITHUB_API_BASE_URL.to_owned(), + owner: "f-droid".to_owned(), + repo: "fdroidclient".to_owned(), + }; + let releases = parse_github_releases_json(fixture_body).unwrap(); + let db = CacheDb::open(data_dir.join("cache.db")).unwrap(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key: config.cache_key(), + provider: GITHUB_PROVIDER_ID.to_owned(), + response_json: serde_json::to_value(releases).unwrap(), + source_response_sha512: vec![sha512_hex(fixture_body.as_bytes())], + provenance_schema_version: Some(PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1.to_owned()), + freshness_json: json!({}), + }) + .unwrap(); + } + + #[cfg(feature = "lua")] + fn write_fdroid_provider_package_repo( + root: &std::path::Path, + manifest_body: Option<&str>, + allow_free_network: bool, + ) { + let package_dir = root.join("android/f-droid/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + let mut metadata = json!({ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } + }); + if allow_free_network { + metadata["lua"] = json!({ + "9999.lua": { "permission": ["allow_free_network"] } + }); + } + fs::write( + package_dir.join("metadata.jsonc"), + serde_json::to_string_pretty(&metadata).unwrap(), + ) + .unwrap(); + if let Some(body) = manifest_body { + fs::write( + package_dir.join("Manifest"), + format!("{} fixture-body\n", sha512_hex(body.as_bytes())), + ) + .unwrap(); + } + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package { + package_name = "org.fdroid.fdroid", +} +"#, + ) + .unwrap(); + } + + #[cfg(feature = "lua")] + fn write_github_provider_package_repo(root: &std::path::Path, manifest_body: &str) { + let package_dir = root.join("android/app/org.fdroid.fdroid"); + fs::create_dir_all(&package_dir).unwrap(); + fs::write( + package_dir.join("metadata.jsonc"), + r#"{ + "type": "android:app", + "android": { "package_name": "org.fdroid.fdroid" } +}"#, + ) + .unwrap(); + fs::write( + package_dir.join("Manifest"), + format!("{} fixture-body\n", sha512_hex(manifest_body.as_bytes())), + ) + .unwrap(); + fs::write( + package_dir.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "F-Droid", + android_package = "org.fdroid.fdroid", + owner = "f-droid", + repo = "fdroidclient", + asset = { include = "[.]apk$" }, +} +"#, + ) + .unwrap(); + } + + #[cfg(feature = "lua")] + fn register_repository( + db: &MainDb, + id: &str, + name: &str, + priority: i32, + root: &std::path::Path, + ) { + db.upsert_repository( + &RepositoryMetadata { + id: id.parse().unwrap(), + name: name.to_owned(), + priority: RepositoryPriority::new(priority), + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(root), + None, + ) + .unwrap(); + } + + #[cfg(feature = "lua")] + fn sha512_hex(body: &[u8]) -> String { + let mut hasher = Sha512::new(); + hasher.update(body); + format!("{:x}", hasher.finalize()) + } + #[cfg(feature = "lua")] fn write_package_directory_static_update_repo(root: &std::path::Path) { let package_dir = root.join("android/app/com.example.autogen"); From 2349451aa7c463e8805a82e87797fd47a1b3c36a Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Sun, 28 Jun 2026 21:00:43 +0800 Subject: [PATCH 60/71] feat(autogen): use F-Droid provider module output --- crates/getter-cli/Cargo.toml | 2 +- crates/getter-cli/src/lib.rs | 19 +- crates/getter-cli/tests/bdd_cli.rs | 57 ++++- .../features/cli/autogen_installed.feature | 8 +- .../getter-operations/src/fdroid_autogen.rs | 242 +++++++++++++----- .../src/lua_provider_host.rs | 14 + 6 files changed, 267 insertions(+), 75 deletions(-) diff --git a/crates/getter-cli/Cargo.toml b/crates/getter-cli/Cargo.toml index a5cec89..4f1520b 100644 --- a/crates/getter-cli/Cargo.toml +++ b/crates/getter-cli/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true [dependencies] getter-core = { path = "../getter-core" } getter-downloader = { path = "../getter-downloader" } -getter-operations = { path = "../getter-operations" } +getter-operations = { path = "../getter-operations", features = ["lua"] } getter-storage = { path = "../getter-storage" } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 0d32f2f..ab22fd9 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -798,7 +798,7 @@ fn execute(invocation: CliInvocation) -> Result { )?) .map_err(|source| CliError::Download(format!("failed to serialize handoff: {source}"))) } - CliCommand::RuntimeScript { script } => run_runtime_script(&script), + CliCommand::RuntimeScript { script } => run_runtime_script(&invocation.data_dir, &script), CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; @@ -1547,7 +1547,7 @@ struct RuntimeScriptStep { plan: Option, } -fn run_runtime_script(path: &Path) -> Result { +fn run_runtime_script(data_dir: &Path, path: &Path) -> Result { let bytes = fs::read(path) .map_err(|source| CliError::Runtime(format!("failed to read runtime script: {source}")))?; let script: RuntimeScript = serde_json::from_slice(&bytes).map_err(|source| { @@ -1557,7 +1557,7 @@ fn run_runtime_script(path: &Path) -> Result { let mut context = RuntimeScriptContext::default(); let mut outputs = Vec::new(); for step in script.steps { - let data = execute_runtime_script_step(&mut runtime, &mut context, step)?; + let data = execute_runtime_script_step(data_dir, &mut runtime, &mut context, step)?; outputs.push(data); } Ok(json!({ "steps": outputs })) @@ -1570,6 +1570,7 @@ struct RuntimeScriptContext { } fn execute_runtime_script_step( + data_dir: &Path, runtime: &mut GetterRuntime, context: &mut RuntimeScriptContext, step: RuntimeScriptStep, @@ -1585,6 +1586,18 @@ fn execute_runtime_script_step( }; runtime_operations::issue_action(runtime, plan) } + "update_check_package_issue_action" => { + let db = open_main_db(data_dir)?; + let payload = + replace_runtime_script_tokens(context, empty_object_payload(step.payload))?; + let payload = serde_json::to_string(&payload).map_err(|source| { + CliError::Runtime(format!("failed to serialize runtime request: {source}")) + })?; + runtime_operations::issue_action_from_registered_package_json( + runtime, data_dir, &db, &payload, + ) + .map_err(|source| CliError::Runtime(source.to_string()))? + } "submit_action" => runtime_json_operation( runtime, runtime_operations::submit_action_json, diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index b0a05e2..c0d8348 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -466,6 +466,33 @@ fn runtime_script_submits_completes_removes_and_cleans_task(world: &mut CliWorld world.runtime_script = Some(script); } +#[given(expr = "a runtime script checking generated package {string} at {string}")] +fn runtime_script_checks_generated_fdroid_package( + world: &mut CliWorld, + package_id: String, + installed_version: String, +) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let script = temp.path().join("runtime-script.json"); + fs::write( + &script, + serde_json::to_vec_pretty(&serde_json::json!({ + "steps": [ + { + "operation": "update_check_package_issue_action", + "payload": { + "package_id": package_id, + "installed_version": installed_version, + } + } + ] + })) + .expect("runtime script serializes"), + ) + .expect("write runtime script"); + world.runtime_script = Some(script); +} + #[given(expr = "a fixture Lua repository {string} with package {string}")] fn fixture_lua_repository(world: &mut CliWorld, repo_id: String, package_id: String) { create_fixture_lua_repository(world, repo_id, package_id, "F-Droid".to_owned()); @@ -1302,6 +1329,26 @@ fn runtime_script_output_removes_completed_task(world: &mut CliWorld) { assert_eq!(steps[5]["data"]["tasks"].as_array().unwrap().len(), 0); } +#[then(expr = "the provider-backed F-Droid update check selects version_code {int}")] +fn provider_backed_fdroid_update_check_selects_version_code( + world: &mut CliWorld, + version_code: i64, +) { + let json = current_json(world); + assert_eq!(json["command"], "runtime script"); + let steps = json["data"]["steps"].as_array().expect("runtime steps"); + assert_eq!(steps.len(), 1); + assert_eq!(steps[0]["operation"], "update_check_package_issue_action"); + let data = &steps[0]["data"]; + assert_eq!(data["provider_calls"][0]["provider"], "fdroid"); + assert_eq!(data["provider_calls"][0]["source"], "cache"); + assert_eq!(data["update"]["status"], "update_available"); + assert_eq!( + data["update"]["selected"]["candidate"]["version_code"].as_i64(), + Some(version_code) + ); +} + #[then(expr = "the task list contains the remembered task with status {string}")] fn task_list_contains_remembered_task_with_status(world: &mut CliWorld, status: String) { let task_id = world @@ -1684,8 +1731,16 @@ fn autogen_repository_contains_generated_fdroid_package(world: &mut CliWorld, pa autogen_repository_contains_generated_package(world, package_id.clone()); let path = autogen_repo_path(world).join(package_relative_path(&package_id)); let content = fs::read_to_string(path.join("9999.lua")).expect("generated package readable"); + assert!(content.contains("local fdroid = require(\"luaclass.fdroid_android\")")); assert!(content.contains("return fdroid.package")); - assert!(content.contains("version_code = 1020000")); + assert!(content.contains("package_name = \"org.fdroid.fdroid\"")); + assert!(!content.contains("version_code = 1020000")); + assert!(!content.contains("local catalog_package =")); + let manifest = fs::read_to_string(path.join("Manifest")).expect("generated Manifest readable"); + assert!( + manifest.contains("fdroid-index.xml"), + "generated F-Droid Manifest should name provider source provenance: {manifest}" + ); } #[then(expr = "the autogen repository contains generated package {string}")] diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index 26ce9c3..2c90255 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -19,11 +19,11 @@ Feature: Installed app autogen Then the command succeeds And the autogen repository contains generated F-Droid package "android/f-droid/app/org.fdroid.fdroid" And the app list contains autogen tracked package "android/f-droid/app/org.fdroid.fdroid" - When I run getter repo validate for autogen - Then the output reports a valid repository without network - When I run getter package eval for package "android/f-droid/app/org.fdroid.fdroid" + Given a runtime script checking generated package "android/f-droid/app/org.fdroid.fdroid" at "1.0.0" + When I run getter runtime script for that script Then the command succeeds - And the package eval contains update version_code 1020000 + And the output is valid JSON + And the provider-backed F-Droid update check selects version_code 1020000 Scenario: User previews F-Droid autogen from installed inventory Given an initialized getter data directory diff --git a/crates/getter-operations/src/fdroid_autogen.rs b/crates/getter-operations/src/fdroid_autogen.rs index c0208b1..b1d33e7 100644 --- a/crates/getter-operations/src/fdroid_autogen.rs +++ b/crates/getter-operations/src/fdroid_autogen.rs @@ -18,7 +18,7 @@ use getter_core::autogen::{ InstalledInventory, InstalledInventoryItem, AUTOGEN_RECORD_VERSION, FDROID_AUTOGEN_GENERATOR, }; use getter_core::{InstalledTarget, PackageId, PackageKind}; -use getter_providers::{FdroidApp, FdroidRelease}; +use getter_providers::FdroidApp; use getter_storage::{CacheDb, MainDb}; use serde::Deserialize; use serde_json::{json, Value}; @@ -92,6 +92,7 @@ pub fn preview_fdroid_packages_json( let candidate = fdroid_candidate_json( &catalog.endpoint.endpoint_id, &catalog.endpoint.endpoint_url, + &catalog.source_response_sha512, &package_id, app, )?; @@ -216,6 +217,7 @@ fn fdroid_package_id(package_name: &str) -> AutogenOperationResult { fn fdroid_candidate_json( endpoint_id: &str, endpoint_url: &str, + source_response_sha512: &[String], package_id: &PackageId, app: &FdroidApp, ) -> AutogenOperationResult { @@ -225,7 +227,7 @@ fn fdroid_candidate_json( .as_deref() .filter(|name| !name.trim().is_empty()) .unwrap_or(&app.package_name); - let files = fdroid_generated_files(endpoint_url, app)?; + let files = fdroid_generated_files(endpoint_id, endpoint_url, source_response_sha512, app)?; let record = AutogenRecord { version: AUTOGEN_RECORD_VERSION, generator: FDROID_AUTOGEN_GENERATOR.to_owned(), @@ -278,15 +280,29 @@ fn fdroid_candidate_json( } fn fdroid_generated_files( + endpoint_id: &str, endpoint_url: &str, + source_response_sha512: &[String], app: &FdroidApp, ) -> AutogenOperationResult> { + if endpoint_id != DEFAULT_FDROID_ENDPOINT_ID || endpoint_url != DEFAULT_FDROID_ENDPOINT_URL { + return Err(AutogenOperationError::Autogen(format!( + "F-Droid provider-module autogen currently supports only endpoint '{}={}', got '{}={}'", + DEFAULT_FDROID_ENDPOINT_ID, DEFAULT_FDROID_ENDPOINT_URL, endpoint_id, endpoint_url + ))); + } + if source_response_sha512.is_empty() { + return Err(AutogenOperationError::Autogen( + "F-Droid provider-module autogen requires provider source provenance for Manifest generation" + .to_owned(), + )); + } let metadata = render_pretty_json(json!({ "type": "android:app", "android": { "package_name": app.package_name }, }))?; - let manifest = String::new(); - let version_lua = fdroid_version_lua(endpoint_url, app); + let manifest = fdroid_manifest(source_response_sha512); + let version_lua = fdroid_version_lua(app); Ok(vec![ generated_file("metadata.jsonc", metadata), generated_file("Manifest", manifest), @@ -294,54 +310,20 @@ fn fdroid_generated_files( ]) } -fn fdroid_version_lua(endpoint_url: &str, app: &FdroidApp) -> String { - let mut releases = app.packages.clone(); - releases.sort_by(|left, right| { - right - .version_code - .cmp(&left.version_code) - .then_with(|| right.version.cmp(&left.version)) - .then_with(|| left.apk_name.cmp(&right.apk_name)) - }); - let updates = releases +fn fdroid_manifest(source_response_sha512: &[String]) -> String { + source_response_sha512 .iter() - .map(|release| fdroid_update_lua(endpoint_url, release)) - .collect::>() - .join(",\n"); - format!( - "#!/bin/upa-lua v1\n-- @generated by UpgradeAll getter autogen (F-Droid catalog fixture)\nlocal catalog_package = {{\n package_name = {},\n updates = {{{updates}\n }},\n}}\n\nlocal fdroid = {{}}\nfunction fdroid.package(spec)\n if spec.package_name ~= catalog_package.package_name then\n error(\"F-Droid generated package_name mismatch\")\n end\n return package_version {{ updates = catalog_package.updates }}\nend\n\nreturn fdroid.package {{\n package_name = {},\n}}\n", - lua_string(&app.package_name), - lua_string(&app.package_name), - ) + .map(|digest| format!("{digest} fdroid-index.xml\n")) + .collect() } -fn fdroid_update_lua(endpoint_url: &str, release: &FdroidRelease) -> String { - let version_code = release - .version_code - .map(|value| format!("\n version_code = {value},")) - .unwrap_or_default(); - let sha256 = release - .sha256 - .as_deref() - .map(|value| format!("\n sha256 = {},", lua_string(value))) - .unwrap_or_default(); - let size = release - .size - .map(|value| format!("\n size = {value},")) - .unwrap_or_default(); +fn fdroid_version_lua(app: &FdroidApp) -> String { format!( - "\n {{\n version = {},{version_code}\n source = \"fdroid\",\n artifacts = {{\n {{\n name = {},\n url = {},\n file_name = {},{sha256}{size}\n }},\n }},\n }}", - lua_string(&release.version), - lua_string(&release.apk_name), - lua_string(&fdroid_artifact_url(endpoint_url, &release.apk_name)), - lua_string(&release.apk_name), + "#!/bin/upa-lua v1\n-- @generated by UpgradeAll getter autogen (F-Droid provider module)\nlocal fdroid = require(\"luaclass.fdroid_android\")\n\nreturn fdroid.package {{\n package_name = {},\n}}\n", + lua_string(&app.package_name), ) } -fn fdroid_artifact_url(endpoint_url: &str, apk_name: &str) -> String { - format!("{}/{}", endpoint_url.trim_end_matches('/'), apk_name) -} - fn render_pretty_json(value: Value) -> AutogenOperationResult { serde_json::to_string_pretty(&value) .map(|mut content| { @@ -378,10 +360,12 @@ fn lua_string(value: &str) -> String { #[cfg(test)] mod tests { use super::*; - use getter_core::lua::evaluate_package_directory_script; + use crate::provider_cache::source_response_sha512; use getter_core::repository::{ - RepositoryMetadata, RepositoryPackageDirectoryLayout, REPO_API_VERSION_V1, + PackageTypeMetadata, RepositoryMetadata, RepositoryPackageDirectoryLayout, + REPO_API_VERSION_V1, }; + use getter_core::runtime::GetterRuntime; use getter_core::RepositoryPriority; const FDROID_FIXTURE: &str = r#" @@ -459,16 +443,27 @@ mod tests { .find(|file| file["relative_path"] == "9999.lua") .and_then(|file| file["content"].as_str()) .unwrap(); - assert!(version_lua.contains("local catalog_package =")); - assert!(version_lua.contains("local fdroid = {}")); - assert!(version_lua.contains("version_code = 1020000")); + assert!(version_lua.contains("local fdroid = require(\"luaclass.fdroid_android\")")); assert!(version_lua.contains("return fdroid.package")); assert!(version_lua.contains("package_name = \"org.fdroid.fdroid\"")); - assert!(version_lua.contains("https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk")); - assert!(!version_lua.contains("require(\"luaclass.fdroid_android\")")); + assert!(!version_lua.contains("endpoint_id")); + assert!(!version_lua.contains("local catalog_package =")); + assert!(!version_lua.contains("version_code = 1020000")); + assert!(!version_lua.contains("https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk")); + assert!(!version_lua.contains("getter_dev")); assert!(!version_lua.contains("getter.provider")); - assert!(files.iter().any(|file| file["relative_path"] == "Manifest" - && file["content"].as_str().unwrap().is_empty())); + let manifest = files + .iter() + .find(|file| file["relative_path"] == "Manifest") + .and_then(|file| file["content"].as_str()) + .unwrap(); + assert_eq!( + manifest, + format!( + "{} fdroid-index.xml\n", + source_response_sha512(FDROID_FIXTURE) + ) + ); assert_eq!( preview["skipped"][0]["reason"], "provider_package_not_found" @@ -480,6 +475,57 @@ mod tests { assert!(!temp.path().join("repo/autogen").exists()); } + #[test] + fn provider_module_generation_rejects_non_default_endpoint() { + let app = FdroidApp { + package_name: "org.fdroid.fdroid".to_owned(), + name: None, + summary: None, + packages: Vec::new(), + }; + let digest = vec![source_response_sha512(FDROID_FIXTURE)]; + + let custom_id_error = + fdroid_generated_files("custom", DEFAULT_FDROID_ENDPOINT_URL, &digest, &app) + .unwrap_err(); + assert!( + matches!(custom_id_error, AutogenOperationError::Autogen(detail) if detail.contains("supports only endpoint 'official=https://f-droid.org/repo'")) + ); + + let custom_url_error = fdroid_generated_files( + DEFAULT_FDROID_ENDPOINT_ID, + "https://example.invalid/fdroid", + &digest, + &app, + ) + .unwrap_err(); + assert!( + matches!(custom_url_error, AutogenOperationError::Autogen(detail) if detail.contains("got 'official=https://example.invalid/fdroid'")) + ); + } + + #[test] + fn provider_module_generation_requires_source_provenance() { + let app = FdroidApp { + package_name: "org.fdroid.fdroid".to_owned(), + name: None, + summary: None, + packages: Vec::new(), + }; + + let error = fdroid_generated_files( + DEFAULT_FDROID_ENDPOINT_ID, + DEFAULT_FDROID_ENDPOINT_URL, + &[], + &app, + ) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("requires provider source provenance")) + ); + } + #[test] fn preview_skips_fdroid_package_covered_by_higher_priority_repo() { let temp = tempfile::tempdir().unwrap(); @@ -567,22 +613,86 @@ mod tests { .package(&"android/f-droid/app/org.fdroid.fdroid".parse().unwrap()) .unwrap(); let metadata = layout.package_metadata(package_dir).unwrap(); - let script = layout.unambiguous_version_script(package_dir).unwrap(); - let package = evaluate_package_directory_script( - &"autogen".parse().unwrap(), - package_dir, - &metadata, - script, + assert!(matches!( + &metadata.package, + PackageTypeMetadata::AndroidApp { android } + if android.package_name == "org.fdroid.fdroid" + )); + + let mut runtime = GetterRuntime::new(); + let issued = crate::runtime::issue_action_from_registered_package_json( + &mut runtime, + temp.path(), + &main_db, + &json!({ + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "installed_version": "1.0.0" + }) + .to_string(), ) .unwrap(); - assert_eq!(package.name, "android/f-droid/app/org.fdroid.fdroid"); - assert_eq!(package.installed.len(), 1); - assert_eq!(package.updates.len(), 2); - assert_eq!(package.updates[0].version_code, Some(1020000)); + assert_eq!(issued["provider_calls"][0]["provider"], "fdroid"); + assert_eq!(issued["provider_calls"][0]["source"], "cache"); + assert_eq!(issued["update"]["status"], "update_available"); assert_eq!( - package.updates[0].artifacts[0].sha256.as_deref(), - Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + issued["update"]["selected"]["candidate"]["version"], + "1.20.0" ); + assert_eq!( + issued["update"]["selected"]["candidate"]["version_code"], + 1020000 + ); + assert_eq!( + issued["update"]["actions"][0]["url"], + "https://f-droid.org/repo/org.fdroid.fdroid_1020000.apk" + ); + } + + #[test] + fn generated_fdroid_package_update_check_rejects_manifest_mismatch() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let preview = preview_fdroid_packages_json( + temp.path(), + &main_db, + &cache_db, + &json!({ + "index_xml": FDROID_FIXTURE, + "package_names": ["org.fdroid.fdroid"] + }) + .to_string(), + ) + .unwrap(); + apply_fdroid_preview_json( + temp.path(), + &main_db, + &preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap(); + let manifest_path = temp + .path() + .join("repo/autogen/android/f-droid/app/org.fdroid.fdroid/Manifest"); + std::fs::write(&manifest_path, "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 fdroid-index.xml\n").unwrap(); + + let mut runtime = GetterRuntime::new(); + let error = crate::runtime::issue_action_from_registered_package_json( + &mut runtime, + temp.path(), + &main_db, + &json!({ + "package_id": "android/f-droid/app/org.fdroid.fdroid", + "installed_version": "1.0.0" + }) + .to_string(), + ) + .unwrap_err(); + + assert_eq!(error.code(), "package.eval_error"); + assert!(error + .to_string() + .contains("package.provider.response_not_in_manifest")); } #[test] diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 36c55fc..12756cf 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -86,6 +86,7 @@ const DEFAULT_GITHUB_ENDPOINT_ID: &str = "github"; /// or built-in `luaclass/` modules while catalog parsing/cache behavior stays /// in `getter-operations`. #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] pub fn fdroid_package_eval_json( data_dir: &Path, request_json: &str, @@ -149,6 +150,7 @@ pub fn fdroid_package_eval_json( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] #[derive(Debug, Deserialize)] struct FdroidPackageEvalRequest { repository_id: RepositoryId, @@ -164,6 +166,7 @@ struct FdroidPackageEvalRequest { } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] struct FdroidDevHostConfig { cache_db_path: PathBuf, endpoint: FdroidEndpointConfig, @@ -188,6 +191,7 @@ struct FdroidProviderHostConfig { /// or built-in `luaclass/` modules while release parsing/cache behavior stays /// in `getter-operations`. #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] pub fn github_package_eval_json( data_dir: &Path, request_json: &str, @@ -257,6 +261,7 @@ pub fn github_package_eval_json( /// installs the stable namespace and runtime hooks, but still uses fixture data /// and remains hidden from CLI/native/Flutter product APIs. #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] pub fn stable_provider_package_eval_json( data_dir: &Path, request_json: &str, @@ -304,6 +309,7 @@ pub fn stable_provider_package_eval_json( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] #[derive(Debug, Deserialize)] struct StableProviderPackageEvalRequest { repository_id: RepositoryId, @@ -357,6 +363,7 @@ fn evaluate_with_stable_provider_host( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] #[derive(Debug, Deserialize)] struct GithubPackageEvalRequest { repository_id: RepositoryId, @@ -374,6 +381,7 @@ struct GithubPackageEvalRequest { } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] struct GithubDevHostConfig { cache_db_path: PathBuf, endpoint_id: String, @@ -489,6 +497,7 @@ pub(crate) fn evaluate_provider_backed_package( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] fn install_fdroid_dev_host(lua: &Lua, config: FdroidDevHostConfig) -> mlua::Result<()> { let provider_config = FdroidProviderHostConfig { cache_db_path: config.cache_db_path.clone(), @@ -548,6 +557,7 @@ impl FdroidUpdateHostRequest { } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Result<()> { let provider_config = GithubProviderHostConfig { cache_db_path: config.cache_db_path.clone(), @@ -881,6 +891,7 @@ fn provider_call_from_envelope(envelope: &Table) -> mlua::Result { } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] fn envelope_candidates(envelope: Table) -> mlua::Result { envelope.get("candidates") } @@ -1103,6 +1114,7 @@ fn reject_unproven_manifest_cache( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] pub(crate) fn provider_cache_mode( mode: Option<&str>, ) -> Result { @@ -1116,6 +1128,7 @@ pub(crate) fn provider_cache_mode( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] fn find_repository( db: &MainDb, repository_id: &RepositoryId, @@ -1131,6 +1144,7 @@ fn find_repository( } #[cfg(feature = "lua-provider-host-dev")] +#[allow(dead_code)] fn repo_path(repository: &StoredRepository) -> Result { repository.path.as_ref().map(PathBuf::from).ok_or_else(|| { LuaProviderHostOperationError::InvalidRequest(format!( From 969a605c1a9d780548bfa9a4d6cc253bc579092e Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 2 Jul 2026 11:37:10 +0800 Subject: [PATCH 61/71] feat(provider): preserve GitHub release metadata --- crates/getter-core/src/lib.rs | 4 + crates/getter-core/src/lua.rs | 12 ++ crates/getter-core/src/update.rs | 3 + .../getter-operations/src/fdroid_autogen.rs | 10 +- crates/getter-operations/src/runtime.rs | 163 ++++++++++++++++-- crates/getter-providers/src/lib.rs | 10 ++ 6 files changed, 183 insertions(+), 19 deletions(-) diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 957386c..3fce46b 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -283,6 +283,8 @@ pub struct UpdateCandidate { #[serde(default)] pub version_code: Option, #[serde(default)] + pub changelog: Option, + #[serde(default)] pub channel: Option, #[serde(default)] pub source: Option, @@ -295,6 +297,8 @@ pub struct UpdateArtifact { pub name: String, pub url: String, #[serde(default)] + pub content_type: Option, + #[serde(default)] pub file_name: Option, #[serde(default)] pub sha256: Option, diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index 3bdf60b..fa6044a 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -789,6 +789,7 @@ fn parse_update_candidates( Ok(UpdateCandidate { version: required_string(path, object, "version")?.to_owned(), version_code: optional_i64(path, object, "version_code")?, + changelog: optional_string(object, "changelog"), channel: optional_string(object, "channel"), source: optional_string(object, "source"), artifacts, @@ -818,6 +819,7 @@ fn parse_update_artifacts( Ok(UpdateArtifact { name: required_string(path, object, "name")?.to_owned(), url: required_string(path, object, "url")?.to_owned(), + content_type: optional_string(object, "content_type"), file_name: optional_string(object, "file_name"), sha256: optional_string(object, "sha256"), size: optional_u64(path, object, "size")?, @@ -974,12 +976,14 @@ return package_version { { version = "1.2.0", version_code = 120, + changelog = "Release notes from provider snapshot", channel = "stable", source = "fixture", artifacts = { { name = "app.apk", url = "https://example.invalid/app.apk", + content_type = "application/vnd.android.package-archive", file_name = "fdroid.apk", sha256 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", size = 12345, @@ -1007,8 +1011,16 @@ return package_version { assert_eq!(package.updates.len(), 1); assert_eq!(package.updates[0].version, "1.2.0"); assert_eq!(package.updates[0].version_code, Some(120)); + assert_eq!( + package.updates[0].changelog.as_deref(), + Some("Release notes from provider snapshot") + ); assert_eq!(package.updates[0].channel.as_deref(), Some("stable")); assert_eq!(package.updates[0].source.as_deref(), Some("fixture")); + assert_eq!( + package.updates[0].artifacts[0].content_type.as_deref(), + Some("application/vnd.android.package-archive") + ); assert_eq!( package.updates[0].artifacts[0].file_name.as_deref(), Some("fdroid.apk") diff --git a/crates/getter-core/src/update.rs b/crates/getter-core/src/update.rs index 6e9260c..4315365 100644 --- a/crates/getter-core/src/update.rs +++ b/crates/getter-core/src/update.rs @@ -505,6 +505,7 @@ mod tests { vec![UpdateCandidate { version: "1.2.0".to_owned(), version_code: None, + changelog: None, channel: None, source: None, artifacts: Vec::new(), @@ -541,11 +542,13 @@ mod tests { UpdateCandidate { version: version.to_owned(), version_code: None, + changelog: None, channel: None, source: None, artifacts: vec![UpdateArtifact { name: "APK".to_owned(), url: format!("https://example.invalid/{version}.apk"), + content_type: None, file_name: Some("app.apk".to_owned()), sha256: None, size: None, diff --git a/crates/getter-operations/src/fdroid_autogen.rs b/crates/getter-operations/src/fdroid_autogen.rs index b1d33e7..3e3ea1a 100644 --- a/crates/getter-operations/src/fdroid_autogen.rs +++ b/crates/getter-operations/src/fdroid_autogen.rs @@ -361,10 +361,10 @@ fn lua_string(value: &str) -> String { mod tests { use super::*; use crate::provider_cache::source_response_sha512; - use getter_core::repository::{ - PackageTypeMetadata, RepositoryMetadata, RepositoryPackageDirectoryLayout, - REPO_API_VERSION_V1, - }; + #[cfg(feature = "lua")] + use getter_core::repository::{PackageTypeMetadata, RepositoryPackageDirectoryLayout}; + use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; + #[cfg(feature = "lua")] use getter_core::runtime::GetterRuntime; use getter_core::RepositoryPriority; @@ -576,6 +576,7 @@ mod tests { assert_eq!(preview["skipped"][0]["covering_repo_id"], "official"); } + #[cfg(feature = "lua")] #[test] fn apply_writes_valid_fdroid_package_directory() { let temp = tempfile::tempdir().unwrap(); @@ -648,6 +649,7 @@ mod tests { ); } + #[cfg(feature = "lua")] #[test] fn generated_fdroid_package_update_check_rejects_manifest_mismatch() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index d68025c..08d8c88 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -556,6 +556,14 @@ mod tests { } ]"#; + #[cfg(feature = "lua")] + const UPGRADEALL_GITHUB_RELEASES_SNAPSHOT: &str = + include_str!("../../../tests/files/web/github_api_release.json"); + + #[cfg(feature = "lua")] + const UPGRADEALL_OLD_GITHUB_RELEASES_NORMALIZED: &str = + include_str!("../../../tests/files/data/provider_github_release.json"); + #[cfg(feature = "lua")] #[test] fn registered_package_update_check_issues_action_from_package_directory_static_updates() { @@ -717,6 +725,83 @@ mod tests { assert_eq!(issued["update"]["actions"][0]["file_name"], "F-Droid.apk"); } + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_matches_old_upgradeall_github_release_snapshot() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_upgradeall_github_provider_package_repo( + &repo_root, + UPGRADEALL_GITHUB_RELEASES_SNAPSHOT, + ); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + seed_github_provider_cache_for( + data_dir, + "DUpdateSystem", + "UpgradeAll", + UPGRADEALL_GITHUB_RELEASES_SNAPSHOT, + ); + let old_releases: Value = + serde_json::from_str(UPGRADEALL_OLD_GITHUB_RELEASES_NORMALIZED).unwrap(); + let expected_release = &old_releases[0]; + let expected_asset = &expected_release["assets"][0]; + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/app/net.xzos.upgradeall", + "installed_version": "0.13-beta.3" + }) + .to_string(), + ) + .unwrap(); + + assert_eq!(issued["package"]["source_priority"], json!(["github"])); + assert_eq!( + issued["package"]["installed"][0], + json!({ "kind": "android_package", "package_name": "net.xzos.upgradeall" }) + ); + assert_eq!(issued["provider_calls"][0]["provider"], "github"); + assert_eq!(issued["provider_calls"][0]["owner"], "DUpdateSystem"); + assert_eq!(issued["provider_calls"][0]["repo"], "UpgradeAll"); + assert_eq!(issued["provider_calls"][0]["source"], "cache"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + expected_release["version_number"] + ); + assert_eq!( + issued["update"]["selected"]["candidate"]["changelog"], + expected_release["changelog"] + ); + assert_eq!( + issued["update"]["selected"]["artifact"]["name"], + expected_asset["file_name"] + ); + assert_eq!( + issued["update"]["selected"]["artifact"]["content_type"], + expected_asset["file_type"] + ); + assert_eq!( + issued["update"]["actions"][0]["url"], + expected_asset["download_url"] + ); + assert_eq!( + issued["update"]["actions"][0]["file_name"], + expected_asset["file_name"] + ); + let action_id = issued["action"]["action_id"].as_str().unwrap(); + let submitted = + submit_action_json(&mut runtime, &json!({ "action_id": action_id }).to_string()) + .unwrap(); + assert_eq!(submitted["package_id"], "android/app/net.xzos.upgradeall"); + } + #[cfg(feature = "lua")] #[test] fn registered_package_update_check_does_not_accept_provider_fixture_payload() { @@ -954,10 +1039,20 @@ mod tests { #[cfg(feature = "lua")] fn seed_github_provider_cache(data_dir: &std::path::Path, fixture_body: &str) { + seed_github_provider_cache_for(data_dir, "f-droid", "fdroidclient", fixture_body); + } + + #[cfg(feature = "lua")] + fn seed_github_provider_cache_for( + data_dir: &std::path::Path, + owner: &str, + repo: &str, + fixture_body: &str, + ) { let config = GithubReleaseConfig { api_base_url: DEFAULT_GITHUB_API_BASE_URL.to_owned(), - owner: "f-droid".to_owned(), - repo: "fdroidclient".to_owned(), + owner: owner.to_owned(), + repo: repo.to_owned(), }; let releases = parse_github_releases_json(fixture_body).unwrap(); let db = CacheDb::open(data_dir.join("cache.db")).unwrap(); @@ -1016,13 +1111,47 @@ return fdroid.package { #[cfg(feature = "lua")] fn write_github_provider_package_repo(root: &std::path::Path, manifest_body: &str) { let package_dir = root.join("android/app/org.fdroid.fdroid"); - fs::create_dir_all(&package_dir).unwrap(); + write_github_provider_package( + &package_dir, + manifest_body, + "F-Droid", + "org.fdroid.fdroid", + "f-droid", + "fdroidclient", + ); + } + + #[cfg(feature = "lua")] + fn write_upgradeall_github_provider_package_repo(root: &std::path::Path, manifest_body: &str) { + let package_dir = root.join("android/app/net.xzos.upgradeall"); + write_github_provider_package( + &package_dir, + manifest_body, + "UpgradeAll", + "net.xzos.upgradeall", + "DUpdateSystem", + "UpgradeAll", + ); + } + + #[cfg(feature = "lua")] + fn write_github_provider_package( + package_dir: &std::path::Path, + manifest_body: &str, + name: &str, + android_package: &str, + owner: &str, + repo: &str, + ) { + fs::create_dir_all(package_dir).unwrap(); fs::write( package_dir.join("metadata.jsonc"), - r#"{ + format!( + r#"{{ "type": "android:app", - "android": { "package_name": "org.fdroid.fdroid" } -}"#, + "android": {{ "package_name": "{android_package}" }} +}}"# + ), ) .unwrap(); fs::write( @@ -1032,16 +1161,18 @@ return fdroid.package { .unwrap(); fs::write( package_dir.join("9999.lua"), - r#"#!/bin/upa-lua v1 + format!( + r#"#!/bin/upa-lua v1 local github_android = require("luaclass.github_android_apk") -return github_android.package { - name = "F-Droid", - android_package = "org.fdroid.fdroid", - owner = "f-droid", - repo = "fdroidclient", - asset = { include = "[.]apk$" }, -} -"#, +return github_android.package {{ + name = "{name}", + android_package = "{android_package}", + owner = "{owner}", + repo = "{repo}", + asset = {{ include = "[.]apk$" }}, +}} +"# + ), ) .unwrap(); } @@ -1126,11 +1257,13 @@ return package_version { .map(|version| UpdateCandidate { version: version.to_owned(), version_code: None, + changelog: None, channel: None, source: None, artifacts: vec![UpdateArtifact { name: "app.apk".to_owned(), url: "https://example.invalid/app.apk".to_owned(), + content_type: None, file_name: Some("app.apk".to_owned()), sha256: None, size: None, diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index a71cd49..001eae5 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -77,6 +77,7 @@ impl FdroidRelease { UpdateCandidate { version: self.version.clone(), version_code: self.version_code, + changelog: None, channel: None, source: Some("fdroid".to_owned()), artifacts: vec![UpdateArtifact { @@ -86,6 +87,7 @@ impl FdroidRelease { .as_deref() .map(|base| fdroid_artifact_url(base, &self.apk_name)) .unwrap_or_else(|| self.apk_name.clone()), + content_type: None, file_name: Some(self.apk_name.clone()), sha256: self.sha256.clone(), size: self.size, @@ -399,6 +401,7 @@ fn github_release_update_candidate( Some(UpdateCandidate { version: release.tag_name.clone(), version_code: None, + changelog: release.body.clone(), channel: release.prerelease.then(|| "prerelease".to_owned()), source: Some("github".to_owned()), artifacts, @@ -409,6 +412,7 @@ fn github_asset_update_artifact(asset: &GithubReleaseAsset) -> UpdateArtifact { UpdateArtifact { name: asset.name.clone(), url: asset.browser_download_url.clone(), + content_type: asset.content_type.clone(), file_name: Some(asset.name.clone()), sha256: github_asset_sha256(asset.digest.as_deref()), size: asset.size, @@ -505,6 +509,7 @@ mod tests { updates: vec![UpdateCandidate { version: "1.2.0".to_owned(), version_code: None, + changelog: None, channel: Some("stable".to_owned()), source: Some("fixture".to_owned()), artifacts: Vec::new(), @@ -623,6 +628,7 @@ mod tests { assert_eq!(candidates.len(), 1); assert_eq!(candidates[0].version, "v1.2.0"); + assert_eq!(candidates[0].changelog.as_deref(), Some("Release notes")); assert_eq!(candidates[0].source.as_deref(), Some("github")); assert_eq!(candidates[0].artifacts.len(), 1); let artifact = &candidates[0].artifacts[0]; @@ -631,6 +637,10 @@ mod tests { artifact.url, "https://github.com/example/app/releases/download/v1.2.0/app-release.apk" ); + assert_eq!( + artifact.content_type.as_deref(), + Some("application/vnd.android.package-archive") + ); assert_eq!(artifact.file_name.as_deref(), Some("app-release.apk")); assert_eq!( artifact.sha256.as_deref(), From 4d49476471a6567dfa5e59aa01c02a230c93cd00 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 2 Jul 2026 14:58:26 +0800 Subject: [PATCH 62/71] feat(autogen): add GitHub Android package generation --- crates/getter-cli/src/lib.rs | 292 ++++++- crates/getter-cli/tests/bdd_cli.rs | 136 ++++ .../features/cli/autogen_installed.feature | 19 + crates/getter-core/src/autogen.rs | 12 + .../getter-operations/src/github_autogen.rs | 733 ++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + 6 files changed, 1192 insertions(+), 1 deletion(-) create mode 100644 crates/getter-operations/src/github_autogen.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index ab22fd9..b062204 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -23,6 +23,7 @@ use getter_downloader::{ }; use getter_operations::autogen::{self, AutogenAcceptance, AutogenOperationError}; use getter_operations::fdroid_autogen; +use getter_operations::github_autogen; use getter_operations::github_latest_commit::{self, GithubLatestCommitOperationError}; use getter_operations::github_releases::{self, GithubReleaseOperationError}; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; @@ -138,6 +139,20 @@ pub enum CliCommand { preview: PathBuf, acceptance: AutogenAcceptance, }, + AutogenGithubPreview { + owner: String, + repo: String, + android_package: String, + display_name: Option, + releases: PathBuf, + asset_include: Option, + asset_exclude: Option, + include_prereleases: bool, + }, + AutogenGithubApply { + preview: PathBuf, + acceptance: AutogenAcceptance, + }, ProviderGithubReleases { owner: String, repo: String, @@ -603,6 +618,32 @@ where acceptance: parse_autogen_acceptance(rest)?, } } + [domain, subject, action, rest @ ..] + if domain == "autogen" && subject == "github" && action == "preview" => + { + let args = parse_autogen_github_preview_args(rest)?; + CliCommand::AutogenGithubPreview { + owner: args.owner, + repo: args.repo, + android_package: args.android_package, + display_name: args.display_name, + releases: args.releases, + asset_include: args.asset_include, + asset_exclude: args.asset_exclude, + include_prereleases: args.include_prereleases, + } + } + [domain, subject, action, flag, preview, rest @ ..] + if domain == "autogen" + && subject == "github" + && action == "apply" + && flag == "--preview" => + { + CliCommand::AutogenGithubApply { + preview: PathBuf::from(preview), + acceptance: parse_autogen_acceptance(rest)?, + } + } [domain, provider, action, rest @ ..] if domain == "provider" && provider == "github" && action == "releases" => { @@ -869,6 +910,53 @@ fn execute(invocation: CliInvocation) -> Result { ) .map_err(CliError::from) } + CliCommand::AutogenGithubPreview { + owner, + repo, + android_package, + display_name, + releases, + asset_include, + asset_exclude, + include_prereleases, + } => { + let db = open_main_db(&invocation.data_dir)?; + let cache_db = open_cache_db(&invocation.data_dir)?; + let releases_json = read_github_releases_fixture(&releases)?; + let request = json!({ + "owner": owner, + "repo": repo, + "android_package": android_package, + "display_name": display_name, + "releases_json": releases_json, + "include_prereleases": include_prereleases, + "asset": { + "include": asset_include, + "exclude": asset_exclude, + }, + }); + github_autogen::preview_github_android_package_json( + &invocation.data_dir, + &db, + &cache_db, + &request.to_string(), + ) + .map_err(CliError::from) + } + CliCommand::AutogenGithubApply { + preview, + acceptance, + } => { + let db = open_main_db(&invocation.data_dir)?; + let preview = read_autogen_preview(&preview, "github.autogen.preview")?; + github_autogen::apply_github_preview_json( + &invocation.data_dir, + &db, + &preview, + &acceptance, + ) + .map_err(CliError::from) + } CliCommand::ProviderGithubReleases { owner, repo, @@ -1137,6 +1225,18 @@ fn parse_fdroid_autogen_preview_args( Ok((index, inventory, package_names)) } +#[derive(Debug, Default, PartialEq, Eq)] +struct AutogenGithubPreviewArgs { + owner: String, + repo: String, + android_package: String, + display_name: Option, + releases: PathBuf, + asset_include: Option, + asset_exclude: Option, + include_prereleases: bool, +} + #[derive(Debug, Default, PartialEq, Eq)] struct ProviderGithubReleasesArgs { owner: String, @@ -1157,6 +1257,128 @@ struct ProviderGithubLatestCommitArgs { refresh: bool, } +fn parse_autogen_github_preview_args( + args: &[String], +) -> Result { + let mut parsed = AutogenGithubPreviewArgs::default(); + let mut position = 0; + while position < args.len() { + match args[position].as_str() { + "--owner" => { + parsed.owner = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "autogen github preview --owner requires an owner".to_owned(), + ) + })? + .clone(); + position += 2; + } + "--repo" => { + parsed.repo = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage("autogen github preview --repo requires a repo".to_owned()) + })? + .clone(); + position += 2; + } + "--android-package" => { + parsed.android_package = args + .get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "autogen github preview --android-package requires a package name" + .to_owned(), + ) + })? + .clone(); + position += 2; + } + "--display-name" => { + parsed.display_name = Some( + args.get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "autogen github preview --display-name requires a name".to_owned(), + ) + })? + .clone(), + ); + position += 2; + } + "--releases" => { + let path = args.get(position + 1).ok_or_else(|| { + CliError::Usage( + "autogen github preview --releases requires a fixture path".to_owned(), + ) + })?; + parsed.releases = PathBuf::from(path); + position += 2; + } + "--asset-include" => { + parsed.asset_include = Some( + args.get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "autogen github preview --asset-include requires a regex" + .to_owned(), + ) + })? + .clone(), + ); + position += 2; + } + "--asset-exclude" => { + parsed.asset_exclude = Some( + args.get(position + 1) + .ok_or_else(|| { + CliError::Usage( + "autogen github preview --asset-exclude requires a regex" + .to_owned(), + ) + })? + .clone(), + ); + position += 2; + } + "--include-prereleases" => { + parsed.include_prereleases = true; + position += 1; + } + other => { + return Err(CliError::Usage(format!( + "unsupported autogen github preview argument '{other}'" + ))) + } + } + } + + if parsed.owner.trim().is_empty() { + return Err(CliError::Usage( + "autogen github preview requires --owner ".to_owned(), + )); + } + if parsed.repo.trim().is_empty() { + return Err(CliError::Usage( + "autogen github preview requires --repo ".to_owned(), + )); + } + if parsed.android_package.trim().is_empty() { + return Err(CliError::Usage( + "autogen github preview requires --android-package ".to_owned(), + )); + } + if parsed.releases.as_os_str().is_empty() { + return Err(CliError::Usage( + "autogen github preview requires --releases ".to_owned(), + )); + } + + Ok(parsed) +} + fn parse_provider_github_releases_args( args: &[String], ) -> Result { @@ -2027,7 +2249,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo --releases [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo --releases [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -2096,6 +2318,8 @@ impl CliCommand { Self::AutogenCleanupApply { .. } => "autogen cleanup apply", Self::AutogenFdroidPreview { .. } => "autogen fdroid preview", Self::AutogenFdroidApply { .. } => "autogen fdroid apply", + Self::AutogenGithubPreview { .. } => "autogen github preview", + Self::AutogenGithubApply { .. } => "autogen github apply", Self::ProviderGithubReleases { .. } => "provider github releases", Self::ProviderGithubLatestCommit { .. } => "provider github latest-commit", Self::LegacyImportRoomBundle { .. } => "legacy import-room-bundle", @@ -2188,6 +2412,72 @@ mod tests { ); } + #[test] + fn parses_autogen_github_preview_and_apply_commands() { + let preview = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "autogen", + "github", + "preview", + "--owner", + "DUpdateSystem", + "--repo", + "UpgradeAll", + "--android-package", + "net.xzos.upgradeall", + "--display-name", + "UpgradeAll", + "--releases", + "/tmp/releases.json", + "--asset-include", + "UpgradeAll_.*[.]apk$", + "--asset-exclude", + "debug", + "--include-prereleases", + ]) + .unwrap(); + assert_eq!( + preview.command, + CliCommand::AutogenGithubPreview { + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + android_package: "net.xzos.upgradeall".to_owned(), + display_name: Some("UpgradeAll".to_owned()), + releases: PathBuf::from("/tmp/releases.json"), + asset_include: Some("UpgradeAll_.*[.]apk$".to_owned()), + asset_exclude: Some("debug".to_owned()), + include_prereleases: true, + } + ); + + let apply = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "autogen", + "github", + "apply", + "--preview", + "/tmp/preview.json", + "--accept", + "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall", + ]) + .unwrap(); + assert_eq!( + apply.command, + CliCommand::AutogenGithubApply { + preview: PathBuf::from("/tmp/preview.json"), + acceptance: AutogenAcceptance::Accept(vec![ + "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall" + .parse() + .unwrap(), + ]), + } + ); + } + #[test] fn provider_github_latest_commit_missing_cli_flags_is_usage_error() { let output = run([ diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index c0d8348..6a38e3c 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -241,6 +241,18 @@ fn fixture_github_releases_response(world: &mut CliWorld, project: String) { world.github_releases = Some(releases); } +#[given(expr = "the committed GitHub releases snapshot for {string}")] +fn committed_github_releases_snapshot(world: &mut CliWorld, _project: String) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let releases = temp.path().join("github-releases-snapshot.json"); + fs::write( + &releases, + include_str!("../../../tests/files/web/github_api_release.json"), + ) + .expect("write committed GitHub releases snapshot"); + world.github_releases = Some(releases); +} + #[given(expr = "a fixture GitHub commit response for {string}")] fn fixture_github_commit_response(world: &mut CliWorld, project: String) { let temp = world.temp.as_ref().expect("tempdir exists"); @@ -1010,6 +1022,49 @@ fn run_getter_autogen_fdroid_preview_for_inventory(world: &mut CliWorld) { world.json = None; } +#[when( + expr = "I run getter autogen github preview for owner {string} repo {string} android package {string}" +)] +fn run_getter_autogen_github_preview( + world: &mut CliWorld, + owner: String, + repo: String, + android_package: String, +) { + let releases = world + .github_releases + .as_ref() + .expect("GitHub releases fixture exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "github".to_owned(), + "preview".to_owned(), + "--owner".to_owned(), + owner, + "--repo".to_owned(), + repo.clone(), + "--android-package".to_owned(), + android_package, + "--display-name".to_owned(), + "UpgradeAll".to_owned(), + "--releases".to_owned(), + releases.to_string_lossy().to_string(), + "--asset-include".to_owned(), + if repo == "UpgradeAll" { + "UpgradeAll_.*[.]apk$".to_owned() + } else { + r"\.apk$".to_owned() + }, + "--asset-exclude".to_owned(), + "debug".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when(expr = "I run getter provider github releases for owner {string} repo {string}")] fn run_getter_provider_github_releases(world: &mut CliWorld, owner: String, repo: String) { let releases = world @@ -1062,6 +1117,27 @@ fn run_getter_provider_github_latest_commit(world: &mut CliWorld, owner: String, world.json = None; } +#[when("I run getter autogen github apply for that preview with accept-all")] +fn run_getter_autogen_github_apply_accept_all(world: &mut CliWorld) { + let preview = world + .autogen_preview + .as_ref() + .expect("autogen preview exists"); + let output = run_getter( + world, + [ + "autogen".to_owned(), + "github".to_owned(), + "apply".to_owned(), + "--preview".to_owned(), + preview.to_string_lossy().to_string(), + "--accept-all".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen fdroid apply for that preview with accept-all")] fn run_getter_autogen_fdroid_apply_accept_all(world: &mut CliWorld) { let preview = world @@ -1329,6 +1405,30 @@ fn runtime_script_output_removes_completed_task(world: &mut CliWorld) { assert_eq!(steps[5]["data"]["tasks"].as_array().unwrap().len(), 0); } +#[then( + expr = "the provider-backed GitHub update check selects version {string} with artifact {string}" +)] +fn provider_backed_github_update_check_selects_version_with_artifact( + world: &mut CliWorld, + version: String, + artifact_name: String, +) { + let json = current_json(world); + assert_eq!(json["command"], "runtime script"); + let steps = json["data"]["steps"].as_array().expect("runtime steps"); + assert_eq!(steps.len(), 1); + assert_eq!(steps[0]["operation"], "update_check_package_issue_action"); + let data = &steps[0]["data"]; + assert_eq!(data["provider_calls"][0]["provider"], "github"); + assert_eq!(data["provider_calls"][0]["source"], "cache"); + assert_eq!(data["update"]["status"], "update_available"); + assert_eq!(data["update"]["selected"]["candidate"]["version"], version); + assert_eq!( + data["update"]["selected"]["candidate"]["artifacts"][0]["name"], + artifact_name + ); +} + #[then(expr = "the provider-backed F-Droid update check selects version_code {int}")] fn provider_backed_fdroid_update_check_selects_version_code( world: &mut CliWorld, @@ -1617,6 +1717,23 @@ fn update_check_has_no_selected_update(world: &mut CliWorld) { assert_eq!(json["data"]["actions"], Value::Array(Vec::new())); } +#[then(expr = "the GitHub autogen preview contains candidate {string}")] +fn github_autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) { + let json = current_json(world); + assert_eq!(json["ok"], true); + assert_eq!(json["command"], "autogen github preview"); + assert_eq!(json["data"]["operation"], "github.autogen.preview"); + let candidates = json["data"]["candidates"] + .as_array() + .expect("candidates array"); + assert!( + candidates + .iter() + .any(|candidate| candidate["package_id"].as_str() == Some(package_id.as_str())), + "preview should contain {package_id}: {candidates:?}" + ); +} + #[then(expr = "the F-Droid autogen preview contains candidate {string}")] fn fdroid_autogen_preview_contains_candidate(world: &mut CliWorld, package_id: String) { let json = current_json(world); @@ -1726,6 +1843,25 @@ fn github_latest_commit_provider_returns_live_revision(world: &mut CliWorld, rev assert!(json["data"]["diagnostics"].as_array().unwrap().is_empty()); } +#[then(expr = "the autogen repository contains generated GitHub package {string}")] +fn autogen_repository_contains_generated_github_package(world: &mut CliWorld, package_id: String) { + autogen_repository_contains_generated_package(world, package_id.clone()); + let path = autogen_repo_path(world).join(package_relative_path(&package_id)); + let content = fs::read_to_string(path.join("9999.lua")).expect("generated package readable"); + assert!(content.contains("local github_android = require(\"luaclass.github_android_apk\")")); + assert!(content.contains("return github_android.package")); + assert!(content.contains("android_package = \"net.xzos.upgradeall\"")); + assert!(content.contains("owner = \"DUpdateSystem\"")); + assert!(content.contains("repo = \"UpgradeAll\"")); + assert!(!content.contains("releases_json")); + assert!(!content.contains("getter.provider")); + let manifest = fs::read_to_string(path.join("Manifest")).expect("generated Manifest readable"); + assert!( + manifest.contains("github-releases.json"), + "generated GitHub Manifest should name provider source provenance: {manifest}" + ); +} + #[then(expr = "the autogen repository contains generated F-Droid package {string}")] fn autogen_repository_contains_generated_fdroid_package(world: &mut CliWorld, package_id: String) { autogen_repository_contains_generated_package(world, package_id.clone()); diff --git a/crates/getter-cli/tests/features/cli/autogen_installed.feature b/crates/getter-cli/tests/features/cli/autogen_installed.feature index 2c90255..e9c1094 100644 --- a/crates/getter-cli/tests/features/cli/autogen_installed.feature +++ b/crates/getter-cli/tests/features/cli/autogen_installed.feature @@ -25,6 +25,25 @@ Feature: Installed app autogen And the output is valid JSON And the provider-backed F-Droid update check selects version_code 1020000 + Scenario: User applies GitHub Android APK autogen and validates the generated package directory + Given an initialized getter data directory + And the committed GitHub releases snapshot for "DUpdateSystem/UpgradeAll" + When I run getter autogen github preview for owner "DUpdateSystem" repo "UpgradeAll" android package "net.xzos.upgradeall" + Then the command succeeds + And the output is valid JSON + And the GitHub autogen preview contains candidate "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall" + And the autogen repository has not been written + And I save the autogen preview to a file + When I run getter autogen github apply for that preview with accept-all + Then the command succeeds + And the autogen repository contains generated GitHub package "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall" + And the app list contains autogen tracked package "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall" + Given a runtime script checking generated package "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall" at "0.12.0" + When I run getter runtime script for that script + Then the command succeeds + And the output is valid JSON + And the provider-backed GitHub update check selects version "0.13-beta.4" with artifact "UpgradeAll_0.13-beta.4.apk" + Scenario: User previews F-Droid autogen from installed inventory Given an initialized getter data directory And a fixture F-Droid catalog index with package "org.fdroid.fdroid" diff --git a/crates/getter-core/src/autogen.rs b/crates/getter-core/src/autogen.rs index c8b7eae..8c34680 100644 --- a/crates/getter-core/src/autogen.rs +++ b/crates/getter-core/src/autogen.rs @@ -16,6 +16,7 @@ pub const AUTOGEN_RECORD_FILE: &str = ".autogen.jsonc"; pub const AUTOGEN_RECORD_VERSION: u32 = 1; pub const INSTALLED_AUTOGEN_GENERATOR: &str = "installed-inventory"; pub const FDROID_AUTOGEN_GENERATOR: &str = "fdroid-catalog"; +pub const GITHUB_AUTOGEN_GENERATOR: &str = "github-releases"; pub const DEFAULT_AUTOGEN_REPOSITORY_ID: &str = "autogen"; pub const DEFAULT_AUTOGEN_REPOSITORY_NAME: &str = "UpgradeAll Autogen"; pub const GENERATED_MARKER: &str = "@generated by UpgradeAll getter autogen"; @@ -117,6 +118,17 @@ pub enum AutogenRecordInput { endpoint_url: String, package_name: String, }, + GithubAndroidApk { + owner: String, + repo: String, + android_package: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + asset_include: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + asset_exclude: Option, + #[serde(default)] + include_prereleases: bool, + }, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/crates/getter-operations/src/github_autogen.rs b/crates/getter-operations/src/github_autogen.rs new file mode 100644 index 0000000..988a334 --- /dev/null +++ b/crates/getter-operations/src/github_autogen.rs @@ -0,0 +1,733 @@ +//! Fixture-backed GitHub Android APK autogen preview/apply operations. +//! +//! This module generates ordinary package directories that use the standard +//! `luaclass.github_android_apk` provider module. It consumes cached or +//! fixture-backed GitHub release facts owned by getter and deliberately avoids +//! live GitHub transport or Flutter/Kotlin provider parsing. + +use crate::autogen::{self, AutogenAcceptance, AutogenOperationError, AutogenOperationResult}; +use crate::github_releases::{ + read_or_refresh_github_releases, GithubReleaseConfig, DEFAULT_GITHUB_API_BASE_URL, + GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, +}; +use crate::provider_cache::{ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheSource}; +use getter_core::autogen::{ + content_hash, package_relative_path, record_file_key, render_autogen_record, AutogenRecord, + AutogenRecordInput, GeneratedPackageFile, AUTOGEN_RECORD_VERSION, GITHUB_AUTOGEN_GENERATOR, +}; +use getter_core::{InstalledTarget, PackageId, PackageKind}; +use getter_providers::{ + github_release_update_candidates, GithubAssetFilter, GithubRelease, + GithubReleaseCandidateOptions, +}; +use getter_storage::{CacheDb, MainDb}; +use serde::Deserialize; +use serde_json::{json, Value}; +use std::path::{Path, PathBuf}; + +pub fn preview_github_android_package_json( + data_dir: &Path, + main_db: &MainDb, + cache_db: &CacheDb, + request_json: &str, +) -> AutogenOperationResult { + let request: GithubAutogenPreviewRequest = + serde_json::from_str(request_json).map_err(|source| { + AutogenOperationError::Autogen(format!( + "invalid GitHub autogen preview request: {source}" + )) + })?; + let owner = required_segment(request.owner, "owner")?; + let repo = required_segment(request.repo, "repo")?; + let android_package = required_non_empty(request.android_package, "android_package")?; + let display_name = request + .display_name + .as_deref() + .map(str::trim) + .filter(|value| !value.is_empty()) + .unwrap_or(&repo) + .to_owned(); + let api_base_url = request + .api_base_url + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| DEFAULT_GITHUB_API_BASE_URL.to_owned()); + if api_base_url != DEFAULT_GITHUB_API_BASE_URL { + return Err(AutogenOperationError::Autogen(format!( + "GitHub provider-module autogen currently supports only API base URL '{DEFAULT_GITHUB_API_BASE_URL}', got '{api_base_url}'" + ))); + } + let mode = match request.mode.as_deref() { + Some("force_refresh") => ProviderCacheMode::ForceRefresh, + Some("use_cached") | None => ProviderCacheMode::UseCached, + Some(other) => { + return Err(AutogenOperationError::Autogen(format!( + "unknown GitHub autogen preview mode '{other}'" + ))) + } + }; + let asset_filter = request.asset.unwrap_or_default().into_filter(); + let options = GithubReleaseCandidateOptions { + include_prereleases: request.include_prereleases, + asset_filter: asset_filter.clone(), + }; + let package_id = github_android_package_id(&owner, &repo, &android_package)?; + let result = read_or_refresh_github_releases( + cache_db, + GithubReleaseConfig { + api_base_url, + owner: owner.clone(), + repo: repo.clone(), + }, + mode, + || { + request.releases_json.ok_or_else(|| { + "fixture-backed GitHub autogen refresh requires releases_json".to_owned() + }) + }, + ) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + let (target_alias, target_path, target_priority) = + autogen::generated_repository_config(data_dir)?; + let covered = + autogen::higher_priority_package_coverage(main_db, &target_alias, target_priority)?; + let mut diagnostics: Vec = result + .diagnostics + .iter() + .map(provider_diagnostic_json) + .collect(); + let mut candidates = Vec::new(); + let mut skipped = Vec::new(); + + if let Some(repository_id) = covered.get(&package_id) { + skipped.push(json!({ + "package_id": package_id.to_string(), + "reason": "covered_by_higher_priority_repo", + "covering_repo_id": repository_id.as_str(), + })); + } else { + let update_candidates = github_release_update_candidates(&result.releases, &options) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + if update_candidates.is_empty() + && github_eligible_release_count(&result.releases, &options) > 0 + { + diagnostics.push(json!({ + "code": GITHUB_ASSET_NOT_FOUND, + "message": "no GitHub release assets matched the requested filters", + "provider": GITHUB_PROVIDER_ID, + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + })); + skipped.push(json!({ + "package_id": package_id.to_string(), + "reason": "provider_asset_not_found", + })); + } else if update_candidates.is_empty() { + diagnostics.push(json!({ + "code": "provider.github.release_not_found", + "message": format!("GitHub repository '{owner}/{repo}' has no eligible releases"), + "provider": GITHUB_PROVIDER_ID, + "owner": owner, + "repo": repo, + "cache_key": result.cache_key, + })); + skipped.push(json!({ + "package_id": package_id.to_string(), + "reason": "provider_release_not_found", + })); + } else { + candidates.push(github_candidate_json( + &result.source_response_sha512, + &package_id, + GithubGeneratedPackageInput { + owner: &owner, + repo: &repo, + android_package: &android_package, + display_name: &display_name, + asset_filter: &asset_filter, + include_prereleases: request.include_prereleases, + }, + )?); + } + } + + Ok(json!({ + "operation": "github.autogen.preview", + "provider": GITHUB_PROVIDER_ID, + "api_base_url": result.config.api_base_url, + "owner": result.config.owner, + "repo": result.config.repo, + "cache_key": result.cache_key, + "source": provider_source_json(result.source), + "target_repo_id": target_alias.as_str(), + "target_repo_path": target_path, + "summary": { + "candidate_count": candidates.len(), + "skipped_count": skipped.len(), + "write_count": candidates.len(), + "delete_count": 0, + }, + "candidates": candidates, + "skipped": skipped, + "diagnostics": diagnostics, + })) +} + +pub fn apply_github_preview_json( + data_dir: &Path, + main_db: &MainDb, + preview: &Value, + acceptance: &AutogenAcceptance, +) -> AutogenOperationResult { + autogen::apply_preview( + data_dir, + main_db, + preview, + acceptance, + "github.autogen.preview", + GITHUB_AUTOGEN_GENERATOR, + ) +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct GithubAutogenPreviewRequest { + #[serde(default)] + api_base_url: Option, + #[serde(default)] + owner: Option, + #[serde(default)] + repo: Option, + #[serde(default)] + android_package: Option, + #[serde(default)] + display_name: Option, + #[serde(default)] + asset: Option, + #[serde(default)] + include_prereleases: bool, + #[serde(default)] + mode: Option, + #[serde(default)] + releases_json: Option, +} + +#[derive(Debug, Default, Deserialize)] +#[serde(deny_unknown_fields)] +struct GithubAssetFilterRequest { + #[serde(default)] + include: Option, + #[serde(default)] + exclude: Option, +} + +impl GithubAssetFilterRequest { + fn into_filter(self) -> GithubAssetFilter { + GithubAssetFilter { + include: self + .include + .map(|value| value.trim().to_owned()) + .filter(|value| !value.is_empty()) + .or_else(|| Some("[.]apk$".to_owned())), + exclude: self + .exclude + .map(|value| value.trim().to_owned()) + .filter(|value| !value.is_empty()), + } + } +} + +struct GithubGeneratedPackageInput<'a> { + owner: &'a str, + repo: &'a str, + android_package: &'a str, + display_name: &'a str, + asset_filter: &'a GithubAssetFilter, + include_prereleases: bool, +} + +fn github_candidate_json( + source_response_sha512: &[String], + package_id: &PackageId, + input: GithubGeneratedPackageInput<'_>, +) -> AutogenOperationResult { + let relative_path = package_relative_path(package_id); + let files = github_generated_files(source_response_sha512, &input)?; + let record = AutogenRecord { + version: AUTOGEN_RECORD_VERSION, + generator: GITHUB_AUTOGEN_GENERATOR.to_owned(), + package_id: package_id.clone(), + output_relative_path: relative_path.clone(), + input: AutogenRecordInput::GithubAndroidApk { + owner: input.owner.to_owned(), + repo: input.repo.to_owned(), + android_package: input.android_package.to_owned(), + asset_include: input.asset_filter.include.clone(), + asset_exclude: input.asset_filter.exclude.clone(), + include_prereleases: input.include_prereleases, + }, + files: files + .iter() + .map(|file| { + ( + record_file_key(&file.relative_path), + file.content_hash.clone(), + ) + }) + .collect(), + }; + let record_content = render_autogen_record(&record) + .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; + let content_hash = content_hash(&record_content); + let file_json: Vec = files + .iter() + .map(|file| { + json!({ + "relative_path": file.relative_path, + "content_hash": file.content_hash, + "content": file.content, + }) + }) + .collect(); + + Ok(json!({ + "package_id": package_id.to_string(), + "kind": package_id.kind().as_str(), + "display_name": input.display_name, + "installed_target": InstalledTarget::AndroidPackage { package_name: input.android_package.to_owned() }, + "action": "create", + "output_relative_path": relative_path, + "content_hash": content_hash, + "content": record_content, + "autogen_record_content": record_content, + "files": file_json, + "provider": GITHUB_PROVIDER_ID, + "upstream_id": format!("{}/{}", input.owner, input.repo), + })) +} + +fn github_generated_files( + source_response_sha512: &[String], + input: &GithubGeneratedPackageInput<'_>, +) -> AutogenOperationResult> { + if source_response_sha512.is_empty() { + return Err(AutogenOperationError::Autogen( + "GitHub provider-module autogen requires provider source provenance for Manifest generation" + .to_owned(), + )); + } + let metadata = render_pretty_json(json!({ + "type": "android:app", + "display_name": input.display_name, + "android": { "package_name": input.android_package }, + }))?; + let manifest = github_manifest(source_response_sha512); + let version_lua = github_version_lua(input); + Ok(vec![ + generated_file("metadata.jsonc", metadata), + generated_file("Manifest", manifest), + generated_file("9999.lua", version_lua), + ]) +} + +fn github_manifest(source_response_sha512: &[String]) -> String { + source_response_sha512 + .iter() + .map(|digest| format!("{digest} github-releases.json\n")) + .collect() +} + +fn github_version_lua(input: &GithubGeneratedPackageInput<'_>) -> String { + let mut fields = vec![ + format!(" name = {},", lua_string(input.display_name)), + format!(" android_package = {},", lua_string(input.android_package)), + format!(" owner = {},", lua_string(input.owner)), + format!(" repo = {},", lua_string(input.repo)), + ]; + if input.asset_filter.include.is_some() || input.asset_filter.exclude.is_some() { + fields.push(" asset = {".to_owned()); + if let Some(include) = &input.asset_filter.include { + fields.push(format!(" include = {},", lua_string(include))); + } + if let Some(exclude) = &input.asset_filter.exclude { + fields.push(format!(" exclude = {},", lua_string(exclude))); + } + fields.push(" },".to_owned()); + } + if input.include_prereleases { + fields.push(" include_prereleases = true,".to_owned()); + } + format!( + "#!/bin/upa-lua v1\n-- @generated by UpgradeAll getter autogen (GitHub release provider module)\nlocal github_android = require(\"luaclass.github_android_apk\")\n\nreturn github_android.package {{\n{}\n}}\n", + fields.join("\n"), + ) +} + +fn github_android_package_id( + owner: &str, + repo: &str, + android_package: &str, +) -> AutogenOperationResult { + PackageId::new( + PackageKind::Android, + format!("github/{owner}/{repo}/{android_package}"), + ) + .map_err(|source| AutogenOperationError::Autogen(source.to_string())) +} + +fn render_pretty_json(value: Value) -> AutogenOperationResult { + serde_json::to_string_pretty(&value) + .map(|mut content| { + content.push('\n'); + content + }) + .map_err(|source| AutogenOperationError::Autogen(source.to_string())) +} + +fn generated_file(relative_path: &str, content: String) -> GeneratedPackageFile { + GeneratedPackageFile { + relative_path: PathBuf::from(relative_path), + content_hash: content_hash(&content), + content, + } +} + +fn lua_string(value: &str) -> String { + let mut escaped = String::from("\""); + for ch in value.chars() { + match ch { + '\\' => escaped.push_str("\\\\"), + '"' => escaped.push_str("\\\""), + '\n' => escaped.push_str("\\n"), + '\r' => escaped.push_str("\\r"), + '\t' => escaped.push_str("\\t"), + other => escaped.push(other), + } + } + escaped.push('"'); + escaped +} + +fn required_segment(value: Option, field: &'static str) -> AutogenOperationResult { + let value = required_non_empty(value, field)?; + if value.contains('/') { + return Err(AutogenOperationError::Autogen(format!( + "GitHub autogen {field} must not contain '/'" + ))); + } + Ok(value) +} + +fn required_non_empty( + value: Option, + field: &'static str, +) -> AutogenOperationResult { + value + .map(|value| value.trim().to_owned()) + .filter(|value| !value.is_empty()) + .ok_or_else(|| AutogenOperationError::Autogen(format!("missing {field}"))) +} + +fn github_eligible_release_count( + releases: &[GithubRelease], + options: &GithubReleaseCandidateOptions, +) -> usize { + releases + .iter() + .filter(|release| !release.draft) + .filter(|release| options.include_prereleases || !release.prerelease) + .count() +} + +fn provider_source_json(source: ProviderCacheSource) -> &'static str { + match source { + ProviderCacheSource::Cache => "cache", + ProviderCacheSource::Refreshed => "refreshed", + ProviderCacheSource::Stale => "stale", + } +} + +fn provider_diagnostic_json(diagnostic: &ProviderCacheDiagnostic) -> Value { + json!({ + "code": diagnostic.code, + "message": diagnostic.message, + "cache_key": diagnostic.cache_key, + "provider": diagnostic.provider, + "stale_fetched_at_unix": diagnostic.stale_fetched_at_unix, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + #[cfg(feature = "lua")] + use getter_core::repository::{PackageTypeMetadata, RepositoryPackageDirectoryLayout}; + use getter_core::repository::{RepositoryMetadata, REPO_API_VERSION_V1}; + #[cfg(feature = "lua")] + use getter_core::runtime::GetterRuntime; + use getter_core::RepositoryPriority; + use serde_json::{json, Value}; + use sha2::{Digest, Sha512}; + + const UPGRADEALL_GITHUB_RELEASES: &str = + include_str!("../../../tests/files/web/github_api_release.json"); + const UPGRADEALL_PACKAGE_ID: &str = + "android/github/DUpdateSystem/UpgradeAll/net.xzos.upgradeall"; + + #[test] + fn preview_generates_github_android_package_directory_from_release_snapshot() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + + let preview = preview_github_android_package_json( + temp.path(), + &main_db, + &cache_db, + &upgradeall_request().to_string(), + ) + .unwrap(); + + assert_eq!(preview["operation"], "github.autogen.preview"); + assert_eq!(preview["provider"], "github"); + assert_eq!(preview["owner"], "DUpdateSystem"); + assert_eq!(preview["repo"], "UpgradeAll"); + assert_eq!(preview["target_repo_id"], "autogen"); + assert_eq!(preview["source"], "refreshed"); + assert_eq!(preview["candidates"].as_array().unwrap().len(), 1); + let candidate = &preview["candidates"][0]; + assert_eq!(candidate["package_id"], UPGRADEALL_PACKAGE_ID); + assert_eq!(candidate["output_relative_path"], UPGRADEALL_PACKAGE_ID); + assert_eq!(candidate["display_name"], "UpgradeAll"); + assert_eq!( + candidate["installed_target"]["package_name"], + "net.xzos.upgradeall" + ); + assert_eq!(candidate["provider"], "github"); + assert_eq!(candidate["upstream_id"], "DUpdateSystem/UpgradeAll"); + + let record: Value = + serde_json::from_str(candidate["autogen_record_content"].as_str().unwrap()).unwrap(); + assert_eq!(record["generator"], "github-releases"); + assert_eq!(record["input"]["kind"], "github_android_apk"); + assert_eq!(record["input"]["owner"], "DUpdateSystem"); + assert_eq!(record["input"]["repo"], "UpgradeAll"); + assert_eq!(record["input"]["android_package"], "net.xzos.upgradeall"); + assert_eq!(record["input"]["asset_include"], "UpgradeAll_.*[.]apk$"); + assert_eq!(record["input"]["include_prereleases"], false); + + let files = candidate["files"].as_array().unwrap(); + let version_lua = generated_file_content(files, "9999.lua"); + assert!( + version_lua.contains("local github_android = require(\"luaclass.github_android_apk\")") + ); + assert!(version_lua.contains("return github_android.package")); + assert!(version_lua.contains("name = \"UpgradeAll\"")); + assert!(version_lua.contains("android_package = \"net.xzos.upgradeall\"")); + assert!(version_lua.contains("owner = \"DUpdateSystem\"")); + assert!(version_lua.contains("repo = \"UpgradeAll\"")); + assert!(version_lua.contains("include = \"UpgradeAll_.*[.]apk$\"")); + assert!(!version_lua.contains("releases_json")); + assert!(!version_lua.contains("getter.provider")); + assert!(!version_lua.contains("getter_dev")); + let manifest = generated_file_content(files, "Manifest"); + assert_eq!( + manifest, + format!( + "{} github-releases.json\n", + sha512_hex(UPGRADEALL_GITHUB_RELEASES.as_bytes()) + ) + ); + assert!(!temp.path().join("repo/autogen").exists()); + } + + #[cfg(feature = "lua")] + #[test] + fn apply_writes_valid_github_package_directory_and_runtime_uses_cache() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let preview = preview_github_android_package_json( + temp.path(), + &main_db, + &cache_db, + &upgradeall_request().to_string(), + ) + .unwrap(); + + let result = apply_github_preview_json( + temp.path(), + &main_db, + &preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap(); + + assert_eq!(result["applied_count"], 1); + let repo_root = temp.path().join("repo/autogen"); + let package_dir = repo_root.join(UPGRADEALL_PACKAGE_ID); + assert!(package_dir.join("metadata.jsonc").is_file()); + assert!(package_dir.join("Manifest").is_file()); + assert!(package_dir.join("9999.lua").is_file()); + assert!(package_dir.join(".autogen.jsonc").is_file()); + let layout = RepositoryPackageDirectoryLayout::load(&repo_root).unwrap(); + let package_dir = layout + .package(&UPGRADEALL_PACKAGE_ID.parse().unwrap()) + .unwrap(); + let metadata = layout.package_metadata(package_dir).unwrap(); + assert!(matches!( + &metadata.package, + PackageTypeMetadata::AndroidApp { android } + if android.package_name == "net.xzos.upgradeall" + )); + + let mut runtime = GetterRuntime::new(); + let issued = crate::runtime::issue_action_from_registered_package_json( + &mut runtime, + temp.path(), + &main_db, + &json!({ + "package_id": UPGRADEALL_PACKAGE_ID, + "installed_version": "0.12.0" + }) + .to_string(), + ) + .unwrap(); + assert_eq!(issued["provider_calls"][0]["provider"], "github"); + assert_eq!(issued["provider_calls"][0]["owner"], "DUpdateSystem"); + assert_eq!(issued["provider_calls"][0]["repo"], "UpgradeAll"); + assert_eq!(issued["provider_calls"][0]["source"], "cache"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + "0.13-beta.4" + ); + assert_eq!( + issued["update"]["selected"]["candidate"]["artifacts"][0]["name"], + "UpgradeAll_0.13-beta.4.apk" + ); + assert_eq!( + issued["update"]["selected"]["candidate"]["artifacts"][0]["content_type"], + "application/vnd.android.package-archive" + ); + assert!(issued["update"]["selected"]["candidate"]["changelog"] + .as_str() + .unwrap() + .contains("Ukrainian")); + } + + #[test] + fn provider_module_generation_requires_source_provenance() { + let asset_filter = GithubAssetFilter { + include: Some("[.]apk$".to_owned()), + exclude: None, + }; + let error = github_generated_files( + &[], + &GithubGeneratedPackageInput { + owner: "DUpdateSystem", + repo: "UpgradeAll", + android_package: "net.xzos.upgradeall", + display_name: "UpgradeAll", + asset_filter: &asset_filter, + include_prereleases: false, + }, + ) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("requires provider source provenance")) + ); + } + + #[test] + fn preview_rejects_custom_github_api_base_url_generation() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let mut request = upgradeall_request(); + request["api_base_url"] = json!("https://example.invalid/api"); + + let error = preview_github_android_package_json( + temp.path(), + &main_db, + &cache_db, + &request.to_string(), + ) + .unwrap_err(); + + assert!( + matches!(error, AutogenOperationError::Autogen(detail) if detail.contains("supports only API base URL")) + ); + } + + #[test] + fn preview_skips_github_package_covered_by_higher_priority_repo() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let official_root = temp.path().join("repo/official"); + let package_dir = official_root.join(UPGRADEALL_PACKAGE_ID); + std::fs::create_dir_all(&package_dir).unwrap(); + std::fs::write( + package_dir.join("metadata.jsonc"), + r#"{ "type": "android:app", "android": { "package_name": "net.xzos.upgradeall" } }"#, + ) + .unwrap(); + main_db + .upsert_repository( + &RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".to_owned(), + priority: RepositoryPriority::DEFAULT, + api_version: REPO_API_VERSION_V1.to_owned(), + }, + Some(&official_root), + None, + ) + .unwrap(); + + let preview = preview_github_android_package_json( + temp.path(), + &main_db, + &cache_db, + &upgradeall_request().to_string(), + ) + .unwrap(); + + assert!(preview["candidates"].as_array().unwrap().is_empty()); + assert_eq!(preview["skipped"][0]["package_id"], UPGRADEALL_PACKAGE_ID); + assert_eq!( + preview["skipped"][0]["reason"], + "covered_by_higher_priority_repo" + ); + assert_eq!(preview["skipped"][0]["covering_repo_id"], "official"); + } + + fn upgradeall_request() -> Value { + json!({ + "owner": "DUpdateSystem", + "repo": "UpgradeAll", + "android_package": "net.xzos.upgradeall", + "display_name": "UpgradeAll", + "asset": { + "include": "UpgradeAll_.*[.]apk$" + }, + "releases_json": UPGRADEALL_GITHUB_RELEASES, + }) + } + + fn generated_file_content<'a>(files: &'a [Value], relative_path: &str) -> &'a str { + files + .iter() + .find(|file| file["relative_path"] == relative_path) + .and_then(|file| file["content"].as_str()) + .unwrap_or_else(|| panic!("generated file {relative_path} not found: {files:?}")) + } + + fn sha512_hex(body: &[u8]) -> String { + let mut hasher = Sha512::new(); + hasher.update(body); + format!("{:x}", hasher.finalize()) + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 8d7fc30..1851139 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -8,6 +8,7 @@ pub mod autogen; pub mod fdroid_autogen; pub mod fdroid_catalog; +pub mod github_autogen; pub mod github_latest_commit; pub mod github_releases; pub mod legacy_room; From 1f7b181a127527fce53f10381502d8b9f600cc1a Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 2 Jul 2026 18:04:38 +0800 Subject: [PATCH 63/71] feat(provider): refresh GitHub releases through live transport --- Cargo.lock | 117 ++++ Cargo.toml | 2 +- crates/getter-cli/src/lib.rs | 82 ++- crates/getter-operations/Cargo.toml | 2 + .../getter-operations/src/github_releases.rs | 540 +++++++++++++++++- .../src/lua_provider_host.rs | 41 +- crates/getter-operations/src/runtime.rs | 269 ++++++++- 7 files changed, 988 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a65d2b4..b1df2b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "ahash" version = "0.8.12" @@ -85,6 +91,12 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "2.13.0" @@ -247,6 +259,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -430,6 +451,16 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "futures" version = "0.3.32" @@ -624,6 +655,7 @@ dependencies = [ "sha2", "tempfile", "thiserror 1.0.69", + "ureq", ] [[package]] @@ -734,6 +766,22 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + [[package]] name = "humantime" version = "2.3.0" @@ -921,6 +969,16 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.2.1" @@ -1057,6 +1115,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5" +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pin-project" version = "1.1.13" @@ -1242,7 +1306,9 @@ version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ + "log", "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -1447,6 +1513,12 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + [[package]] name = "slab" version = "0.4.12" @@ -1744,6 +1816,42 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" +dependencies = [ + "base64", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "ureq-proto", + "utf8-zero", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + [[package]] name = "utf8parse" version = "0.2.2" @@ -1787,6 +1895,15 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "webpki-roots" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "which" version = "7.0.3" diff --git a/Cargo.toml b/Cargo.toml index ba37ca3..400474f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ default = ["cli", "domain"] domain = ["dep:getter-core", "dep:getter-storage", "dep:getter-operations"] lua = ["getter-core/lua", "getter-operations/lua"] cli = ["dep:getter-cli"] -rustls-platform-verifier = ["dep:rustls-platform-verifier"] +rustls-platform-verifier = ["dep:rustls-platform-verifier", "getter-operations?/rustls-platform-verifier"] rustls-platform-verifier-android = ["rustls-platform-verifier", "rustls-platform-verifier/jni"] webpki-roots = [] native-tokio = [] diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index b062204..6a87d37 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -156,7 +156,7 @@ pub enum CliCommand { ProviderGithubReleases { owner: String, repo: String, - releases: PathBuf, + releases: Option, asset_include: Option, asset_exclude: Option, include_prereleases: bool, @@ -967,7 +967,10 @@ fn execute(invocation: CliInvocation) -> Result { refresh, } => { let db = open_cache_db(&invocation.data_dir)?; - let releases_json = read_github_releases_fixture(&releases)?; + let releases_json = releases + .as_deref() + .map(read_github_releases_fixture) + .transpose()?; let request = json!({ "owner": owner, "repo": repo, @@ -1241,7 +1244,7 @@ struct AutogenGithubPreviewArgs { struct ProviderGithubReleasesArgs { owner: String, repo: String, - releases: PathBuf, + releases: Option, asset_include: Option, asset_exclude: Option, include_prereleases: bool, @@ -1414,7 +1417,7 @@ fn parse_provider_github_releases_args( "provider github releases --releases requires a fixture path".to_owned(), ) })?; - parsed.releases = PathBuf::from(path); + parsed.releases = Some(PathBuf::from(path)); position += 2; } "--asset-include" => { @@ -1469,12 +1472,6 @@ fn parse_provider_github_releases_args( "provider github releases requires --repo ".to_owned(), )); } - if parsed.releases.as_os_str().is_empty() { - return Err(CliError::Usage( - "provider github releases requires --releases ".to_owned(), - )); - } - Ok(parsed) } @@ -2249,7 +2246,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo --releases [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo [--releases ] [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -2379,6 +2376,69 @@ mod tests { ); } + #[test] + fn parses_provider_github_releases_command_with_optional_fixture() { + let parsed = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "provider", + "github", + "releases", + "--owner", + "DUpdateSystem", + "--repo", + "UpgradeAll", + "--releases", + "/tmp/releases.json", + "--asset-include", + "UpgradeAll_.*[.]apk$", + "--asset-exclude", + "debug", + "--include-prereleases", + "--refresh", + ]) + .unwrap(); + assert_eq!( + parsed.command, + CliCommand::ProviderGithubReleases { + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + releases: Some(PathBuf::from("/tmp/releases.json")), + asset_include: Some("UpgradeAll_.*[.]apk$".to_owned()), + asset_exclude: Some("debug".to_owned()), + include_prereleases: true, + refresh: true, + } + ); + + let live = parse_args([ + "getter", + "--data-dir", + "/tmp/ua-getter", + "provider", + "github", + "releases", + "--owner", + "DUpdateSystem", + "--repo", + "UpgradeAll", + ]) + .unwrap(); + assert_eq!( + live.command, + CliCommand::ProviderGithubReleases { + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + releases: None, + asset_include: None, + asset_exclude: None, + include_prereleases: false, + refresh: false, + } + ); + } + #[test] fn parses_provider_github_latest_commit_command() { let parsed = parse_args([ diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index 81c2e82..0d51558 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -7,6 +7,7 @@ edition.workspace = true default = [] lua = ["getter-core/lua", "dep:mlua"] lua-provider-host-dev = ["lua"] +rustls-platform-verifier = ["ureq/platform-verifier"] [dependencies] json_comments = "0.2" @@ -18,6 +19,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" sha2 = "0.10" thiserror = "1" +ureq = { version = "3.3.0", default-features = false, features = ["gzip", "rustls"] } [dev-dependencies] tempfile = "3" diff --git a/crates/getter-operations/src/github_releases.rs b/crates/getter-operations/src/github_releases.rs index cdac38b..32f700c 100644 --- a/crates/getter-operations/src/github_releases.rs +++ b/crates/getter-operations/src/github_releases.rs @@ -1,9 +1,9 @@ -//! Fixture-backed GitHub release cache/query operations. +//! GitHub release cache/query operations. //! -//! This is the first getter-owned GitHub provider slice. It parses controlled -//! GitHub REST release fixtures, runs them through the shared provider cache, -//! and normalizes release assets into getter update candidates without adding -//! live HTTP, Flutter/Kotlin provider parsing, downloader, or installer logic. +//! Getter owns both fixture-backed and live GitHub REST release refreshes, +//! runs them through the shared provider cache, and normalizes release assets +//! into getter update candidates without moving provider parsing, transport +//! controls, downloader, or installer logic into Flutter/Kotlin. use crate::provider_cache::{ read_or_refresh_provider_response_with_provenance, source_response_sha512, @@ -18,11 +18,13 @@ use getter_storage::{CacheDb, StorageError}; use serde::Deserialize; use serde_json::{json, Value}; use sha2::{Digest, Sha512}; +use std::time::Duration; pub const GITHUB_PROVIDER_ID: &str = "github"; pub const GITHUB_RELEASES_CACHE_VERSION: &str = "github-releases-v1"; pub const DEFAULT_GITHUB_API_BASE_URL: &str = "https://api.github.com"; pub const GITHUB_ASSET_NOT_FOUND: &str = "provider.github.asset_not_found"; +const GITHUB_RELEASE_TRANSPORT_TIMEOUT: Duration = Duration::from_secs(30); #[derive(Debug, Clone, PartialEq, Eq)] pub struct GithubReleaseConfig { @@ -53,6 +55,110 @@ pub struct GithubReleaseResult { pub diagnostics: Vec, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct GithubReleaseTransportRequest<'a> { + pub api_base_url: &'a str, + pub owner: &'a str, + pub repo: &'a str, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GithubReleaseTransportResponse { + pub body: String, + pub etag: Option, + pub last_modified: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum GithubReleaseTransportError { + #[error("GitHub release transport failed: {0}")] + Transport(String), + #[error("GitHub release transport returned HTTP {status}: {message}")] + HttpStatus { status: u16, message: String }, +} + +pub trait GithubReleaseTransport { + fn fetch_releases( + &self, + request: &GithubReleaseTransportRequest<'_>, + ) -> Result; +} + +pub struct UreqGithubReleaseTransport { + agent: ureq::Agent, +} + +impl Default for UreqGithubReleaseTransport { + fn default() -> Self { + #[cfg(feature = "rustls-platform-verifier")] + { + return Self { + agent: ureq::Agent::config_builder() + .timeout_global(Some(GITHUB_RELEASE_TRANSPORT_TIMEOUT)) + .tls_config( + ureq::tls::TlsConfig::builder() + .root_certs(ureq::tls::RootCerts::PlatformVerifier) + .build(), + ) + .build() + .new_agent(), + }; + } + + #[cfg(not(feature = "rustls-platform-verifier"))] + { + Self { + agent: ureq::Agent::config_builder() + .timeout_global(Some(GITHUB_RELEASE_TRANSPORT_TIMEOUT)) + .build() + .new_agent(), + } + } + } +} + +impl UreqGithubReleaseTransport { + pub fn new() -> Self { + Self::default() + } +} + +impl GithubReleaseTransport for UreqGithubReleaseTransport { + fn fetch_releases( + &self, + request: &GithubReleaseTransportRequest<'_>, + ) -> Result { + let url = github_releases_url(request.api_base_url, request.owner, request.repo); + let mut response = self + .agent + .get(&url) + .header("Accept", "application/vnd.github+json") + .header("X-GitHub-Api-Version", "2022-11-28") + .header("User-Agent", "UpgradeAll-getter") + .call() + .map_err(github_transport_error)?; + let etag = response + .headers() + .get("etag") + .and_then(|value| value.to_str().ok()) + .map(str::to_owned); + let last_modified = response + .headers() + .get("last-modified") + .and_then(|value| value.to_str().ok()) + .map(str::to_owned); + let body = response + .body_mut() + .read_to_string() + .map_err(|source| GithubReleaseTransportError::Transport(source.to_string()))?; + Ok(GithubReleaseTransportResponse { + body, + etag, + last_modified, + }) + } +} + #[derive(Debug, thiserror::Error)] pub enum GithubReleaseOperationError { #[error("provider cache operation failed: {0}")] @@ -75,6 +181,76 @@ pub fn read_or_refresh_github_releases( ) -> Result where F: FnOnce() -> Result, +{ + read_or_refresh_github_releases_with_refresh(db, config, mode, || { + let body = refresh_json()?; + github_release_refresh_from_body(body, json!({})) + }) +} + +pub fn read_or_refresh_github_releases_from_default_transport( + db: &CacheDb, + config: GithubReleaseConfig, + mode: ProviderCacheMode, +) -> Result { + read_or_refresh_github_releases_from_transport( + db, + config, + mode, + &UreqGithubReleaseTransport::new(), + ) +} + +pub fn read_or_refresh_github_releases_from_transport( + db: &CacheDb, + config: GithubReleaseConfig, + mode: ProviderCacheMode, + transport: &T, +) -> Result +where + T: GithubReleaseTransport + ?Sized, +{ + read_or_refresh_github_releases_from_transport_checked(db, config, mode, transport, |_| Ok(())) +} + +pub fn read_or_refresh_github_releases_from_transport_checked( + db: &CacheDb, + config: GithubReleaseConfig, + mode: ProviderCacheMode, + transport: &T, + validate_body: F, +) -> Result +where + T: GithubReleaseTransport + ?Sized, + F: FnOnce(&str) -> Result<(), String>, +{ + let refresh_config = config.clone(); + read_or_refresh_github_releases_with_refresh(db, config, mode, || { + let request = GithubReleaseTransportRequest { + api_base_url: &refresh_config.api_base_url, + owner: &refresh_config.owner, + repo: &refresh_config.repo, + }; + let response = transport + .fetch_releases(&request) + .map_err(|source| source.to_string())?; + validate_body(&response.body)?; + let freshness_json = json!({ + "etag": response.etag, + "last_modified": response.last_modified, + }); + github_release_refresh_from_body(response.body, freshness_json) + }) +} + +fn read_or_refresh_github_releases_with_refresh( + db: &CacheDb, + config: GithubReleaseConfig, + mode: ProviderCacheMode, + refresh: F, +) -> Result +where + F: FnOnce() -> Result, { let cache_key = config.cache_key(); let cache_result = read_or_refresh_provider_response_with_provenance( @@ -84,19 +260,7 @@ where provider: GITHUB_PROVIDER_ID, mode, }, - || { - let json = refresh_json()?; - let source_digest = source_response_sha512(json.as_bytes()); - let releases = - parse_github_releases_json(&json).map_err(|source| source.to_string())?; - let response_json = - serde_json::to_value(releases).map_err(|source| source.to_string())?; - Ok(ProviderResponseRefresh { - response_json, - source_response_sha512: vec![source_digest], - freshness_json: serde_json::json!({}), - }) - }, + refresh, )?; let response = cache_result.response; let releases = serde_json::from_value(response.response_json)?; @@ -112,10 +276,61 @@ where }) } +fn github_releases_url(api_base_url: &str, owner: &str, repo: &str) -> String { + format!( + "{}/repos/{owner}/{repo}/releases", + api_base_url.trim_end_matches('/') + ) +} + +fn github_transport_error(source: ureq::Error) -> GithubReleaseTransportError { + match source { + ureq::Error::StatusCode(status) => GithubReleaseTransportError::HttpStatus { + status, + message: github_http_status_message(status).to_owned(), + }, + other => GithubReleaseTransportError::Transport(other.to_string()), + } +} + +fn github_http_status_message(status: u16) -> &'static str { + match status { + 403 | 429 => "GitHub API rate limit or access restriction", + 404 => "GitHub repository or release endpoint was not found", + 401 => "GitHub API authentication is required", + _ => "GitHub API request failed", + } +} + +fn github_release_refresh_from_body( + body: String, + freshness_json: Value, +) -> Result { + let source_digest = source_response_sha512(body.as_bytes()); + let releases = parse_github_releases_json(&body).map_err(|source| source.to_string())?; + let response_json = serde_json::to_value(releases).map_err(|source| source.to_string())?; + Ok(ProviderResponseRefresh { + response_json, + source_response_sha512: vec![source_digest], + freshness_json, + }) +} + pub fn github_releases_json( db: &CacheDb, request_json: &str, ) -> Result { + github_releases_json_with_transport(db, request_json, &UreqGithubReleaseTransport::new()) +} + +pub fn github_releases_json_with_transport( + db: &CacheDb, + request_json: &str, + transport: &T, +) -> Result +where + T: GithubReleaseTransport + ?Sized, +{ let request: GithubReleasesJsonRequest = serde_json::from_str(request_json)?; let owner = required_non_empty(request.owner, "owner")?; let repo = required_non_empty(request.repo, "repo")?; @@ -144,11 +359,11 @@ pub fn github_releases_json( exclude: asset_filter.exclude, }, }; - let result = read_or_refresh_github_releases(db, config, mode, || { - request.releases_json.ok_or_else(|| { - "fixture-backed GitHub release refresh requires releases_json".to_owned() - }) - })?; + let result = if let Some(releases_json) = request.releases_json { + read_or_refresh_github_releases(db, config, mode, || Ok(releases_json)) + } else { + read_or_refresh_github_releases_from_transport(db, config, mode, transport) + }?; let candidates = github_release_update_candidates(&result.releases, &options)?; let mut diagnostics = result .diagnostics @@ -253,10 +468,13 @@ fn digest_hex(value: &str) -> String { #[cfg(test)] mod tests { use super::*; - use crate::provider_cache::{CACHE_REFRESH_FAILED, USED_STALE_CACHE}; + use crate::provider_cache::{source_response_sha512, CACHE_REFRESH_FAILED, USED_STALE_CACHE}; use getter_storage::ProviderResponseUpsert; use serde_json::json; - use std::cell::Cell; + use std::cell::{Cell, RefCell}; + use std::io::{Read, Write}; + use std::net::TcpListener; + use std::thread; const GITHUB_RELEASES_FIXTURE: &str = r#"[ { @@ -326,6 +544,189 @@ mod tests { assert_eq!(cached.response_json[0]["tag_name"], "v1.2.0"); } + #[test] + fn cache_miss_fetches_live_github_releases_through_transport() { + let db = CacheDb::open_in_memory().unwrap(); + let config = GithubReleaseConfig { + api_base_url: "https://api.github.test/".to_owned(), + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + }; + let cache_key = config.cache_key(); + let transport = MockGithubReleaseTransport { + response: RefCell::new(Ok(GithubReleaseTransportResponse { + body: GITHUB_RELEASES_FIXTURE.to_owned(), + etag: Some("W/\"release-snapshot\"".to_owned()), + last_modified: Some("Wed, 01 Jul 2026 00:00:00 GMT".to_owned()), + })), + requests: RefCell::new(Vec::new()), + }; + + let result = read_or_refresh_github_releases_from_transport( + &db, + config, + ProviderCacheMode::UseCached, + &transport, + ) + .unwrap(); + + assert_eq!(transport.requests.borrow().len(), 1); + let request = &transport.requests.borrow()[0]; + assert_eq!(request.api_base_url, "https://api.github.test/"); + assert_eq!(request.owner, "DUpdateSystem"); + assert_eq!(request.repo, "UpgradeAll"); + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + assert_eq!( + result.source_response_sha512, + vec![source_response_sha512(GITHUB_RELEASES_FIXTURE.as_bytes())] + ); + let cached = db.provider_response(&cache_key).unwrap().unwrap(); + assert_eq!(cached.provider, GITHUB_PROVIDER_ID); + assert_eq!(cached.response_json[0]["tag_name"], "v1.2.0"); + assert_eq!(cached.freshness_json["etag"], "W/\"release-snapshot\""); + assert_eq!( + cached.freshness_json["last_modified"], + "Wed, 01 Jul 2026 00:00:00 GMT" + ); + } + + #[test] + fn default_transport_fetches_releases_from_mock_http_server() { + let db = CacheDb::open_in_memory().unwrap(); + let (base_url, handle) = serve_one_github_response( + 200, + &[ + ("ETag", "W/\"mock-releases\""), + ("Last-Modified", "Wed, 01 Jul 2026 00:00:00 GMT"), + ("Content-Type", "application/json"), + ], + GITHUB_RELEASES_FIXTURE, + ); + let config = GithubReleaseConfig { + api_base_url: base_url, + owner: "DUpdateSystem".to_owned(), + repo: "UpgradeAll".to_owned(), + }; + + let result = read_or_refresh_github_releases_from_default_transport( + &db, + config, + ProviderCacheMode::UseCached, + ) + .unwrap(); + let request = handle.join().unwrap(); + + assert!(request.starts_with("GET /repos/DUpdateSystem/UpgradeAll/releases HTTP/1.1")); + assert!(request.contains("accept: application/vnd.github+json")); + assert!(request.contains("x-github-api-version: 2022-11-28")); + assert!(request.contains("user-agent: UpgradeAll-getter")); + assert_eq!(result.source, ProviderCacheSource::Refreshed); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + assert_eq!(result.source_response_sha512.len(), 1); + } + + #[test] + fn transport_cache_hit_does_not_call_live_refresh() { + let db = CacheDb::open_in_memory().unwrap(); + let config = config(); + read_or_refresh_github_releases(&db, config.clone(), ProviderCacheMode::UseCached, || { + Ok(GITHUB_RELEASES_FIXTURE.to_owned()) + }) + .unwrap(); + let transport = MockGithubReleaseTransport { + response: RefCell::new(Err(GithubReleaseTransportError::Transport( + "transport should not run".to_owned(), + ))), + requests: RefCell::new(Vec::new()), + }; + + let result = read_or_refresh_github_releases_from_transport( + &db, + config, + ProviderCacheMode::UseCached, + &transport, + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Cache); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + assert!(transport.requests.borrow().is_empty()); + } + + #[test] + fn transport_refresh_failure_without_stale_cache_is_provider_error() { + let db = CacheDb::open_in_memory().unwrap(); + let transport = MockGithubReleaseTransport { + response: RefCell::new(Err(GithubReleaseTransportError::HttpStatus { + status: 429, + message: "GitHub API rate limit or access restriction".to_owned(), + })), + requests: RefCell::new(Vec::new()), + }; + + let error = read_or_refresh_github_releases_from_transport( + &db, + config(), + ProviderCacheMode::ForceRefresh, + &transport, + ) + .unwrap_err(); + + assert!(matches!( + error, + GithubReleaseOperationError::Cache(ProviderCacheOperationError::RefreshFailed(_)) + )); + assert!(error.to_string().contains("HTTP 429")); + } + + #[test] + fn forced_transport_refresh_failure_returns_stale_cache_with_diagnostics() { + let db = CacheDb::open_in_memory().unwrap(); + let config = config(); + let cache_key = config.cache_key(); + db.upsert_provider_response(&ProviderResponseUpsert { + cache_key, + provider: GITHUB_PROVIDER_ID.to_owned(), + response_json: serde_json::from_str(GITHUB_RELEASES_FIXTURE).unwrap(), + source_response_sha512: vec![source_response_sha512( + GITHUB_RELEASES_FIXTURE.as_bytes(), + )], + provenance_schema_version: Some( + crate::provider_cache::PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1.to_owned(), + ), + freshness_json: serde_json::json!({}), + }) + .unwrap(); + let transport = MockGithubReleaseTransport { + response: RefCell::new(Err(GithubReleaseTransportError::HttpStatus { + status: 429, + message: "GitHub API rate limit or access restriction".to_owned(), + })), + requests: RefCell::new(Vec::new()), + }; + + let result = read_or_refresh_github_releases_from_transport( + &db, + config, + ProviderCacheMode::ForceRefresh, + &transport, + ) + .unwrap(); + + assert_eq!(result.source, ProviderCacheSource::Stale); + assert_eq!(result.releases[0].tag_name, "v1.2.0"); + let codes: Vec<_> = result + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect(); + assert_eq!(codes, vec![CACHE_REFRESH_FAILED, USED_STALE_CACHE]); + assert!(result.diagnostics[0] + .message + .contains("HTTP 429: GitHub API rate limit")); + } + #[test] fn cache_hit_avoids_refreshing_fixture_releases() { let db = CacheDb::open_in_memory().unwrap(); @@ -419,6 +820,35 @@ mod tests { assert!(response["diagnostics"].as_array().unwrap().is_empty()); } + #[test] + fn json_operation_without_fixture_uses_live_transport() { + let db = CacheDb::open_in_memory().unwrap(); + let transport = MockGithubReleaseTransport { + response: RefCell::new(Ok(GithubReleaseTransportResponse { + body: GITHUB_RELEASES_FIXTURE.to_owned(), + etag: Some("W/\"json-live\"".to_owned()), + last_modified: None, + })), + requests: RefCell::new(Vec::new()), + }; + let request = json!({ + "owner": "example", + "repo": "app", + "asset": { "include": "\\.apk$", "exclude": "debug" } + }); + + let response = + github_releases_json_with_transport(&db, &request.to_string(), &transport).unwrap(); + + assert_eq!(transport.requests.borrow().len(), 1); + assert_eq!(response["source"], "refreshed"); + assert_eq!(response["candidates"][0]["version"], "v1.2.0"); + assert_eq!(response["diagnostics"], json!([])); + let cache_key = response["cache_key"].as_str().unwrap(); + let cached = db.provider_response(cache_key).unwrap().unwrap(); + assert_eq!(cached.freshness_json["etag"], "W/\"json-live\""); + } + #[test] fn json_operation_reports_asset_not_found_diagnostic() { let db = CacheDb::open_in_memory().unwrap(); @@ -451,4 +881,64 @@ mod tests { GithubReleaseOperationError::Cache(ProviderCacheOperationError::RefreshFailed(_)) )); } + + #[derive(Debug, Clone, PartialEq, Eq)] + struct RecordedGithubReleaseRequest { + api_base_url: String, + owner: String, + repo: String, + } + + struct MockGithubReleaseTransport { + response: RefCell>, + requests: RefCell>, + } + + impl GithubReleaseTransport for MockGithubReleaseTransport { + fn fetch_releases( + &self, + request: &GithubReleaseTransportRequest<'_>, + ) -> Result { + self.requests + .borrow_mut() + .push(RecordedGithubReleaseRequest { + api_base_url: request.api_base_url.to_owned(), + owner: request.owner.to_owned(), + repo: request.repo.to_owned(), + }); + self.response.borrow().clone() + } + } + + fn serve_one_github_response( + status: u16, + headers: &[(&str, &str)], + body: &'static str, + ) -> (String, thread::JoinHandle) { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let headers = headers + .iter() + .map(|(name, value)| (name.to_string(), value.to_string())) + .collect::>(); + let handle = thread::spawn(move || { + let (mut stream, _) = listener.accept().unwrap(); + let mut buffer = [0; 4096]; + let bytes = stream.read(&mut buffer).unwrap(); + let request = String::from_utf8_lossy(&buffer[..bytes]).into_owned(); + let reason = if status == 200 { "OK" } else { "Error" }; + write!( + stream, + "HTTP/1.1 {status} {reason}\r\nContent-Length: {}\r\nConnection: close\r\n", + body.len() + ) + .unwrap(); + for (name, value) in headers { + write!(stream, "{name}: {value}\r\n").unwrap(); + } + write!(stream, "\r\n{body}").unwrap(); + request + }); + (format!("http://{address}"), handle) + } } diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 12756cf..4e0f8da 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -10,8 +10,11 @@ use crate::fdroid_catalog::{ read_or_refresh_fdroid_catalog, FdroidCatalogOperationError, FdroidEndpointConfig, DEFAULT_FDROID_ENDPOINT_ID, DEFAULT_FDROID_ENDPOINT_URL, FDROID_PROVIDER_ID, }; +#[cfg(feature = "lua-provider-host-dev")] +use crate::github_releases::UreqGithubReleaseTransport; use crate::github_releases::{ - read_or_refresh_github_releases, GithubReleaseConfig, GithubReleaseOperationError, + read_or_refresh_github_releases, read_or_refresh_github_releases_from_transport_checked, + GithubReleaseConfig, GithubReleaseOperationError, GithubReleaseTransport, DEFAULT_GITHUB_API_BASE_URL, GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, }; use crate::lua_runtime_hooks::load_runtime_hooks; @@ -296,6 +299,7 @@ pub fn stable_provider_package_eval_json( github_api_base_url: request.github_api_base_url, github_releases_json: request.github_releases_json, github_include_prereleases: request.github_include_prereleases, + github_release_transport: Some(Rc::new(UreqGithubReleaseTransport::new())), }, )?; let package = serde_json::to_value(result.package)?; @@ -399,11 +403,12 @@ struct GithubProviderHostConfig { api_base_url: String, mode: ProviderCacheMode, releases_json: Option, + release_transport: Option>, default_include_prereleases: bool, manifest: Option, } -#[derive(Debug, Clone)] +#[derive(Clone)] pub(crate) struct ProviderBackedPackageEvalConfig { pub(crate) mode: ProviderCacheMode, pub(crate) fdroid_endpoint_id: Option, @@ -412,6 +417,7 @@ pub(crate) struct ProviderBackedPackageEvalConfig { pub(crate) github_endpoint_id: Option, pub(crate) github_api_base_url: Option, pub(crate) github_releases_json: Option, + pub(crate) github_release_transport: Option>, pub(crate) github_include_prereleases: bool, } @@ -479,6 +485,7 @@ pub(crate) fn evaluate_provider_backed_package( api_base_url: github_api_base_url, mode: config.mode, releases_json: config.github_releases_json, + release_transport: config.github_release_transport, default_include_prereleases: config.github_include_prereleases, manifest, }, @@ -565,6 +572,7 @@ fn install_github_dev_host(lua: &Lua, config: GithubDevHostConfig) -> mlua::Resu api_base_url: config.api_base_url.clone(), mode: config.mode, releases_json: config.releases_json.clone(), + release_transport: None, default_include_prereleases: config.default_include_prereleases, manifest: None, }; @@ -797,16 +805,27 @@ fn github_release_candidates_envelope( repo: request.repo, }; let refresh_fixture = config.releases_json; + let refresh_transport = config.release_transport; let manifest = config.manifest.clone(); - let result = read_or_refresh_github_releases(&db, provider_config, config.mode, || { - let releases = refresh_fixture.ok_or_else(|| { - "GitHub release provider host has no cached releases and no refresh source installed" - .to_owned() - })?; - require_manifest_body(&manifest, GITHUB_PROVIDER_ID, &releases) - .map_err(|source| source.to_string())?; - Ok(releases) - }) + let result = if let Some(releases) = refresh_fixture { + read_or_refresh_github_releases(&db, provider_config, config.mode, || { + require_manifest_body(&manifest, GITHUB_PROVIDER_ID, &releases) + .map_err(|source| source.to_string())?; + Ok(releases) + }) + } else if let Some(transport) = refresh_transport { + read_or_refresh_github_releases_from_transport_checked( + &db, + provider_config, + config.mode, + transport.as_ref(), + |body| require_manifest_body(&manifest, GITHUB_PROVIDER_ID, body).map_err(|source| source.to_string()), + ) + } else { + read_or_refresh_github_releases(&db, provider_config, config.mode, || { + Err("GitHub release provider host has no cached releases and no refresh source installed".to_owned()) + }) + } .map_err(mlua::Error::external)?; reject_unproven_manifest_cache( &manifest, diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 08d8c88..8c5f971 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -6,6 +6,8 @@ //! persisted task state or duplicating task/control semantics in Flutter or //! Android glue. +#[cfg(feature = "lua")] +use crate::github_releases::{GithubReleaseTransport, UreqGithubReleaseTransport}; #[cfg(feature = "lua")] use crate::lua_provider_host::{ evaluate_provider_backed_package, LuaProviderHostOperationError, @@ -35,6 +37,8 @@ use serde::Deserialize; use serde_json::{json, Value}; #[cfg(feature = "lua")] use std::path::PathBuf; +#[cfg(feature = "lua")] +use std::rc::Rc; #[derive(Debug, thiserror::Error)] pub enum RuntimeOperationError { @@ -161,9 +165,26 @@ pub fn issue_action_from_registered_package_json( data_dir: &std::path::Path, db: &MainDb, request_json: &str, +) -> Result { + issue_action_from_registered_package_json_with_github_transport( + runtime, + data_dir, + db, + request_json, + Some(Rc::new(UreqGithubReleaseTransport::new())), + ) +} + +#[cfg(feature = "lua")] +fn issue_action_from_registered_package_json_with_github_transport( + runtime: &mut GetterRuntime, + data_dir: &std::path::Path, + db: &MainDb, + request_json: &str, + github_release_transport: Option>, ) -> Result { let request: RegisteredPackageUpdateActionRequest = parse_request(request_json)?; - let evaluated = evaluate_registered_package(data_dir, db, &request)?; + let evaluated = evaluate_registered_package(data_dir, db, &request, github_release_transport)?; let candidates = StaticPackageUpdatesProvider.check_updates(&evaluated.package); let update = check_updates_offline( evaluated.package.id.clone(), @@ -342,6 +363,7 @@ struct OfflineUpdateActionRequest { #[cfg(feature = "lua")] #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] struct RegisteredPackageUpdateActionRequest { package_id: PackageId, #[serde(default)] @@ -405,6 +427,7 @@ fn evaluate_registered_package( data_dir: &std::path::Path, db: &MainDb, request: &RegisteredPackageUpdateActionRequest, + github_release_transport: Option>, ) -> Result { let repositories = db.repositories()?; let mut missing_path = None; @@ -441,6 +464,7 @@ fn evaluate_registered_package( github_endpoint_id: None, github_api_base_url: None, github_releases_json: None, + github_release_transport: github_release_transport.clone(), github_include_prereleases: false, }, )?; @@ -497,7 +521,9 @@ mod tests { use crate::fdroid_catalog::{FdroidEndpointConfig, FDROID_PROVIDER_ID}; #[cfg(feature = "lua")] use crate::github_releases::{ - GithubReleaseConfig, DEFAULT_GITHUB_API_BASE_URL, GITHUB_PROVIDER_ID, + GithubReleaseConfig, GithubReleaseTransport, GithubReleaseTransportError, + GithubReleaseTransportRequest, GithubReleaseTransportResponse, DEFAULT_GITHUB_API_BASE_URL, + GITHUB_PROVIDER_ID, }; #[cfg(feature = "lua")] use crate::provider_cache::PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1; @@ -516,7 +542,11 @@ mod tests { #[cfg(feature = "lua")] use sha2::{Digest, Sha512}; #[cfg(feature = "lua")] + use std::cell::RefCell; + #[cfg(feature = "lua")] use std::fs; + #[cfg(feature = "lua")] + use std::rc::Rc; #[cfg(feature = "lua")] const FDROID_INDEX_FIXTURE: &str = r#" @@ -725,6 +755,140 @@ mod tests { assert_eq!(issued["update"]["actions"][0]["file_name"], "F-Droid.apk"); } + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_refreshes_github_cache_on_miss_with_getter_transport() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + write_github_provider_package_repo(&repo_root, GITHUB_RELEASES_FIXTURE); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + let transport = + github_transport_with_body(GITHUB_RELEASES_FIXTURE, Some("W/\"runtime-github\"")); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json_with_github_transport( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/app/org.fdroid.fdroid", + "installed_version": "v1.0.0" + }) + .to_string(), + Some(transport.clone()), + ) + .unwrap(); + + assert_eq!(transport.requests.borrow().len(), 1); + assert_eq!(transport.requests.borrow()[0].owner, "f-droid"); + assert_eq!(transport.requests.borrow()[0].repo, "fdroidclient"); + assert_eq!(issued["provider_calls"][0]["provider"], "github"); + assert_eq!(issued["provider_calls"][0]["source"], "refreshed"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + "v1.20.0" + ); + let config = GithubReleaseConfig { + api_base_url: DEFAULT_GITHUB_API_BASE_URL.to_owned(), + owner: "f-droid".to_owned(), + repo: "fdroidclient".to_owned(), + }; + let cached = CacheDb::open(data_dir.join("cache.db")) + .unwrap() + .provider_response(&config.cache_key()) + .unwrap() + .unwrap(); + assert_eq!(cached.response_json[0]["tag_name"], "v1.20.0"); + assert_eq!(cached.freshness_json["etag"], "W/\"runtime-github\""); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_live_github_refresh_allows_free_network_without_manifest() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); + write_github_provider_package_with_options( + &package_dir, + None, + true, + "F-Droid", + "org.fdroid.fdroid", + "f-droid", + "fdroidclient", + ); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + let transport = github_transport_with_body(GITHUB_RELEASES_FIXTURE, None); + let mut runtime = GetterRuntime::new(); + + let issued = issue_action_from_registered_package_json_with_github_transport( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/app/org.fdroid.fdroid", + "installed_version": "v1.0.0" + }) + .to_string(), + Some(transport.clone()), + ) + .unwrap(); + + assert_eq!(transport.requests.borrow().len(), 1); + assert_eq!(issued["provider_calls"][0]["source"], "refreshed"); + assert_eq!(issued["update"]["status"], "update_available"); + assert_eq!( + issued["update"]["selected"]["candidate"]["version"], + "v1.20.0" + ); + } + + #[cfg(feature = "lua")] + #[test] + fn registered_package_update_check_rejects_unmanifested_live_github_refresh() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let repo_root = data_dir.join("repo/official"); + let package_dir = repo_root.join("android/app/org.fdroid.fdroid"); + write_github_provider_package_with_options( + &package_dir, + None, + false, + "F-Droid", + "org.fdroid.fdroid", + "f-droid", + "fdroidclient", + ); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + register_repository(&db, "official", "Official", 0, &repo_root); + let transport = github_transport_with_body(GITHUB_RELEASES_FIXTURE, None); + let mut runtime = GetterRuntime::new(); + + let err = issue_action_from_registered_package_json_with_github_transport( + &mut runtime, + data_dir, + &db, + &json!({ + "package_id": "android/app/org.fdroid.fdroid", + "installed_version": "v1.0.0" + }) + .to_string(), + Some(transport), + ) + .unwrap_err(); + + assert_eq!(err.code(), "package.eval_error"); + assert!(err + .to_string() + .contains("package.provider.response_not_in_manifest")); + assert_eq!(runtime.tasks().len(), 0); + } + #[cfg(feature = "lua")] #[test] fn registered_package_update_check_matches_old_upgradeall_github_release_snapshot() { @@ -826,10 +990,8 @@ mod tests { ) .unwrap_err(); - assert_eq!(err.code(), "package.eval_error"); - assert!(err - .to_string() - .contains("F-Droid provider host has no cached catalog")); + assert_eq!(err.code(), "runtime.invalid_request"); + assert!(err.to_string().contains("unknown field `fdroid_index_xml`")); assert_eq!(runtime.tasks().len(), 0); } @@ -1142,23 +1304,50 @@ return fdroid.package { android_package: &str, owner: &str, repo: &str, + ) { + write_github_provider_package_with_options( + package_dir, + Some(manifest_body), + false, + name, + android_package, + owner, + repo, + ); + } + + #[cfg(feature = "lua")] + fn write_github_provider_package_with_options( + package_dir: &std::path::Path, + manifest_body: Option<&str>, + allow_free_network: bool, + name: &str, + android_package: &str, + owner: &str, + repo: &str, ) { fs::create_dir_all(package_dir).unwrap(); + let mut metadata = json!({ + "type": "android:app", + "android": { "package_name": android_package } + }); + if allow_free_network { + metadata["lua"] = json!({ + "9999.lua": { "permission": ["allow_free_network"] } + }); + } fs::write( package_dir.join("metadata.jsonc"), - format!( - r#"{{ - "type": "android:app", - "android": {{ "package_name": "{android_package}" }} -}}"# - ), - ) - .unwrap(); - fs::write( - package_dir.join("Manifest"), - format!("{} fixture-body\n", sha512_hex(manifest_body.as_bytes())), + serde_json::to_string_pretty(&metadata).unwrap(), ) .unwrap(); + if let Some(manifest_body) = manifest_body { + fs::write( + package_dir.join("Manifest"), + format!("{} fixture-body\n", sha512_hex(manifest_body.as_bytes())), + ) + .unwrap(); + } fs::write( package_dir.join("9999.lua"), format!( @@ -1205,6 +1394,52 @@ return github_android.package {{ format!("{:x}", hasher.finalize()) } + #[cfg(feature = "lua")] + #[derive(Debug, Clone, PartialEq, Eq)] + struct RecordedGithubReleaseRequest { + api_base_url: String, + owner: String, + repo: String, + } + + #[cfg(feature = "lua")] + struct MockGithubReleaseTransport { + response: RefCell>, + requests: RefCell>, + } + + #[cfg(feature = "lua")] + fn github_transport_with_body( + body: &str, + etag: Option<&str>, + ) -> Rc { + Rc::new(MockGithubReleaseTransport { + response: RefCell::new(Ok(GithubReleaseTransportResponse { + body: body.to_owned(), + etag: etag.map(str::to_owned), + last_modified: Some("Wed, 01 Jul 2026 00:00:00 GMT".to_owned()), + })), + requests: RefCell::new(Vec::new()), + }) + } + + #[cfg(feature = "lua")] + impl GithubReleaseTransport for MockGithubReleaseTransport { + fn fetch_releases( + &self, + request: &GithubReleaseTransportRequest<'_>, + ) -> Result { + self.requests + .borrow_mut() + .push(RecordedGithubReleaseRequest { + api_base_url: request.api_base_url.to_owned(), + owner: request.owner.to_owned(), + repo: request.repo.to_owned(), + }); + self.response.borrow().clone() + } + } + #[cfg(feature = "lua")] fn write_package_directory_static_update_repo(root: &std::path::Path) { let package_dir = root.join("android/app/com.example.autogen"); From 3d96140e7203702456b8d58bd8dda004aee087e2 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 2 Jul 2026 21:06:13 +0800 Subject: [PATCH 64/71] feat(autogen): refresh GitHub autogen through getter transport --- .../getter-operations/src/github_autogen.rs | 130 +++++++++++++++--- 1 file changed, 110 insertions(+), 20 deletions(-) diff --git a/crates/getter-operations/src/github_autogen.rs b/crates/getter-operations/src/github_autogen.rs index 988a334..bd20d0f 100644 --- a/crates/getter-operations/src/github_autogen.rs +++ b/crates/getter-operations/src/github_autogen.rs @@ -1,14 +1,16 @@ -//! Fixture-backed GitHub Android APK autogen preview/apply operations. +//! GitHub Android APK autogen preview/apply operations. //! //! This module generates ordinary package directories that use the standard -//! `luaclass.github_android_apk` provider module. It consumes cached or -//! fixture-backed GitHub release facts owned by getter and deliberately avoids -//! live GitHub transport or Flutter/Kotlin provider parsing. +//! `luaclass.github_android_apk` provider module. It consumes cached, +//! fixture-backed, or getter-live GitHub release facts owned by getter and +//! deliberately avoids Flutter/Kotlin provider parsing, cache policy, or live +//! transport controls. use crate::autogen::{self, AutogenAcceptance, AutogenOperationError, AutogenOperationResult}; use crate::github_releases::{ - read_or_refresh_github_releases, GithubReleaseConfig, DEFAULT_GITHUB_API_BASE_URL, - GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, + read_or_refresh_github_releases, read_or_refresh_github_releases_from_transport, + GithubReleaseConfig, GithubReleaseTransport, UreqGithubReleaseTransport, + DEFAULT_GITHUB_API_BASE_URL, GITHUB_ASSET_NOT_FOUND, GITHUB_PROVIDER_ID, }; use crate::provider_cache::{ProviderCacheDiagnostic, ProviderCacheMode, ProviderCacheSource}; use getter_core::autogen::{ @@ -31,6 +33,25 @@ pub fn preview_github_android_package_json( cache_db: &CacheDb, request_json: &str, ) -> AutogenOperationResult { + preview_github_android_package_json_with_transport( + data_dir, + main_db, + cache_db, + request_json, + &UreqGithubReleaseTransport::new(), + ) +} + +pub fn preview_github_android_package_json_with_transport( + data_dir: &Path, + main_db: &MainDb, + cache_db: &CacheDb, + request_json: &str, + transport: &T, +) -> AutogenOperationResult +where + T: GithubReleaseTransport + ?Sized, +{ let request: GithubAutogenPreviewRequest = serde_json::from_str(request_json).map_err(|source| { AutogenOperationError::Autogen(format!( @@ -71,20 +92,16 @@ pub fn preview_github_android_package_json( asset_filter: asset_filter.clone(), }; let package_id = github_android_package_id(&owner, &repo, &android_package)?; - let result = read_or_refresh_github_releases( - cache_db, - GithubReleaseConfig { - api_base_url, - owner: owner.clone(), - repo: repo.clone(), - }, - mode, - || { - request.releases_json.ok_or_else(|| { - "fixture-backed GitHub autogen refresh requires releases_json".to_owned() - }) - }, - ) + let config = GithubReleaseConfig { + api_base_url, + owner: owner.clone(), + repo: repo.clone(), + }; + let result = if let Some(releases_json) = request.releases_json { + read_or_refresh_github_releases(cache_db, config, mode, || Ok(releases_json)) + } else { + read_or_refresh_github_releases_from_transport(cache_db, config, mode, transport) + } .map_err(|source| AutogenOperationError::Autogen(source.to_string()))?; let (target_alias, target_path, target_priority) = autogen::generated_repository_config(data_dir)?; @@ -468,6 +485,11 @@ mod tests { use getter_core::RepositoryPriority; use serde_json::{json, Value}; use sha2::{Digest, Sha512}; + use std::cell::RefCell; + + use crate::github_releases::{ + GithubReleaseTransport, GithubReleaseTransportRequest, GithubReleaseTransportResponse, + }; const UPGRADEALL_GITHUB_RELEASES: &str = include_str!("../../../tests/files/web/github_api_release.json"); @@ -541,6 +563,49 @@ mod tests { assert!(!temp.path().join("repo/autogen").exists()); } + #[test] + fn preview_without_fixture_refreshes_github_releases_through_getter_transport() { + let temp = tempfile::tempdir().unwrap(); + let main_db = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache_db = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockGithubReleaseTransport { + response: RefCell::new(Ok(GithubReleaseTransportResponse { + body: UPGRADEALL_GITHUB_RELEASES.to_owned(), + etag: Some("W/\"autogen-live\"".to_owned()), + last_modified: None, + })), + requests: RefCell::new(Vec::new()), + }; + let request = json!({ + "owner": "DUpdateSystem", + "repo": "UpgradeAll", + "android_package": "net.xzos.upgradeall", + "display_name": "UpgradeAll", + "asset": { "include": "UpgradeAll_.*[.]apk$" }, + }); + + let preview = preview_github_android_package_json_with_transport( + temp.path(), + &main_db, + &cache_db, + &request.to_string(), + &transport, + ) + .unwrap(); + + assert_eq!(transport.requests.borrow().len(), 1); + assert_eq!(preview["source"], "refreshed"); + assert_eq!( + preview["candidates"][0]["package_id"], + UPGRADEALL_PACKAGE_ID + ); + let cached = cache_db + .provider_response(preview["cache_key"].as_str().unwrap()) + .unwrap() + .unwrap(); + assert_eq!(cached.freshness_json["etag"], "W/\"autogen-live\""); + } + #[cfg(feature = "lua")] #[test] fn apply_writes_valid_github_package_directory_and_runtime_uses_cache() { @@ -725,6 +790,31 @@ mod tests { .unwrap_or_else(|| panic!("generated file {relative_path} not found: {files:?}")) } + struct MockGithubReleaseTransport { + response: RefCell>, + requests: RefCell>, + } + + impl GithubReleaseTransport for MockGithubReleaseTransport { + fn fetch_releases( + &self, + request: &GithubReleaseTransportRequest<'_>, + ) -> Result< + GithubReleaseTransportResponse, + crate::github_releases::GithubReleaseTransportError, + > { + self.requests.borrow_mut().push(( + request.api_base_url.to_owned(), + request.owner.to_owned(), + request.repo.to_owned(), + )); + self.response + .borrow() + .clone() + .map_err(crate::github_releases::GithubReleaseTransportError::Transport) + } + } + fn sha512_hex(body: &[u8]) -> String { let mut hasher = Sha512::new(); hasher.update(body); From d02b86fe5311b1c2762c31f326155bece179f5f7 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Fri, 3 Jul 2026 00:46:22 +0800 Subject: [PATCH 65/71] feat(runtime): download bytes through getter tasks --- crates/getter-core/src/runtime.rs | 149 +++++- crates/getter-operations/src/download.rs | 580 +++++++++++++++++++++++ crates/getter-operations/src/lib.rs | 1 + crates/getter-operations/src/runtime.rs | 147 +++++- 4 files changed, 856 insertions(+), 21 deletions(-) create mode 100644 crates/getter-operations/src/download.rs diff --git a/crates/getter-core/src/runtime.rs b/crates/getter-core/src/runtime.rs index 992978f..6736506 100644 --- a/crates/getter-core/src/runtime.rs +++ b/crates/getter-core/src/runtime.rs @@ -30,6 +30,30 @@ impl SealedActionPlan { .iter() .any(|action| matches!(action, UpdateAction::Install { .. })) } + + fn download_action(&self) -> Option { + self.actions.iter().find_map(|action| match action { + UpdateAction::Download { url, file_name } => Some(RuntimeDownloadPlan { + url: url.clone(), + file_name: file_name.clone(), + }), + _ => None, + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct RuntimeDownloadPlan { + pub url: String, + pub file_name: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DownloadedFile { + pub file_name: String, + pub local_path: String, + pub size_bytes: u64, + pub sha256: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -49,6 +73,8 @@ pub struct TaskSnapshot { pub capabilities: TaskCapabilities, #[serde(default)] pub current_diagnostic: Option, + #[serde(default)] + pub downloaded_file: Option, pub updated_at: u64, } @@ -293,6 +319,13 @@ impl GetterRuntime { .collect() } + pub fn download_plan( + &self, + task_id: &str, + ) -> Result, RuntimeError> { + Ok(self.task_ref(task_id)?.plan.download_action()) + } + pub fn start_task(&mut self, task_id: &str) -> Result { { let clock = self.tick(); @@ -329,27 +362,34 @@ impl GetterRuntime { } pub fn complete_download(&mut self, task_id: &str) -> Result { - let needs_install = { - let task = self.task_ref(task_id)?; - task.plan.has_install_action() - }; + self.complete_download_internal(task_id, None) + } - if needs_install { - self.enter_install_handoff(task_id) - } else { - { - let clock = self.tick(); - let task = self.task_mut(task_id)?; - if !matches!( - task.status, - RuntimeTaskStatus::Queued | RuntimeTaskStatus::Running - ) { - return Err(RuntimeError::RetryNotSupported(task_id.to_owned())); - } - task.complete(clock); - } - self.snapshot_and_notify(task_id) + pub fn complete_downloaded_file( + &mut self, + task_id: &str, + downloaded_file: DownloadedFile, + ) -> Result { + self.complete_download_internal(task_id, Some(downloaded_file)) + } + + pub fn fail_download( + &mut self, + task_id: &str, + code: impl Into, + message: impl Into, + ) -> Result { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + task.fail( + clock, + TaskPhase::with_reason(TaskPhaseCategory::Failed, TaskPhaseReason::DownloadFailed), + TaskDiagnostic::error(code, message), + RetryResume::Download, + ); } + self.snapshot_and_notify(task_id) } pub fn pause_task(&mut self, task_id: &str) -> Result { @@ -492,6 +532,43 @@ impl GetterRuntime { .collect() } + fn complete_download_internal( + &mut self, + task_id: &str, + downloaded_file: Option, + ) -> Result { + let needs_install = { + let task = self.task_ref(task_id)?; + task.plan.has_install_action() + }; + + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + if !matches!( + task.status, + RuntimeTaskStatus::Queued | RuntimeTaskStatus::Running + ) { + return Err(RuntimeError::RetryNotSupported(task_id.to_owned())); + } + if let Some(downloaded_file) = downloaded_file { + task.downloaded_file = Some(downloaded_file); + } + task.updated_at = clock; + } + + if needs_install { + self.enter_install_handoff(task_id) + } else { + { + let clock = self.tick(); + let task = self.task_mut(task_id)?; + task.complete(clock); + } + self.snapshot_and_notify(task_id) + } + } + fn enter_install_handoff(&mut self, task_id: &str) -> Result { let package_id = self.task_ref(task_id)?.plan.package_id.clone(); if self.package_locks.contains_key(&package_id) { @@ -575,6 +652,7 @@ struct RuntimeTask { phase: TaskPhase, progress: Option, current_diagnostic: Option, + downloaded_file: Option, retry_resume: RetryResume, updated_at: u64, } @@ -588,6 +666,7 @@ impl RuntimeTask { phase: TaskPhase::new(TaskPhaseCategory::Queued), progress: None, current_diagnostic: None, + downloaded_file: None, retry_resume: RetryResume::Download, updated_at, } @@ -602,6 +681,7 @@ impl RuntimeTask { progress: self.progress.clone(), capabilities: self.capabilities(), current_diagnostic: self.current_diagnostic.clone(), + downloaded_file: self.downloaded_file.clone(), updated_at: self.updated_at, } } @@ -641,6 +721,7 @@ impl RuntimeTask { total: None, }); self.current_diagnostic = None; + self.downloaded_file = None; self.updated_at = updated_at; } @@ -713,6 +794,36 @@ mod tests { assert_eq!(notifications.lock().unwrap().len(), 1); } + #[test] + fn runtime_exposes_download_plan_and_downloaded_file_snapshot() { + let mut runtime = GetterRuntime::new(); + let task_id = submit_plan(&mut runtime, plan_without_install("generic/example")); + + let download = runtime.download_plan(&task_id).unwrap().unwrap(); + assert_eq!(download.url, "https://example.invalid/file.bin"); + assert_eq!(download.file_name, "file.bin"); + + runtime.start_task(&task_id).unwrap(); + let completed = runtime + .complete_downloaded_file( + &task_id, + DownloadedFile { + file_name: "file.bin".to_owned(), + local_path: "/tmp/getter/downloads/task-1/file.bin".to_owned(), + size_bytes: 11, + sha256: "sha256-test".to_owned(), + }, + ) + .unwrap(); + + assert_eq!(completed.status, RuntimeTaskStatus::Completed); + assert_eq!(completed.downloaded_file.as_ref().unwrap().size_bytes, 11); + assert_eq!( + completed.downloaded_file.as_ref().unwrap().local_path, + "/tmp/getter/downloads/task-1/file.bin" + ); + } + #[test] fn fake_install_waits_for_generic_user_result() { let mut runtime = GetterRuntime::new(); diff --git a/crates/getter-operations/src/download.rs b/crates/getter-operations/src/download.rs new file mode 100644 index 0000000..9bd8e41 --- /dev/null +++ b/crates/getter-operations/src/download.rs @@ -0,0 +1,580 @@ +//! Getter-owned runtime download executor. +//! +//! This is the first real-byte side-effect executor for ADR-0011 tasks. It is +//! deliberately current-process/task-local: it writes bytes for the submitted +//! task and updates the in-memory runtime snapshot, but it does not create a +//! durable task database, background worker, installer, or recovery mechanism. + +use getter_core::runtime::{ + DownloadedFile, GetterRuntime, RuntimeError, TaskPhaseCategory, TaskSnapshot, +}; +use sha2::{Digest, Sha256}; +use std::fs::{self, File}; +use std::io::{Read, Write}; +use std::path::Path; +use std::time::Duration; + +const RUNTIME_DOWNLOAD_TRANSPORT_TIMEOUT: Duration = Duration::from_secs(300); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct RuntimeDownloadTransportRequest<'a> { + pub url: &'a str, +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum RuntimeDownloadTransportError { + #[error("download transport failed: {0}")] + Transport(String), + #[error("download transport returned HTTP {status}: {message}")] + HttpStatus { status: u16, message: String }, + #[error("download sink failed: {0}")] + Sink(String), +} + +pub trait RuntimeDownloadSink { + fn set_total_bytes(&mut self, total_bytes: Option) -> Result<(), String>; + fn write_chunk(&mut self, chunk: &[u8]) -> Result<(), String>; +} + +pub trait RuntimeDownloadTransport { + fn fetch( + &self, + request: &RuntimeDownloadTransportRequest<'_>, + sink: &mut dyn RuntimeDownloadSink, + ) -> Result<(), RuntimeDownloadTransportError>; +} + +pub struct UreqRuntimeDownloadTransport { + agent: ureq::Agent, +} + +impl Default for UreqRuntimeDownloadTransport { + fn default() -> Self { + #[cfg(feature = "rustls-platform-verifier")] + { + return Self { + agent: ureq::Agent::config_builder() + .timeout_global(Some(RUNTIME_DOWNLOAD_TRANSPORT_TIMEOUT)) + .tls_config( + ureq::tls::TlsConfig::builder() + .root_certs(ureq::tls::RootCerts::PlatformVerifier) + .build(), + ) + .build() + .new_agent(), + }; + } + + #[cfg(not(feature = "rustls-platform-verifier"))] + { + Self { + agent: ureq::Agent::config_builder() + .timeout_global(Some(RUNTIME_DOWNLOAD_TRANSPORT_TIMEOUT)) + .build() + .new_agent(), + } + } + } +} + +impl UreqRuntimeDownloadTransport { + pub fn new() -> Self { + Self::default() + } +} + +impl RuntimeDownloadTransport for UreqRuntimeDownloadTransport { + fn fetch( + &self, + request: &RuntimeDownloadTransportRequest<'_>, + sink: &mut dyn RuntimeDownloadSink, + ) -> Result<(), RuntimeDownloadTransportError> { + let mut response = self + .agent + .get(request.url) + .header("Accept", "application/octet-stream, */*") + .header("User-Agent", "UpgradeAll-getter") + .call() + .map_err(download_transport_error)?; + sink.set_total_bytes(response.body().content_length()) + .map_err(RuntimeDownloadTransportError::Sink)?; + let mut reader = response.body_mut().as_reader(); + let mut buffer = [0_u8; 64 * 1024]; + loop { + let read = reader + .read(&mut buffer) + .map_err(|source| RuntimeDownloadTransportError::Transport(source.to_string()))?; + if read == 0 { + break; + } + sink.write_chunk(&buffer[..read]) + .map_err(RuntimeDownloadTransportError::Sink)?; + } + Ok(()) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum RuntimeDownloadExecutionError { + #[error("task has no download action")] + MissingDownloadAction, + #[error("failed to prepare download file: {0}")] + PrepareFile(std::io::Error), + #[error("download failed: {0}")] + Transport(#[from] RuntimeDownloadTransportError), + #[error("failed to finish download file: {0}")] + FinishFile(std::io::Error), + #[error("runtime error: {0}")] + Runtime(#[from] RuntimeError), +} + +impl RuntimeDownloadExecutionError { + pub fn diagnostic_code(&self) -> &'static str { + match self { + Self::MissingDownloadAction => "download.action_missing", + Self::PrepareFile(_) | Self::FinishFile(_) => "download.file_error", + Self::Transport(RuntimeDownloadTransportError::HttpStatus { .. }) => { + "download.http_status" + } + Self::Transport(_) => "download.transport_error", + Self::Runtime(error) => error.code(), + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum RuntimeDownloadOperationError { + #[error("runtime error: {0}")] + Runtime(#[from] RuntimeError), +} + +pub fn submit_action_and_download( + runtime: &mut GetterRuntime, + data_dir: &Path, + action_id: &str, + transport: &T, +) -> Result +where + T: RuntimeDownloadTransport + ?Sized, +{ + let submitted = runtime.submit_action(action_id)?; + let task_id = submitted.task_id.clone(); + match execute_download_task(runtime, data_dir, &task_id, transport) { + Ok(task) => Ok(task), + Err(error) => { + Ok(runtime.fail_download(&task_id, error.diagnostic_code(), error.to_string())?) + } + } +} + +pub fn retry_download_task( + runtime: &mut GetterRuntime, + data_dir: &Path, + task_id: &str, + transport: &T, +) -> Result +where + T: RuntimeDownloadTransport + ?Sized, +{ + let retried = runtime.retry_task(task_id)?; + if retried.phase.category != TaskPhaseCategory::Download { + return Ok(retried); + } + match execute_download_task(runtime, data_dir, task_id, transport) { + Ok(task) => Ok(task), + Err(error) => { + Ok(runtime.fail_download(task_id, error.diagnostic_code(), error.to_string())?) + } + } +} + +pub fn execute_download_task( + runtime: &mut GetterRuntime, + data_dir: &Path, + task_id: &str, + transport: &T, +) -> Result +where + T: RuntimeDownloadTransport + ?Sized, +{ + runtime.start_task(task_id)?; + let download = runtime + .download_plan(task_id)? + .ok_or(RuntimeDownloadExecutionError::MissingDownloadAction)?; + let file_name = safe_download_file_name(&download.file_name); + let download_dir = data_dir.join("downloads").join(task_id); + fs::create_dir_all(&download_dir).map_err(RuntimeDownloadExecutionError::PrepareFile)?; + let final_path = download_dir.join(&file_name); + let partial_path = download_dir.join(format!(".{file_name}.part")); + let file = File::create(&partial_path).map_err(RuntimeDownloadExecutionError::PrepareFile)?; + + let mut sink = RuntimeFileDownloadSink::new(runtime, task_id, file); + let fetch_result = transport.fetch( + &RuntimeDownloadTransportRequest { url: &download.url }, + &mut sink, + ); + let write_result = sink.finish(); + + if let Err(error) = fetch_result { + let _ = fs::remove_file(&partial_path); + return Err(RuntimeDownloadExecutionError::Transport(error)); + } + let written = write_result.map_err(RuntimeDownloadExecutionError::FinishFile)?; + fs::rename(&partial_path, &final_path).map_err(RuntimeDownloadExecutionError::FinishFile)?; + + let downloaded_file = DownloadedFile { + file_name, + local_path: final_path.to_string_lossy().into_owned(), + size_bytes: written.size_bytes, + sha256: written.sha256, + }; + Ok(runtime.complete_downloaded_file(task_id, downloaded_file)?) +} + +struct RuntimeFileDownloadSink<'a> { + runtime: &'a mut GetterRuntime, + task_id: String, + file: File, + hasher: Sha256, + size_bytes: u64, + total_bytes: Option, +} + +impl<'a> RuntimeFileDownloadSink<'a> { + fn new(runtime: &'a mut GetterRuntime, task_id: &str, file: File) -> Self { + Self { + runtime, + task_id: task_id.to_owned(), + file, + hasher: Sha256::new(), + size_bytes: 0, + total_bytes: None, + } + } + + fn finish(mut self) -> std::io::Result { + self.file.flush()?; + self.file.sync_all()?; + Ok(RuntimeFileDownloadResult { + size_bytes: self.size_bytes, + sha256: format!("{:x}", self.hasher.finalize()), + }) + } + + fn total_bits(&self) -> Option { + self.total_bytes.map(|bytes| bytes.saturating_mul(8)) + } + + fn current_bits(&self) -> u64 { + self.size_bytes.saturating_mul(8) + } + + fn publish_progress(&mut self) -> Result<(), String> { + self.runtime + .set_download_progress(&self.task_id, self.current_bits(), self.total_bits()) + .map(|_| ()) + .map_err(|source| source.to_string()) + } +} + +impl RuntimeDownloadSink for RuntimeFileDownloadSink<'_> { + fn set_total_bytes(&mut self, total_bytes: Option) -> Result<(), String> { + self.total_bytes = total_bytes; + self.publish_progress() + } + + fn write_chunk(&mut self, chunk: &[u8]) -> Result<(), String> { + self.file + .write_all(chunk) + .map_err(|source| source.to_string())?; + self.hasher.update(chunk); + self.size_bytes = self + .size_bytes + .checked_add(chunk.len() as u64) + .ok_or_else(|| "download size overflowed u64".to_owned())?; + self.publish_progress() + } +} + +struct RuntimeFileDownloadResult { + size_bytes: u64, + sha256: String, +} + +fn safe_download_file_name(raw: &str) -> String { + let base = raw + .trim() + .rsplit(['/', '\\']) + .next() + .unwrap_or_default() + .trim(); + let sanitized: String = base + .chars() + .map(|ch| { + if ch.is_control() || matches!(ch, '/' | '\\') { + '_' + } else { + ch + } + }) + .collect(); + if sanitized.is_empty() || sanitized == "." || sanitized == ".." { + "artifact.bin".to_owned() + } else { + sanitized + } +} + +fn download_transport_error(source: ureq::Error) -> RuntimeDownloadTransportError { + match source { + ureq::Error::StatusCode(status) => RuntimeDownloadTransportError::HttpStatus { + status, + message: download_http_status_message(status).to_owned(), + }, + other => RuntimeDownloadTransportError::Transport(other.to_string()), + } +} + +fn download_http_status_message(status: u16) -> &'static str { + match status { + 401 => "download requires authentication", + 403 => "download is forbidden", + 404 => "download artifact was not found", + 429 => "download was rate limited", + _ => "download request failed", + } +} + +#[cfg(test)] +mod tests { + use super::*; + use getter_core::runtime::{PackageVersionLuaObject, RuntimeNotification, SealedActionPlan}; + use getter_core::{PackageId, PackageKind, UpdateAction}; + use std::cell::RefCell; + use std::io::{Read, Write}; + use std::net::TcpListener; + use std::sync::{Arc, Mutex}; + use std::thread; + + struct ChunkedDownloadTransport { + chunks: Vec<&'static [u8]>, + total_bytes: Option, + requests: RefCell>, + } + + impl RuntimeDownloadTransport for ChunkedDownloadTransport { + fn fetch( + &self, + request: &RuntimeDownloadTransportRequest<'_>, + sink: &mut dyn RuntimeDownloadSink, + ) -> Result<(), RuntimeDownloadTransportError> { + self.requests.borrow_mut().push(request.url.to_owned()); + sink.set_total_bytes(self.total_bytes) + .map_err(RuntimeDownloadTransportError::Sink)?; + for chunk in &self.chunks { + sink.write_chunk(chunk) + .map_err(RuntimeDownloadTransportError::Sink)?; + } + Ok(()) + } + } + + #[test] + fn submit_action_downloads_bytes_to_getter_owned_file_and_notifies_progress() { + let temp = tempfile::tempdir().unwrap(); + let notifications = Arc::new(Mutex::new(Vec::new())); + let sink_notifications = notifications.clone(); + let mut runtime = GetterRuntime::new(); + runtime.set_notification_sink(move |notification| { + sink_notifications.lock().unwrap().push(notification); + }); + let action = runtime.issue_action(download_plan("generic/example", "../app.bin")); + let transport = ChunkedDownloadTransport { + chunks: vec![b"hello ", b"getter"], + total_bytes: Some(12), + requests: RefCell::new(Vec::new()), + }; + + let task = + submit_action_and_download(&mut runtime, temp.path(), &action.action_id, &transport) + .unwrap(); + + assert_eq!( + transport.requests.borrow().as_slice(), + &["https://example.invalid/app.bin"] + ); + assert_eq!( + task.status, + getter_core::runtime::RuntimeTaskStatus::Completed + ); + assert_eq!(task.downloaded_file.as_ref().unwrap().file_name, "app.bin"); + assert_eq!(task.downloaded_file.as_ref().unwrap().size_bytes, 12); + assert_eq!( + fs::read(&task.downloaded_file.as_ref().unwrap().local_path).unwrap(), + b"hello getter" + ); + assert_eq!( + task.downloaded_file.as_ref().unwrap().sha256, + "e20360c4483ac234e3233f2f82671d46f67dd09f8d1da753c8c81236aed04690" + ); + + let notifications = notifications.lock().unwrap(); + assert!(notifications.iter().any(|notification| matches!( + notification, + RuntimeNotification::TaskChanged { task } + if task.phase.category == getter_core::runtime::TaskPhaseCategory::Download + && task.progress.as_ref().is_some_and(|progress| progress.current == 48 && progress.total == Some(96)) + ))); + assert!(notifications.iter().any(|notification| matches!( + notification, + RuntimeNotification::TaskChanged { task } + if task.status == getter_core::runtime::RuntimeTaskStatus::Completed + && task.downloaded_file.is_some() + ))); + } + + #[test] + fn transport_failure_marks_existing_task_failed_without_restoring_action() { + let temp = tempfile::tempdir().unwrap(); + let mut runtime = GetterRuntime::new(); + let action = runtime.issue_action(download_plan("generic/example", "app.bin")); + let transport = FailingDownloadTransport; + + let task = + submit_action_and_download(&mut runtime, temp.path(), &action.action_id, &transport) + .unwrap(); + + assert_eq!(task.status, getter_core::runtime::RuntimeTaskStatus::Failed); + assert_eq!( + task.current_diagnostic.as_ref().unwrap().code, + "download.http_status" + ); + assert_eq!( + runtime.submit_action(&action.action_id).unwrap_err().code(), + "action.not_found" + ); + } + + #[test] + fn retry_download_reuses_same_task_and_writes_bytes() { + let temp = tempfile::tempdir().unwrap(); + let mut runtime = GetterRuntime::new(); + let action = runtime.issue_action(download_plan("generic/example", "app.bin")); + let failed = submit_action_and_download( + &mut runtime, + temp.path(), + &action.action_id, + &FailingDownloadTransport, + ) + .unwrap(); + let transport = ChunkedDownloadTransport { + chunks: vec![b"retry bytes"], + total_bytes: Some(11), + requests: RefCell::new(Vec::new()), + }; + + let retried = retry_download_task(&mut runtime, temp.path(), &failed.task_id, &transport) + .expect("retry download"); + + assert_eq!(retried.task_id, failed.task_id); + assert_eq!( + retried.status, + getter_core::runtime::RuntimeTaskStatus::Completed + ); + assert_eq!(retried.downloaded_file.as_ref().unwrap().size_bytes, 11); + assert_eq!( + fs::read(&retried.downloaded_file.as_ref().unwrap().local_path).unwrap(), + b"retry bytes" + ); + } + + struct FailingDownloadTransport; + + impl RuntimeDownloadTransport for FailingDownloadTransport { + fn fetch( + &self, + _: &RuntimeDownloadTransportRequest<'_>, + _: &mut dyn RuntimeDownloadSink, + ) -> Result<(), RuntimeDownloadTransportError> { + Err(RuntimeDownloadTransportError::HttpStatus { + status: 404, + message: "download artifact was not found".to_owned(), + }) + } + } + + #[test] + fn default_transport_fetches_bytes_from_mock_http_server() { + let temp = tempfile::tempdir().unwrap(); + let (url, handle) = serve_one_download_response(b"mock artifact bytes"); + let mut runtime = GetterRuntime::new(); + let action = + runtime.issue_action(download_plan_with_url("generic/example", &url, "app.bin")); + + let task = submit_action_and_download( + &mut runtime, + temp.path(), + &action.action_id, + &UreqRuntimeDownloadTransport::new(), + ) + .unwrap(); + let request = handle.join().unwrap(); + + assert!(request.starts_with("GET /artifact.bin HTTP/1.1")); + assert!(request.contains("accept: application/octet-stream, */*")); + assert!(request.contains("user-agent: UpgradeAll-getter")); + assert_eq!(task.downloaded_file.as_ref().unwrap().size_bytes, 19); + assert_eq!( + fs::read(&task.downloaded_file.as_ref().unwrap().local_path).unwrap(), + b"mock artifact bytes" + ); + } + + fn download_plan(package_id: &str, file_name: &str) -> SealedActionPlan { + download_plan_with_url(package_id, "https://example.invalid/app.bin", file_name) + } + + fn download_plan_with_url(package_id: &str, url: &str, file_name: &str) -> SealedActionPlan { + SealedActionPlan { + package_id: PackageId::new(PackageKind::Generic, package_id.split('/').nth(1).unwrap()) + .unwrap(), + lua_object: PackageVersionLuaObject { + object_id: format!("lua:{package_id}:1.0"), + dependency_digest: "sha256-test".to_owned(), + }, + actions: vec![UpdateAction::Download { + url: url.to_owned(), + file_name: file_name.to_owned(), + }], + } + } + + fn serve_one_download_response(body: &'static [u8]) -> (String, thread::JoinHandle) { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let handle = thread::spawn(move || { + let (mut stream, _) = listener.accept().unwrap(); + let mut request = Vec::new(); + let mut buffer = [0_u8; 1024]; + loop { + let read = stream.read(&mut buffer).unwrap(); + if read == 0 { + break; + } + request.extend_from_slice(&buffer[..read]); + if request.windows(4).any(|window| window == b"\r\n\r\n") { + break; + } + } + write!( + stream, + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nContent-Type: application/octet-stream\r\n\r\n", + body.len() + ) + .unwrap(); + stream.write_all(body).unwrap(); + String::from_utf8_lossy(&request).into_owned() + }); + (format!("http://{address}/artifact.bin"), handle) + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index 1851139..a6fb495 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -6,6 +6,7 @@ //! generated package files, manifests, and tracked state live here. pub mod autogen; +pub mod download; pub mod fdroid_autogen; pub mod fdroid_catalog; pub mod github_autogen; diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 8c5f971..fd08d33 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -6,6 +6,10 @@ //! persisted task state or duplicating task/control semantics in Flutter or //! Android glue. +use crate::download::{ + retry_download_task, submit_action_and_download, RuntimeDownloadOperationError, + RuntimeDownloadTransport, UreqRuntimeDownloadTransport, +}; #[cfg(feature = "lua")] use crate::github_releases::{GithubReleaseTransport, UreqGithubReleaseTransport}; #[cfg(feature = "lua")] @@ -64,6 +68,14 @@ pub enum RuntimeOperationError { Serialize(String), } +impl From for RuntimeOperationError { + fn from(error: RuntimeDownloadOperationError) -> Self { + match error { + RuntimeDownloadOperationError::Runtime(error) => Self::Runtime(error), + } + } +} + impl RuntimeOperationError { pub fn code(&self) -> &'static str { match self { @@ -223,6 +235,37 @@ pub fn submit_action_json( task_json(runtime.submit_action(&request.action_id)?) } +pub fn submit_action_and_download_json( + runtime: &mut GetterRuntime, + data_dir: &std::path::Path, + request_json: &str, +) -> Result { + submit_action_and_download_json_with_transport( + runtime, + data_dir, + request_json, + &UreqRuntimeDownloadTransport::new(), + ) +} + +pub fn submit_action_and_download_json_with_transport( + runtime: &mut GetterRuntime, + data_dir: &std::path::Path, + request_json: &str, + transport: &T, +) -> Result +where + T: RuntimeDownloadTransport + ?Sized, +{ + let request: SubmitActionRequest = parse_request(request_json)?; + task_json(submit_action_and_download( + runtime, + data_dir, + &request.action_id, + transport, + )?) +} + pub fn task_get_json( runtime: &GetterRuntime, request_json: &str, @@ -314,6 +357,37 @@ pub fn task_retry_json( task_json(runtime.retry_task(&request.task_id)?) } +pub fn task_retry_download_json( + runtime: &mut GetterRuntime, + data_dir: &std::path::Path, + request_json: &str, +) -> Result { + task_retry_download_json_with_transport( + runtime, + data_dir, + request_json, + &UreqRuntimeDownloadTransport::new(), + ) +} + +pub fn task_retry_download_json_with_transport( + runtime: &mut GetterRuntime, + data_dir: &std::path::Path, + request_json: &str, + transport: &T, +) -> Result +where + T: RuntimeDownloadTransport + ?Sized, +{ + let request: TaskIdRequest = parse_request(request_json)?; + task_json(retry_download_task( + runtime, + data_dir, + &request.task_id, + transport, + )?) +} + pub fn task_remove_json( runtime: &mut GetterRuntime, request_json: &str, @@ -375,6 +449,7 @@ struct RegisteredPackageUpdateActionRequest { } #[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] struct SubmitActionRequest { action_id: String, } @@ -541,9 +616,7 @@ mod tests { use getter_storage::{CacheDb, ProviderResponseUpsert}; #[cfg(feature = "lua")] use sha2::{Digest, Sha512}; - #[cfg(feature = "lua")] use std::cell::RefCell; - #[cfg(feature = "lua")] use std::fs; #[cfg(feature = "lua")] use std::rc::Rc; @@ -1081,6 +1154,57 @@ mod tests { assert_eq!(submitted["status"], "queued"); } + #[test] + fn json_submit_and_download_writes_file_through_injected_transport() { + let temp = tempfile::tempdir().unwrap(); + let mut runtime = GetterRuntime::new(); + let issued = issue_action(&mut runtime, plan_without_install("generic/example")); + let action_id = issued["action_id"].as_str().unwrap(); + let transport = RecordingDownloadTransport { + requests: RefCell::new(Vec::new()), + }; + + let completed = submit_action_and_download_json_with_transport( + &mut runtime, + temp.path(), + &json!({ "action_id": action_id }).to_string(), + &transport, + ) + .unwrap(); + + assert_eq!( + transport.requests.borrow().as_slice(), + &["https://example.invalid/archive.zip"] + ); + assert_eq!(completed["status"], "completed"); + assert_eq!(completed["downloaded_file"]["file_name"], "archive.zip"); + assert_eq!(completed["downloaded_file"]["size_bytes"], 4); + let local_path = completed["downloaded_file"]["local_path"].as_str().unwrap(); + assert_eq!(fs::read(local_path).unwrap(), b"data"); + assert!(runtime.submit_action(action_id).is_err()); + } + + #[test] + fn json_submit_rejects_product_supplied_download_fields() { + let mut runtime = GetterRuntime::new(); + let issued = issue_action(&mut runtime, plan_without_install("generic/example")); + let action_id = issued["action_id"].as_str().unwrap(); + + let error = submit_action_json( + &mut runtime, + &json!({ + "action_id": action_id, + "url": "https://example.invalid/other.bin" + }) + .to_string(), + ) + .unwrap_err(); + + assert_eq!(error.code(), "runtime.invalid_request"); + assert!(error.detail().unwrap().contains("unknown field `url`")); + assert_eq!(runtime.tasks().len(), 0); + } + #[test] fn offline_update_check_without_update_does_not_issue_action() { let mut runtime = GetterRuntime::new(); @@ -1178,6 +1302,25 @@ mod tests { ); } + struct RecordingDownloadTransport { + requests: RefCell>, + } + + impl crate::download::RuntimeDownloadTransport for RecordingDownloadTransport { + fn fetch( + &self, + request: &crate::download::RuntimeDownloadTransportRequest<'_>, + sink: &mut dyn crate::download::RuntimeDownloadSink, + ) -> Result<(), crate::download::RuntimeDownloadTransportError> { + self.requests.borrow_mut().push(request.url.to_owned()); + sink.set_total_bytes(Some(4)) + .map_err(crate::download::RuntimeDownloadTransportError::Sink)?; + sink.write_chunk(b"data") + .map_err(crate::download::RuntimeDownloadTransportError::Sink)?; + Ok(()) + } + } + fn submit_plan(runtime: &mut GetterRuntime, plan: SealedActionPlan) -> String { let action = runtime.issue_action(plan); runtime.submit_action(&action.action_id).unwrap().task_id From 3c19debc5209243e078ab66a15ed1c9e8e23b93e Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 14 Jul 2026 21:02:03 +0800 Subject: [PATCH 66/71] feat(startup): expose unified app read model --- Cargo.lock | 1 + crates/getter-cli/src/lib.rs | 77 ++- crates/getter-cli/tests/startup_cli.rs | 105 ++++ crates/getter-operations/Cargo.toml | 1 + crates/getter-operations/src/lib.rs | 1 + .../getter-operations/src/provider_cache.rs | 45 +- crates/getter-operations/src/startup.rs | 472 ++++++++++++++++++ crates/getter-operations/tests/startup.rs | 343 +++++++++++++ crates/getter-storage/src/lib.rs | 88 +++- 9 files changed, 1061 insertions(+), 72 deletions(-) create mode 100644 crates/getter-cli/tests/startup_cli.rs create mode 100644 crates/getter-operations/src/startup.rs create mode 100644 crates/getter-operations/tests/startup.rs diff --git a/Cargo.lock b/Cargo.lock index b1df2b7..b0add8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -650,6 +650,7 @@ dependencies = [ "getter-storage", "json_comments", "mlua", + "rusqlite", "serde", "serde_json", "sha2", diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 6a87d37..5de2e57 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -8,8 +8,8 @@ use getter_core::autogen::InstalledInventory; use getter_core::diagnostics::validate_repository_path; use getter_core::lua::evaluate_package_directory_script; use getter_core::repository::{ - default_repository_priority, GetterDataDirLayout, RepositoryMetadata, - RepositoryPackageDirectoryLayout, REPOSITORY_ROOT_METADATA_FILE, REPO_API_VERSION_V1, + default_repository_priority, RepositoryMetadata, RepositoryPackageDirectoryLayout, + REPO_API_VERSION_V1, }; use getter_core::runtime::{GetterRuntime, SealedActionPlan}; use getter_core::task::{ @@ -28,6 +28,7 @@ use getter_operations::github_latest_commit::{self, GithubLatestCommitOperationE use getter_operations::github_releases::{self, GithubReleaseOperationError}; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; use getter_operations::runtime as runtime_operations; +use getter_operations::startup as startup_operations; use getter_storage::legacy_room::{ map_legacy_app, LegacyAppKind, LegacyAppRecord, LegacyExtraAppRecord, LegacyPackageResolution, }; @@ -44,19 +45,6 @@ const MAIN_DB_FILE: &str = "main.db"; const CACHE_DB_FILE: &str = "cache.db"; const MIGRATION_REPORTS_DIR: &str = "migration-reports"; const LEGACY_ROOM_MIGRATION_ID: &str = "legacy-room-v17"; -const REPOSITORY_ROOT_METADATA_STARTER: &str = r#"{ - "version": 1, - // Autogen writes to "autogen" by default. Uncomment and change this - // if generated packages should target another existing repository alias. - // "generated_repository": "autogen", - "priority": { - "local": 100, - "official": 0, - "autogen": -1 - } -} -"#; - #[derive(Debug, Clone, PartialEq, Eq)] pub struct CliInvocation { pub data_dir: PathBuf, @@ -66,6 +54,9 @@ pub struct CliInvocation { #[derive(Debug, Clone, PartialEq, Eq)] pub enum CliCommand { Init, + Startup { + inventory: Option, + }, AppList, HubList, RepoList, @@ -445,6 +436,12 @@ where let command_args = &args[2..]; let command = match command_args { [command] if command == "init" => CliCommand::Init, + [command] if command == "startup" => CliCommand::Startup { inventory: None }, + [command, flag, inventory] if command == "startup" && flag == "--inventory" => { + CliCommand::Startup { + inventory: Some(PathBuf::from(inventory)), + } + } [domain, command] if domain == "app" && command == "list" => CliCommand::AppList, [domain, command] if domain == "hub" && command == "list" => CliCommand::HubList, [domain, command] if domain == "repo" && command == "list" => CliCommand::RepoList, @@ -707,16 +704,27 @@ where fn execute(invocation: CliInvocation) -> Result { match invocation.command { + CliCommand::Startup { inventory } => { + let inventory = match inventory { + Some(path) => read_installed_inventory(&path)?, + None => InstalledInventory::new(Vec::new()), + }; + serde_json::to_value( + startup_operations::startup(&invocation.data_dir, inventory) + .map_err(|error| CliError::Storage(error.to_string()))?, + ) + .map_err(|error| CliError::Storage(error.to_string())) + } CliCommand::Init => { - initialize_storage(&invocation.data_dir)?; - let layout = initialize_data_dir_layout(&invocation.data_dir)?; + let bootstrap = startup_operations::bootstrap_data_dir(&invocation.data_dir) + .map_err(|error| CliError::Storage(error.to_string()))?; Ok(json!({ - "data_dir": layout.root, - "main_db": layout.main_db, - "cache_db": layout.cache_db, - "repo": layout.repository_root, - "rc": layout.runtime_config_root, - "repo_metadata": layout.repository_root.join(REPOSITORY_ROOT_METADATA_FILE), + "data_dir": bootstrap.data_dir, + "main_db": bootstrap.main_db.path, + "cache_db": bootstrap.cache_db.path, + "repo": bootstrap.repo, + "rc": bootstrap.rc, + "repo_metadata": bootstrap.repo_metadata, })) } CliCommand::AppList => { @@ -1092,26 +1100,6 @@ fn initialize_storage(data_dir: &Path) -> Result<(), CliError> { Ok(()) } -fn initialize_data_dir_layout(data_dir: &Path) -> Result { - let layout = GetterDataDirLayout::new(data_dir); - fs::create_dir_all(&layout.repository_root).map_err(|source| { - CliError::Storage(format!("failed to create repository root: {source}")) - })?; - fs::create_dir_all(&layout.runtime_config_root).map_err(|source| { - CliError::Storage(format!("failed to create runtime config root: {source}")) - })?; - let metadata_path = layout.repository_root.join(REPOSITORY_ROOT_METADATA_FILE); - if !metadata_path.exists() { - fs::write(&metadata_path, REPOSITORY_ROOT_METADATA_STARTER).map_err(|source| { - CliError::Storage(format!( - "failed to write repository root metadata '{}': {source}", - metadata_path.display() - )) - })?; - } - Ok(layout) -} - fn open_initialized_storage(data_dir: &Path) -> Result<(), CliError> { initialize_storage(data_dir) } @@ -2246,7 +2234,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo [--releases ] [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir ]|app list|repo list|repo add [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo [--releases ] [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -2291,6 +2279,7 @@ impl CliCommand { fn name(&self) -> &'static str { match self { Self::Init => "init", + Self::Startup { .. } => "startup", Self::AppList => "app list", Self::HubList => "hub list", Self::RepoList => "repo list", diff --git a/crates/getter-cli/tests/startup_cli.rs b/crates/getter-cli/tests/startup_cli.rs new file mode 100644 index 0000000..15a587d --- /dev/null +++ b/crates/getter-cli/tests/startup_cli.rs @@ -0,0 +1,105 @@ +use getter_cli::{run, ExitCode}; +use getter_core::repository::RepositoryMetadata; +use getter_core::{RepositoryId, RepositoryPriority}; +use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; +use serde_json::{json, Value}; + +#[test] +fn startup_command_proves_shared_getter_snapshot_with_empty_inventory_default() { + let temp = tempfile::tempdir().unwrap(); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "startup", + ]); + assert_eq!(output.exit_code, ExitCode::Success, "{}", output.stdout); + let envelope: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(envelope["command"], "startup"); + assert_eq!(envelope["data"]["format"], "getter-startup-snapshot"); + assert_eq!(envelope["data"]["version"], 1); + assert_eq!(envelope["data"]["bootstrap"]["lifecycle"], "initialized"); + assert_eq!(envelope["data"]["apps"], serde_json::json!([])); +} + +#[test] +fn startup_command_uses_inventory_file_to_join_installed_version() { + let temp = tempfile::tempdir().unwrap(); + let repo = temp.path().join("repo/official"); + let package = repo.join("android/com.example"); + std::fs::create_dir_all(&package).unwrap(); + std::fs::write(package.join("Manifest"), "").unwrap(); + std::fs::write( + package.join("metadata.jsonc"), + r#"{ + "type":"android:app", + "display_name":"Example", + "android":{"package_name":"com.example"} + }"#, + ) + .unwrap(); + std::fs::write( + package.join("1.lua"), + r#"#!/bin/upa-lua v1 +return package_version { updates = { { version="2.4.0", artifacts={{name="app.apk",url="https://example.invalid/app.apk"}} } } }"#, + ) + .unwrap(); + let package_id = "android/com.example".parse().unwrap(); + let repository_id = RepositoryId::new("official").unwrap(); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: repository_id.clone(), + name: "Official".to_owned(), + priority: RepositoryPriority::DEFAULT, + api_version: "v1".to_owned(), + }, + Some(&repo), + None, + ) + .unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id, + enabled: true, + favorite: false, + pin_version: None, + repository_id: Some(repository_id), + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + let inventory_path = temp.path().join("inventory.json"); + std::fs::write( + &inventory_path, + serde_json::to_vec(&json!({ + "format": "upgradeall-installed-inventory", + "version": 1, + "items": [{ + "kind": "android_package", + "package_name": "com.example", + "label": "Example", + "version_name": "2.4.0", + "version_code": 24 + }] + })) + .unwrap(), + ) + .unwrap(); + + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "startup", + "--inventory", + inventory_path.to_str().unwrap(), + ]); + + assert_eq!(output.exit_code, ExitCode::Success, "{}", output.stdout); + let envelope: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!( + envelope["data"]["apps"][0]["installed_version"], "2.4.0", + "{}", + output.stdout + ); + assert_eq!(envelope["data"]["apps"][0]["update_status"], "up_to_date"); +} diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index 0d51558..6ce6120 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -22,4 +22,5 @@ thiserror = "1" ureq = { version = "3.3.0", default-features = false, features = ["gzip", "rustls"] } [dev-dependencies] +rusqlite = { version = "0.32", features = ["bundled"] } tempfile = "3" diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index a6fb495..c90e8ed 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -25,3 +25,4 @@ pub mod lua_runtime_hooks; pub mod provider_cache; pub mod read_model; pub mod runtime; +pub mod startup; diff --git a/crates/getter-operations/src/provider_cache.rs b/crates/getter-operations/src/provider_cache.rs index 89179e3..d74e511 100644 --- a/crates/getter-operations/src/provider_cache.rs +++ b/crates/getter-operations/src/provider_cache.rs @@ -10,12 +10,14 @@ use serde_json::{json, Value}; use sha2::{Digest, Sha512}; pub const CACHE_REFRESH_FAILED: &str = "cache.refresh_failed"; +pub const CACHE_ONLY_MISS: &str = "cache.cache_only_miss"; pub const USED_STALE_CACHE: &str = "used_stale_cache"; pub const PROVIDER_RESPONSE_PROVENANCE_SCHEMA_V1: &str = "provider-response-provenance-v1"; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ProviderCacheMode { UseCached, + CacheOnly, ForceRefresh, } @@ -62,6 +64,8 @@ pub enum ProviderCacheOperationError { Storage(#[from] StorageError), #[error("provider refresh failed: {0}")] RefreshFailed(String), + #[error("provider cache-only miss for '{cache_key}' ({provider})")] + CacheOnlyMiss { cache_key: String, provider: String }, } pub fn read_or_refresh_provider_response( @@ -90,7 +94,10 @@ where F: FnOnce() -> Result, { let cached = db.provider_response(request.cache_key)?; - if request.mode == ProviderCacheMode::UseCached { + if matches!( + request.mode, + ProviderCacheMode::UseCached | ProviderCacheMode::CacheOnly + ) { if let Some(response) = cached { return Ok(ProviderCacheResult { response, @@ -98,6 +105,12 @@ where diagnostics: Vec::new(), }); } + if request.mode == ProviderCacheMode::CacheOnly { + return Err(ProviderCacheOperationError::CacheOnlyMiss { + cache_key: request.cache_key.to_owned(), + provider: request.provider.to_owned(), + }); + } } match refresh() { @@ -159,7 +172,7 @@ mod tests { use std::cell::Cell; #[test] - fn uses_cached_provider_response_without_refreshing() { + fn cache_only_hit_returns_cached_response_without_refreshing() { let db = CacheDb::open_in_memory().unwrap(); db.upsert_provider_response(&ProviderResponseUpsert { cache_key: "fdroid:official:index".to_owned(), @@ -177,11 +190,11 @@ mod tests { ProviderCacheRequest { cache_key: "fdroid:official:index", provider: "fdroid", - mode: ProviderCacheMode::UseCached, + mode: ProviderCacheMode::CacheOnly, }, || { refreshed.set(true); - Ok(json!({ "revision": "fresh" })) + panic!("cache-only mode must not refresh a hit") }, ) .unwrap(); @@ -192,6 +205,30 @@ mod tests { assert!(result.diagnostics.is_empty()); } + #[test] + fn cache_only_miss_never_invokes_refresh() { + let db = CacheDb::open_in_memory().unwrap(); + let refresh_called = Cell::new(false); + let error = read_or_refresh_provider_response( + &db, + ProviderCacheRequest { + cache_key: "github:missing", + provider: "github-releases", + mode: ProviderCacheMode::CacheOnly, + }, + || { + refresh_called.set(true); + panic!("cache-only mode must not invoke refresh") + }, + ) + .unwrap_err(); + assert!(matches!( + error, + ProviderCacheOperationError::CacheOnlyMiss { .. } + )); + assert!(!refresh_called.get()); + } + #[test] fn cache_miss_refreshes_and_stores_provider_response() { let db = CacheDb::open_in_memory().unwrap(); diff --git a/crates/getter-operations/src/startup.rs b/crates/getter-operations/src/startup.rs new file mode 100644 index 0000000..090d7f6 --- /dev/null +++ b/crates/getter-operations/src/startup.rs @@ -0,0 +1,472 @@ +//! Idempotent getter startup and unified app/update read model. + +#[cfg(feature = "lua")] +use crate::lua_provider_host::{evaluate_provider_backed_package, ProviderBackedPackageEvalConfig}; +#[cfg(feature = "lua")] +use crate::provider_cache::{ProviderCacheMode, CACHE_ONLY_MISS}; +use getter_core::autogen::{ + validate_installed_inventory, InstalledInventory, InstalledInventoryItem, +}; +#[cfg(feature = "lua")] +use getter_core::repository::RepositoryPackageDirectoryLayout; +use getter_core::repository::{GetterDataDirLayout, REPOSITORY_ROOT_METADATA_FILE}; +use getter_core::update::{ + check_updates_offline, compare_versions, UpdateCheckStatus, UpdateSelectionPolicy, +}; +#[cfg(feature = "lua")] +use getter_core::InstalledTarget; +#[cfg(feature = "lua")] +use getter_storage::StoredPackageResolution; +use getter_storage::{ + CacheDb, MainDb, StorageError, StoredRepository, StoredTrackedPackage, + CACHE_STORAGE_CONTRACT_VERSION, MAIN_STORAGE_CONTRACT_VERSION, +}; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::{Path, PathBuf}; + +const REPOSITORY_ROOT_METADATA_STARTER: &str = r#"{ + "version": 1, + // Autogen writes to "autogen" by default. Uncomment and change this + // if generated packages should target another existing repository alias. + // "generated_repository": "autogen", + "priority": { + "local": 100, + "official": 0, + "autogen": -1 + } +} +"#; +pub const STARTUP_SNAPSHOT_FORMAT: &str = "getter-startup-snapshot"; +pub const STARTUP_SNAPSHOT_VERSION: u32 = 1; + +#[derive(Debug, thiserror::Error)] +pub enum StartupError { + #[error("startup I/O failed: {0}")] + Io(#[from] std::io::Error), + #[error("startup storage failed: {0}")] + Storage(#[from] StorageError), + #[error("invalid installed inventory: {0}")] + Inventory(String), +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct StartupSnapshot { + pub format: String, + pub version: u32, + pub bootstrap: BootstrapStatus, + pub repositories: Vec, + pub apps: Vec, + pub update_count: usize, + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct BootstrapStatus { + pub lifecycle: BootstrapLifecycle, + pub data_dir: PathBuf, + pub main_db: DatabaseStatus, + pub cache_db: DatabaseStatus, + pub repo: PathBuf, + pub rc: PathBuf, + pub repo_metadata: PathBuf, + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum BootstrapLifecycle { + Initialized, + AlreadyInitialized, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DatabaseStatus { + pub path: PathBuf, + pub contract_version: u32, + pub created_this_call: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct StartupRepository { + pub id: String, + pub name: String, + pub priority: i32, + pub api_version: String, + pub path: Option, + pub revision: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct StartupAppSummary { + pub package_id: String, + pub repository_id: Option, + pub name: Option, + pub favorite: bool, + pub pin_version: Option, + pub installed_target: Option, + pub installed_version: Option, + pub effective_installed_version: Option, + pub latest_version: Option, + pub update_status: UpdateStatus, + pub warning: StartupWarning, + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct StartupInstalledTarget { + pub kind: String, + pub id: String, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct StartupWarning { + pub free_network: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct StartupDiagnostic { + pub code: String, + pub message: String, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum UpdateStatus { + Available, + UpToDate, + NoCandidates, + NotInstalled, +} + +pub fn bootstrap_data_dir(data_dir: &Path) -> Result { + let layout = GetterDataDirLayout::new(data_dir); + let main_created = !layout.main_db.exists(); + let cache_created = !layout.cache_db.exists(); + let repo_created = !layout.repository_root.is_dir(); + let rc_created = !layout.runtime_config_root.is_dir(); + fs::create_dir_all(&layout.repository_root)?; + fs::create_dir_all(&layout.runtime_config_root)?; + let repo_metadata = layout.repository_root.join(REPOSITORY_ROOT_METADATA_FILE); + let metadata_created = !repo_metadata.is_file(); + if metadata_created { + fs::write(&repo_metadata, REPOSITORY_ROOT_METADATA_STARTER)?; + } + let (_, main_migrated) = MainDb::open_with_migration_status(&layout.main_db)?; + let (_, cache_migrated) = CacheDb::open_with_migration_status(&layout.cache_db)?; + let mut diagnostics = Vec::new(); + if main_migrated { + diagnostics.push(diagnostic( + "storage.main_migrations_applied", + "Main storage schema migrations were applied", + )); + } + if cache_migrated { + diagnostics.push(diagnostic( + "storage.cache_migrations_applied", + "Cache storage schema migrations were applied", + )); + } + Ok(BootstrapStatus { + lifecycle: if !main_created + && !cache_created + && !repo_created + && !rc_created + && !metadata_created + && !main_migrated + && !cache_migrated + { + BootstrapLifecycle::AlreadyInitialized + } else { + BootstrapLifecycle::Initialized + }, + data_dir: layout.root, + main_db: DatabaseStatus { + path: layout.main_db, + contract_version: MAIN_STORAGE_CONTRACT_VERSION, + created_this_call: main_created, + }, + cache_db: DatabaseStatus { + path: layout.cache_db, + contract_version: CACHE_STORAGE_CONTRACT_VERSION, + created_this_call: cache_created, + }, + repo: layout.repository_root, + rc: layout.runtime_config_root, + repo_metadata, + diagnostics, + }) +} + +pub fn startup( + data_dir: &Path, + inventory: InstalledInventory, +) -> Result { + validate_installed_inventory(&inventory) + .map_err(|error| StartupError::Inventory(error.to_string()))?; + let bootstrap = bootstrap_data_dir(data_dir)?; + let db = MainDb::open(&bootstrap.main_db.path)?; + let stored_repositories = db.repositories()?; + let repositories = stored_repositories.iter().map(repository_dto).collect(); + let apps: Vec<_> = db + .tracked_packages()? + .into_iter() + .filter(|p| p.enabled) + .map(|package| app_summary(data_dir, &stored_repositories, &inventory, package)) + .collect(); + let update_count = apps + .iter() + .filter(|app| app.update_status == UpdateStatus::Available) + .count(); + Ok(StartupSnapshot { + format: STARTUP_SNAPSHOT_FORMAT.to_owned(), + version: STARTUP_SNAPSHOT_VERSION, + bootstrap, + repositories, + apps, + update_count, + diagnostics: vec![], + }) +} + +fn repository_dto(repo: &StoredRepository) -> StartupRepository { + StartupRepository { + id: repo.id.to_string(), + name: repo.name.clone(), + priority: repo.priority.value(), + api_version: repo.api_version.clone(), + path: repo.path.clone(), + revision: repo.revision.clone(), + } +} + +fn diagnostic(code: &str, message: impl Into) -> StartupDiagnostic { + StartupDiagnostic { + code: code.into(), + message: message.into(), + } +} + +#[cfg_attr(not(feature = "lua"), allow(unused_mut, unused_variables))] +fn app_summary( + data_dir: &Path, + repositories: &[StoredRepository], + inventory: &InstalledInventory, + tracked: StoredTrackedPackage, +) -> StartupAppSummary { + let mut diagnostics = Vec::new(); + #[cfg(feature = "lua")] + let repository = match tracked.repository_id.as_ref() { + Some(id) => repositories.iter().find(|repo| &repo.id == id), + None if tracked.package_resolution + == StoredPackageResolution::OfficialRepositoryPackage => + { + repositories.iter().find(|repository| { + let root = repository + .path + .as_ref() + .map(PathBuf::from) + .unwrap_or_else(|| data_dir.join("repo").join(repository.id.as_str())); + RepositoryPackageDirectoryLayout::load(&root) + .ok() + .is_some_and(|layout| layout.package(&tracked.package_id).is_some()) + }) + } + None => None, + }; + #[cfg(not(feature = "lua"))] + let repository = tracked + .repository_id + .as_ref() + .and_then(|id| repositories.iter().find(|repo| &repo.id == id)); + let mut name = None; + let mut installed_target = None; + let mut warning = StartupWarning::default(); + let mut candidates = Vec::new(); + + #[cfg(feature = "lua")] + if tracked.package_resolution == StoredPackageResolution::OfficialRepositoryPackage { + if let Some(repository) = repository { + let root = repository + .path + .as_ref() + .map(PathBuf::from) + .unwrap_or_else(|| data_dir.join("repo").join(repository.id.as_str())); + let evaluated = (|| { + let layout = + RepositoryPackageDirectoryLayout::load(&root).map_err(|e| e.to_string())?; + let package = layout.package(&tracked.package_id).ok_or_else(|| { + format!("package {} is absent from repository", tracked.package_id) + })?; + let metadata = layout + .package_metadata(package) + .map_err(|e| e.to_string())?; + let script = layout + .unambiguous_version_script(package) + .map_err(|e| e.to_string())?; + evaluate_provider_backed_package( + data_dir, + &repository.id, + package, + &metadata, + script, + ProviderBackedPackageEvalConfig { + mode: ProviderCacheMode::CacheOnly, + fdroid_endpoint_id: None, + fdroid_endpoint_url: None, + fdroid_index_xml: None, + github_endpoint_id: None, + github_api_base_url: None, + github_releases_json: None, + github_release_transport: None, + github_include_prereleases: false, + }, + ) + .map(|result| result.package) + .map_err(|e| e.to_string()) + })(); + match evaluated { + Ok(resolved) => { + name = Some(resolved.name); + warning.free_network = resolved.permissions.free_network; + installed_target = resolved.installed.first().map(target_dto); + candidates = resolved.updates; + } + Err(message) => diagnostics.push(diagnostic( + if message.contains(CACHE_ONLY_MISS) || message.contains("cache-only miss") { + "startup.provider_cache_miss" + } else { + "startup.package_eval_failed" + }, + message, + )), + } + } else { + diagnostics.push(diagnostic( + "startup.repository_missing", + "Tracked official package has no resolvable repository", + )); + } + } else { + diagnostics.push(diagnostic( + match tracked.package_resolution { + StoredPackageResolution::GenerateLocalPackage => { + "startup.package_generation_required" + } + StoredPackageResolution::MissingPackageDefinition => "startup.package_missing", + StoredPackageResolution::OfficialRepositoryPackage => unreachable!(), + }, + format!( + "Tracked package resolution is {}", + tracked.package_resolution.as_str() + ), + )); + } + + let installed_version = installed_target + .as_ref() + .and_then(|target| inventory_version(inventory, target)); + if installed_target.is_none() { + diagnostics.push(diagnostic( + "startup.installed_target_missing", + "Package evaluation returned no installed target", + )); + diagnostics.push(diagnostic( + "startup.installed_inventory_missing", + "No installed target was available to match against inventory", + )); + } else if installed_version.is_none() { + diagnostics.push(diagnostic( + "startup.installed_inventory_missing", + "No matching installed inventory item was supplied", + )); + } + + let policy = UpdateSelectionPolicy { + pin_version: tracked.pin_version.clone(), + }; + let result = check_updates_offline( + tracked.package_id.clone(), + installed_version.clone(), + candidates.clone(), + policy, + ) + .ok(); + let latest_version = candidates + .iter() + .max_by(|a, b| compare_versions(&a.version, &b.version)) + .map(|c| c.version.clone()); + let effective_installed_version = tracked + .pin_version + .clone() + .or_else(|| installed_version.clone()); + let update_status = if installed_version.is_none() || installed_target.is_none() { + UpdateStatus::NotInstalled + } else { + match result.as_ref().map(|r| r.status) { + Some(UpdateCheckStatus::UpdateAvailable) => UpdateStatus::Available, + Some(UpdateCheckStatus::UpToDate) => UpdateStatus::UpToDate, + Some(UpdateCheckStatus::NoCandidates) | None => UpdateStatus::NoCandidates, + } + }; + StartupAppSummary { + package_id: tracked.package_id.to_string(), + repository_id: tracked.repository_id.map(|id| id.to_string()), + name, + favorite: tracked.favorite, + pin_version: tracked.pin_version, + installed_target, + installed_version, + effective_installed_version, + latest_version, + update_status, + warning, + diagnostics, + } +} + +#[cfg(feature = "lua")] +fn target_dto(target: &InstalledTarget) -> StartupInstalledTarget { + match target { + InstalledTarget::AndroidPackage { package_name } => StartupInstalledTarget { + kind: "android_package".into(), + id: package_name.clone(), + }, + InstalledTarget::MagiskModule { module_id } => StartupInstalledTarget { + kind: "magisk_module".into(), + id: module_id.clone(), + }, + InstalledTarget::Generic { id } => StartupInstalledTarget { + kind: "generic".into(), + id: id.clone(), + }, + } +} + +fn inventory_version( + inventory: &InstalledInventory, + target: &StartupInstalledTarget, +) -> Option { + inventory + .items + .iter() + .find_map(|item| match (item, target.kind.as_str()) { + ( + InstalledInventoryItem::AndroidPackage { + package_name, + version_name, + .. + }, + "android_package", + ) if package_name == &target.id => version_name.clone(), + ( + InstalledInventoryItem::MagiskModule { + module_id, + version_name, + .. + }, + "magisk_module", + ) if module_id == &target.id => version_name.clone(), + _ => None, + }) +} diff --git a/crates/getter-operations/tests/startup.rs b/crates/getter-operations/tests/startup.rs new file mode 100644 index 0000000..4c19f93 --- /dev/null +++ b/crates/getter-operations/tests/startup.rs @@ -0,0 +1,343 @@ +#![cfg(feature = "lua")] + +use getter_core::autogen::{InstalledInventory, InstalledInventoryItem}; +use getter_core::repository::RepositoryMetadata; +use getter_core::{RepositoryId, RepositoryPriority}; +use getter_operations::startup::{startup, UpdateStatus}; +use getter_storage::{ + CacheDb, MainDb, ProviderResponseUpsert, StoredPackageResolution, TrackedPackageUpsert, +}; +use serde_json::json; +use std::fs; + +#[test] +fn startup_bootstraps_idempotently_and_joins_static_updates() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + + let first = startup(data_dir, InstalledInventory::new(vec![])).unwrap(); + let second = startup(data_dir, InstalledInventory::new(vec![])).unwrap(); + assert_eq!( + first.bootstrap.lifecycle, + getter_operations::startup::BootstrapLifecycle::Initialized + ); + assert_eq!( + second.bootstrap.lifecycle, + getter_operations::startup::BootstrapLifecycle::AlreadyInitialized + ); + assert!(first.bootstrap.main_db.created_this_call); + assert!(!second.bootstrap.main_db.created_this_call); + assert_eq!( + first.bootstrap.main_db.contract_version, + getter_storage::MAIN_STORAGE_CONTRACT_VERSION + ); + assert_eq!( + first.bootstrap.cache_db.contract_version, + getter_storage::CACHE_STORAGE_CONTRACT_VERSION + ); + let metadata = fs::read_to_string(data_dir.join("repo/metadata.jsonc")).unwrap(); + assert!(metadata.contains("// \"generated_repository\": \"autogen\"")); + assert!(metadata.contains("\"official\": 0")); + assert!(first + .bootstrap + .diagnostics + .iter() + .any(|diagnostic| { diagnostic.code == "storage.main_migrations_applied" })); + assert!(first + .bootstrap + .diagnostics + .iter() + .any(|diagnostic| { diagnostic.code == "storage.cache_migrations_applied" })); + assert!(second.bootstrap.diagnostics.is_empty()); + + let repo = data_dir.join("repo/official"); + let package = repo.join("android/org.fdroid.fdroid"); + fs::create_dir_all(&package).unwrap(); + fs::write(package.join("Manifest"), "").unwrap(); + fs::write( + package.join("metadata.jsonc"), + r#"{ + "type":"android:app", "display_name":"F-Droid", + "android":{"package_name":"org.fdroid.fdroid"}, + "lua":{"9999.lua":{"permission":["allow_free_network"]}} + }"#, + ) + .unwrap(); + fs::write( + package.join("9999.lua"), + r#"#!/bin/upa-lua v1 +return package_version { updates = { + { version="1.1.0", artifacts={{name="fdroid.apk",url="https://example.invalid/1.1.apk"}} }, + { version="1.2.0", artifacts={{name="fdroid.apk",url="https://example.invalid/1.2.apk"}} }, +} }"#, + ) + .unwrap(); + + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".into(), + priority: RepositoryPriority::DEFAULT, + api_version: "v1".into(), + }, + Some(&repo), + None, + ) + .unwrap(); + let package_id = "android/org.fdroid.fdroid".parse().unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id, + enabled: true, + favorite: true, + pin_version: Some("1.1.0".into()), + repository_id: Some(RepositoryId::new("official").unwrap()), + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + + let snapshot = startup( + data_dir, + InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.fdroid".into(), + label: Some("F-Droid installed".into()), + version_name: Some("1.0.0".into()), + version_code: Some(100), + }]), + ) + .unwrap(); + let app = &snapshot.apps[0]; + assert_eq!(app.name.as_deref(), Some("F-Droid")); + assert_eq!(app.installed_version.as_deref(), Some("1.0.0")); + assert_eq!(app.effective_installed_version.as_deref(), Some("1.1.0")); + assert_eq!(app.latest_version.as_deref(), Some("1.2.0")); + assert_eq!(app.update_status, UpdateStatus::Available); + assert!(app.warning.free_network); + assert_eq!(snapshot.update_count, 1); +} + +#[test] +fn startup_resolves_official_package_without_repository_to_highest_priority_covering_repo() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + startup(data_dir, InstalledInventory::new(vec![])).unwrap(); + + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + for (id, priority, contains_package, latest_version) in [ + ("highest-without-package", 100, false, "9.9.9"), + ("highest-covering", 50, true, "2.0.0"), + ("lower-covering", 10, true, "1.5.0"), + ] { + let repo = data_dir.join("repo").join(id); + fs::create_dir_all(&repo).unwrap(); + if contains_package { + let package = repo.join("android/org.example.app"); + fs::create_dir_all(&package).unwrap(); + fs::write(package.join("Manifest"), "").unwrap(); + fs::write( + package.join("metadata.jsonc"), + r#"{ + "type":"android:app", "display_name":"Example", + "android":{"package_name":"org.example.app"} + }"#, + ) + .unwrap(); + fs::write( + package.join("9999.lua"), + format!( + r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{{{ version="{latest_version}", artifacts={{{{name="app.apk",url="https://example.invalid/app.apk"}}}} }}}} }}"# + ), + ) + .unwrap(); + } + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new(id).unwrap(), + name: id.into(), + priority: RepositoryPriority::new(priority), + api_version: "v1".into(), + }, + Some(&repo), + None, + ) + .unwrap(); + } + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/org.example.app".parse().unwrap(), + enabled: true, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + + let snapshot = startup( + data_dir, + InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "org.example.app".into(), + label: Some("Installed Example".into()), + version_name: Some("1.0.0".into()), + version_code: Some(1), + }]), + ) + .unwrap(); + + assert_eq!(snapshot.apps[0].name.as_deref(), Some("Example")); + assert_eq!(snapshot.apps[0].latest_version.as_deref(), Some("2.0.0")); + assert_eq!(snapshot.apps[0].update_status, UpdateStatus::Available); + assert!(!snapshot + .diagnostics + .iter() + .any(|diagnostic| diagnostic.code == "startup.repository_missing")); +} + +#[test] +fn startup_reports_initialized_when_an_existing_valid_database_needs_a_migration() { + let temp = tempfile::tempdir().unwrap(); + startup(temp.path(), InstalledInventory::new(vec![])).unwrap(); + + let main_db = rusqlite::Connection::open(temp.path().join("main.db")).unwrap(); + main_db + .execute( + "DELETE FROM schema_migrations WHERE id = 'main-task-v1'", + [], + ) + .unwrap(); + + let migrated = startup(temp.path(), InstalledInventory::new(vec![])).unwrap(); + + assert_eq!( + migrated.bootstrap.lifecycle, + getter_operations::startup::BootstrapLifecycle::Initialized + ); + assert!(!migrated.bootstrap.main_db.created_this_call); + assert!(migrated + .bootstrap + .diagnostics + .iter() + .any(|diagnostic| { diagnostic.code == "storage.main_migrations_applied" })); +} + +#[test] +fn startup_keeps_missing_facts_nullable_with_stable_diagnostics() { + let temp = tempfile::tempdir().unwrap(); + startup(temp.path(), InstalledInventory::new(vec![])).unwrap(); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + let package_id = "android/org.missing.app".parse().unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id, + enabled: true, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: StoredPackageResolution::MissingPackageDefinition, + }) + .unwrap(); + + let snapshot = startup(temp.path(), InstalledInventory::new(vec![])).unwrap(); + let app = &snapshot.apps[0]; + assert!(app.repository_id.is_none()); + assert!(app.installed_version.is_none()); + assert!(app.latest_version.is_none()); + let codes: Vec<_> = app.diagnostics.iter().map(|d| d.code.as_str()).collect(); + assert!(codes.contains(&"startup.package_missing")); + assert!(codes.contains(&"startup.installed_inventory_missing")); + assert_eq!(app.update_status, UpdateStatus::NotInstalled); + assert_eq!(snapshot.update_count, 0); +} + +#[test] +fn bootstrap_reports_layout_created_even_when_databases_already_exist() { + let temp = tempfile::tempdir().unwrap(); + MainDb::open(temp.path().join("main.db")).unwrap(); + CacheDb::open(temp.path().join("cache.db")).unwrap(); + + let snapshot = startup(temp.path(), InstalledInventory::new(vec![])).unwrap(); + assert_eq!( + snapshot.bootstrap.lifecycle, + getter_operations::startup::BootstrapLifecycle::Initialized + ); + assert!(!snapshot.bootstrap.main_db.created_this_call); + assert!(!snapshot.bootstrap.cache_db.created_this_call); + assert!(snapshot.bootstrap.repo.join("metadata.jsonc").is_file()); + assert!(snapshot.bootstrap.rc.is_dir()); +} + +#[test] +fn startup_reads_warm_github_cache_without_transport_and_normalizes_updates() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + startup(data_dir, InstalledInventory::new(vec![])).unwrap(); + let repo = data_dir.join("repo/official"); + let package = repo.join("android/com.example.app"); + fs::create_dir_all(&package).unwrap(); + fs::write(package.join("Manifest"), "").unwrap(); + fs::write( + package.join("metadata.jsonc"), + r#"{ + "type":"android:app", "display_name":"Example", + "android":{"package_name":"com.example.app"}, + "lua":{"9999.lua":{"permission":["allow_free_network"]}} + }"#, + ) + .unwrap(); + fs::write( + package.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local result = getter.provider.github.release_candidates { + owner = "example", repo = "app", asset = { include = "app.apk" }, +} +return package_version { name="Example", source_priority={"github"}, updates=result.candidates } +"#, + ) + .unwrap(); + let metadata = RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".into(), + priority: RepositoryPriority::new(0), + api_version: "v1".into(), + }; + MainDb::open(data_dir.join("main.db")) + .unwrap() + .upsert_repository(&metadata, Some(&repo), None) + .unwrap(); + let package_id = "android/com.example.app".parse().unwrap(); + MainDb::open(data_dir.join("main.db")) + .unwrap() + .upsert_tracked_package(&TrackedPackageUpsert { + package_id, + enabled: true, + favorite: false, + pin_version: None, + repository_id: Some(metadata.id), + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + let config = getter_operations::github_releases::GithubReleaseConfig { + api_base_url: getter_operations::github_releases::DEFAULT_GITHUB_API_BASE_URL.into(), + owner: "example".into(), + repo: "app".into(), + }; + CacheDb::open(data_dir.join("cache.db")).unwrap().upsert_provider_response(&ProviderResponseUpsert { + cache_key: config.cache_key(), provider: "github".into(), + response_json: json!([{"tag_name":"2.0.0","name":"2.0.0","draft":false,"prerelease":false, + "published_at":"2026-01-01T00:00:00Z","assets":[{"name":"app.apk","browser_download_url":"https://example/app.apk","size":1}]}]), + source_response_sha512: vec![], provenance_schema_version: None, freshness_json: json!({}), + }).unwrap(); + + let snapshot = startup( + data_dir, + InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "com.example.app".into(), + label: None, + version_name: Some("1.0.0".into()), + version_code: None, + }]), + ) + .unwrap(); + assert_eq!(snapshot.apps[0].latest_version.as_deref(), Some("2.0.0")); + assert_eq!(snapshot.apps[0].update_status, UpdateStatus::Available); + assert_eq!(snapshot.update_count, 1); +} diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index e2b2c7e..ebe139e 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -14,6 +14,11 @@ use serde_json::Value; use std::path::Path; use std::str::FromStr; +pub const MAIN_STORAGE_CONTRACT_VERSION: u32 = 1; +pub const CACHE_STORAGE_CONTRACT_VERSION: u32 = 1; +pub const MAIN_APPLIED_MIGRATION_IDS: &[&str] = &["main-v1", "main-task-v1"]; +pub const CACHE_APPLIED_MIGRATION_IDS: &[&str] = &["cache-v1", "cache-provider-provenance-v1"]; + #[derive(Debug, thiserror::Error)] pub enum StorageError { #[error("sqlite error: {0}")] @@ -52,10 +57,16 @@ pub struct CacheDb { impl MainDb { pub fn open(path: impl AsRef) -> Result { + Self::open_with_migration_status(path).map(|(db, _)| db) + } + + pub fn open_with_migration_status( + path: impl AsRef, + ) -> Result<(Self, bool), StorageError> { let conn = Connection::open(path)?; let db = Self { conn }; - db.migrate()?; - Ok(db) + let migrations_applied = db.migrate()?; + Ok((db, migrations_applied)) } pub fn open_in_memory() -> Result { @@ -65,7 +76,7 @@ impl MainDb { Ok(db) } - fn migrate(&self) -> Result<(), StorageError> { + fn migrate(&self) -> Result { self.conn.execute_batch( r#" PRAGMA foreign_keys = ON; @@ -146,15 +157,14 @@ CREATE TABLE IF NOT EXISTS install_handoffs ( "package_resolution", "TEXT NOT NULL DEFAULT 'missing_package_definition'", )?; - self.conn.execute( - "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('main-v1')", - [], - )?; - self.conn.execute( - "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('main-task-v1')", - [], - )?; - Ok(()) + let mut migrations_applied = false; + for migration_id in MAIN_APPLIED_MIGRATION_IDS { + migrations_applied |= self.conn.execute( + "INSERT OR IGNORE INTO schema_migrations(id) VALUES (?1)", + [migration_id], + )? > 0; + } + Ok(migrations_applied) } fn ensure_column( @@ -838,10 +848,16 @@ WHERE task_id = ?4 impl CacheDb { pub fn open(path: impl AsRef) -> Result { + Self::open_with_migration_status(path).map(|(db, _)| db) + } + + pub fn open_with_migration_status( + path: impl AsRef, + ) -> Result<(Self, bool), StorageError> { let conn = Connection::open(path)?; let db = Self { conn }; - db.migrate()?; - Ok(db) + let migrations_applied = db.migrate()?; + Ok((db, migrations_applied)) } pub fn open_in_memory() -> Result { @@ -851,7 +867,7 @@ impl CacheDb { Ok(db) } - fn migrate(&self) -> Result<(), StorageError> { + fn migrate(&self) -> Result { self.conn.execute_batch( r#" PRAGMA foreign_keys = ON; @@ -893,15 +909,14 @@ CREATE TABLE IF NOT EXISTS provider_responses ( "freshness_json", "TEXT NOT NULL DEFAULT '{}'", )?; - self.conn.execute( - "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('cache-v1')", - [], - )?; - self.conn.execute( - "INSERT OR IGNORE INTO schema_migrations(id) VALUES ('cache-provider-provenance-v1')", - [], - )?; - Ok(()) + let mut migrations_applied = false; + for migration_id in CACHE_APPLIED_MIGRATION_IDS { + migrations_applied |= self.conn.execute( + "INSERT OR IGNORE INTO schema_migrations(id) VALUES (?1)", + [migration_id], + )? > 0; + } + Ok(migrations_applied) } fn ensure_column( @@ -1408,6 +1423,31 @@ mod tests { use getter_core::repository::REPO_API_VERSION_V1; use getter_core::task::TaskExecutor; + #[test] + fn applied_migration_rows_match_public_constants() { + let main = MainDb::open_in_memory().unwrap(); + let main_ids = main + .conn + .prepare("SELECT id FROM schema_migrations ORDER BY rowid") + .unwrap() + .query_map([], |row| row.get::<_, String>(0)) + .unwrap() + .collect::, _>>() + .unwrap(); + assert_eq!(main_ids, MAIN_APPLIED_MIGRATION_IDS); + + let cache = CacheDb::open_in_memory().unwrap(); + let cache_ids = cache + .conn + .prepare("SELECT id FROM schema_migrations ORDER BY rowid") + .unwrap() + .query_map([], |row| row.get::<_, String>(0)) + .unwrap() + .collect::, _>>() + .unwrap(); + assert_eq!(cache_ids, CACHE_APPLIED_MIGRATION_IDS); + } + #[test] fn main_db_stores_repository_registry_ordered_by_priority() { let db = MainDb::open_in_memory().unwrap(); From 5ba6ccfb147ac468182a5cd433d6e3ecedd189fd Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Wed, 15 Jul 2026 01:50:35 +0800 Subject: [PATCH 67/71] feat(cli): inspect and check tracked apps --- crates/getter-cli/src/lib.rs | 120 +++++++ crates/getter-cli/tests/app_cli.rs | 200 +++++++++++ crates/getter-cli/tests/bdd_cli.rs | 75 +++- .../features/cli/app_inspect_check.feature | 22 ++ crates/getter-operations/src/app.rs | 186 ++++++++++ crates/getter-operations/src/lib.rs | 1 + .../getter-operations/src/provider_cache.rs | 3 +- crates/getter-operations/src/runtime.rs | 105 ++++-- crates/getter-operations/src/startup.rs | 75 +++- crates/getter-operations/tests/app.rs | 338 ++++++++++++++++++ 10 files changed, 1086 insertions(+), 39 deletions(-) create mode 100644 crates/getter-cli/tests/app_cli.rs create mode 100644 crates/getter-cli/tests/features/cli/app_inspect_check.feature create mode 100644 crates/getter-operations/src/app.rs create mode 100644 crates/getter-operations/tests/app.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 5de2e57..391419d 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -58,6 +58,14 @@ pub enum CliCommand { inventory: Option, }, AppList, + AppShow { + package_id: PackageId, + inventory: Option, + }, + AppCheck { + package_id: PackageId, + inventory: Option, + }, HubList, RepoList, RepoAdd { @@ -204,6 +212,16 @@ pub enum CliError { Runtime(String), #[error("autogen error: {0}")] Autogen(String), + #[error("app is not tracked: {0}")] + AppUntracked(String), + #[error("app is disabled: {0}")] + AppDisabled(String), + #[error("{detail}")] + AppOperation { + code: &'static str, + message: &'static str, + detail: String, + }, #[error("provider error: {0}")] Provider(String), #[error("Legacy Room export bundle is invalid")] @@ -226,6 +244,9 @@ impl CliError { | Self::Update(_) | Self::Runtime(_) | Self::Autogen(_) => ExitCode::GenericFailure, + Self::AppUntracked(_) | Self::AppDisabled(_) | Self::AppOperation { .. } => { + ExitCode::GenericFailure + } Self::Provider(_) => ExitCode::Provider, Self::Download(_) => ExitCode::Download, Self::InvalidLegacyBundle { .. } @@ -245,6 +266,9 @@ impl CliError { Self::Download(_) => "download.task_error", Self::Runtime(_) => "runtime.error", Self::Autogen(_) => "autogen.error", + Self::AppUntracked(_) => "app.untracked", + Self::AppDisabled(_) => "app.disabled", + Self::AppOperation { code, .. } => code, Self::Provider(_) => "provider.error", Self::InvalidLegacyBundle { .. } => "migration.invalid_bundle", Self::UnsupportedLegacyBundle { .. } => "migration.unsupported_bundle", @@ -263,6 +287,9 @@ impl CliError { Self::Download(_) => "Getter download task operation failed", Self::Runtime(_) => "Getter runtime operation failed", Self::Autogen(_) => "Getter autogen operation failed", + Self::AppUntracked(_) => "Getter app is not tracked", + Self::AppDisabled(_) => "Getter app is disabled", + Self::AppOperation { message, .. } => message, Self::Provider(_) => "Getter provider operation failed", Self::InvalidLegacyBundle { .. } => "Legacy Room export bundle is invalid", Self::UnsupportedLegacyBundle { .. } => { @@ -283,7 +310,10 @@ impl CliError { | Self::Download(detail) | Self::Runtime(detail) | Self::Autogen(detail) + | Self::AppUntracked(detail) + | Self::AppDisabled(detail) | Self::Provider(detail) => Some(detail.as_str()), + Self::AppOperation { detail, .. } => Some(detail.as_str()), Self::InvalidLegacyBundle { .. } | Self::UnsupportedLegacyBundle { .. } | Self::InvalidLegacyDb { .. } @@ -305,6 +335,9 @@ impl CliError { | Self::Download(_) | Self::Runtime(_) | Self::Autogen(_) + | Self::AppUntracked(_) + | Self::AppDisabled(_) + | Self::AppOperation { .. } | Self::Provider(_) => None, } } @@ -443,6 +476,41 @@ where } } [domain, command] if domain == "app" && command == "list" => CliCommand::AppList, + [domain, command, package_id] + if domain == "app" && (command == "show" || command == "check") => + { + let package_id = parse_package_id(package_id)?; + if command == "show" { + CliCommand::AppShow { + package_id, + inventory: None, + } + } else { + CliCommand::AppCheck { + package_id, + inventory: None, + } + } + } + [domain, command, package_id, flag, inventory] + if domain == "app" + && (command == "show" || command == "check") + && flag == "--inventory" => + { + let package_id = parse_package_id(package_id)?; + let inventory = Some(PathBuf::from(inventory)); + if command == "show" { + CliCommand::AppShow { + package_id, + inventory, + } + } else { + CliCommand::AppCheck { + package_id, + inventory, + } + } + } [domain, command] if domain == "hub" && command == "list" => CliCommand::HubList, [domain, command] if domain == "repo" && command == "list" => CliCommand::RepoList, [domain, command, id, path] if domain == "repo" && command == "add" => { @@ -702,6 +770,22 @@ where Ok(CliInvocation { data_dir, command }) } +fn map_app_error(error: getter_operations::app::AppOperationError) -> CliError { + match error { + getter_operations::app::AppOperationError::Untracked(id) => { + CliError::AppUntracked(id.to_string()) + } + getter_operations::app::AppOperationError::Disabled(id) => { + CliError::AppDisabled(id.to_string()) + } + other => CliError::AppOperation { + code: other.code(), + message: other.message(), + detail: other.detail(), + }, + } +} + fn execute(invocation: CliInvocation) -> Result { match invocation.command { CliCommand::Startup { inventory } => { @@ -731,6 +815,40 @@ fn execute(invocation: CliInvocation) -> Result { let db = open_main_db(&invocation.data_dir)?; Ok(json!({ "apps": tracked_packages_json(db.tracked_packages()?) })) } + CliCommand::AppShow { + package_id, + inventory, + } => { + let inventory = match inventory { + Some(path) => read_installed_inventory(&path)?, + None => InstalledInventory::new(Vec::new()), + }; + serde_json::to_value( + getter_operations::app::inspect_app(&invocation.data_dir, inventory, &package_id) + .map_err(map_app_error)?, + ) + .map_err(|error| CliError::Storage(error.to_string())) + } + CliCommand::AppCheck { + package_id, + inventory, + } => { + let inventory = match inventory { + Some(path) => read_installed_inventory(&path)?, + None => InstalledInventory::new(Vec::new()), + }; + let mut runtime = GetterRuntime::new(); + serde_json::to_value( + getter_operations::app::check_app( + &mut runtime, + &invocation.data_dir, + inventory, + &package_id, + ) + .map_err(map_app_error)?, + ) + .map_err(|error| CliError::Storage(error.to_string())) + } CliCommand::HubList => { open_initialized_storage(&invocation.data_dir)?; Ok(json!({ "hubs": [] })) @@ -2281,6 +2399,8 @@ impl CliCommand { Self::Init => "init", Self::Startup { .. } => "startup", Self::AppList => "app list", + Self::AppShow { .. } => "app show", + Self::AppCheck { .. } => "app check", Self::HubList => "hub list", Self::RepoList => "repo list", Self::RepoAdd { .. } => "repo add", diff --git a/crates/getter-cli/tests/app_cli.rs b/crates/getter-cli/tests/app_cli.rs new file mode 100644 index 0000000..a3123de --- /dev/null +++ b/crates/getter-cli/tests/app_cli.rs @@ -0,0 +1,200 @@ +use getter_cli::{run, ExitCode}; +use getter_core::repository::RepositoryMetadata; +use getter_core::{RepositoryId, RepositoryPriority}; +use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; +use serde_json::{json, Value}; +use std::fs; + +fn fixture(latest: &str) -> tempfile::TempDir { + let temp = tempfile::tempdir().unwrap(); + run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "init", + ]); + let package = temp.path().join("repo/official/android/com.example"); + fs::create_dir_all(&package).unwrap(); + fs::write(package.join("Manifest"), "").unwrap(); + fs::write(package.join("metadata.jsonc"), r#"{"type":"android:app","display_name":"Example","android":{"package_name":"com.example"}}"#).unwrap(); + fs::write(package.join("1.lua"), format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{ {{ version="{latest}", artifacts={{{{name="app.apk",url="https://example.invalid/app.apk"}}}} }} }} }}"#)).unwrap(); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".into(), + priority: RepositoryPriority::DEFAULT, + api_version: "v1".into(), + }, + Some(&temp.path().join("repo/official")), + None, + ) + .unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/com.example".parse().unwrap(), + enabled: true, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + fs::write(temp.path().join("inventory.json"), json!({"format":"upgradeall-installed-inventory","version":1,"items":[{"kind":"android_package","package_name":"com.example","version_name":"1"}]}).to_string()).unwrap(); + temp +} + +#[test] +fn app_show_is_normal_cache_only_detail_command() { + let temp = fixture("2"); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "show", + "android/com.example", + "--inventory", + temp.path().join("inventory.json").to_str().unwrap(), + ]); + assert_eq!(output.exit_code, ExitCode::Success, "{}", output.stdout); + let envelope: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(envelope["command"], "app show"); + assert_eq!(envelope["data"]["package_id"], "android/com.example"); + assert_eq!(envelope["data"]["update_status"], "available"); + assert!(envelope["data"].get("action").is_none()); +} + +#[test] +fn app_check_returns_current_process_action_for_update() { + let temp = fixture("2"); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "check", + "android/com.example", + "--inventory", + temp.path().join("inventory.json").to_str().unwrap(), + ]); + assert_eq!(output.exit_code, ExitCode::Success, "{}", output.stdout); + let envelope: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(envelope["command"], "app check"); + assert!(envelope["data"]["action"]["action_id"].is_string()); +} + +#[test] +fn app_show_without_inventory_reports_not_installed_without_inventing_version() { + let temp = fixture("2"); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "show", + "android/com.example", + ]); + let body: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(output.exit_code, ExitCode::Success); + assert_eq!(body["data"]["update_status"], "not_installed"); + assert_eq!(body["data"]["installed_version"], Value::Null); +} + +#[test] +fn app_commands_preserve_stable_inventory_and_runtime_error_envelopes() { + let temp = fixture("2"); + fs::write( + temp.path().join("invalid-inventory.json"), + json!({"format":"wrong","version":1,"items":[]}).to_string(), + ) + .unwrap(); + let invalid_inventory = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "show", + "android/com.example", + "--inventory", + temp.path().join("invalid-inventory.json").to_str().unwrap(), + ]); + let body: Value = serde_json::from_str(&invalid_inventory.stdout).unwrap(); + assert_eq!(invalid_inventory.exit_code, ExitCode::GenericFailure); + assert_eq!(body["error"]["code"], "inventory.invalid"); + assert_eq!(body["error"]["message"], "Installed inventory is invalid"); + + fs::write( + temp.path().join("repo/official/android/com.example/1.lua"), + "this is not valid lua", + ) + .unwrap(); + let runtime_failure = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "check", + "android/com.example", + "--inventory", + temp.path().join("inventory.json").to_str().unwrap(), + ]); + let body: Value = serde_json::from_str(&runtime_failure.stdout).unwrap(); + assert_eq!(runtime_failure.exit_code, ExitCode::GenericFailure); + assert_eq!(body["error"]["code"], "package.eval_error"); + assert_ne!(body["error"]["code"], "storage.error"); +} + +#[test] +fn app_show_returns_untracked_and_disabled_error_envelopes() { + let temp = fixture("2"); + let untracked = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "show", + "android/not.tracked", + ]); + let body: Value = serde_json::from_str(&untracked.stdout).unwrap(); + assert_eq!(untracked.exit_code, ExitCode::GenericFailure); + assert_eq!(body["error"]["code"], "app.untracked"); + + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/com.example".parse().unwrap(), + enabled: false, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + let disabled = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "show", + "android/com.example", + ]); + let body: Value = serde_json::from_str(&disabled.stdout).unwrap(); + assert_eq!(disabled.exit_code, ExitCode::GenericFailure); + assert_eq!(body["error"]["code"], "app.disabled"); +} + +#[test] +fn app_commands_reject_product_transport_controls() { + let temp = fixture("2"); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "check", + "android/com.example", + "--endpoint", + "https://example.invalid", + ]); + assert_eq!(output.exit_code, ExitCode::Usage); +} diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 6a38e3c..10a762f 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -1,4 +1,5 @@ use cucumber::{given, then, when, World as _}; +use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; use rusqlite::Connection; use serde_json::Value; use std::fs; @@ -541,7 +542,7 @@ fn create_package_directory_repository( .expect("write package metadata"); fs::write( package_dir.join("1.20.0.lua"), - "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } } }", + "#!/bin/upa-lua v1\nreturn package_version { installed = { { kind = \"android_package\", package_name = \"org.fdroid.fdroid\" } }, updates = { { version = \"1.20.0\", artifacts = { { name = \"app.apk\", url = \"https://example.invalid/app.apk\" } } } } }", ) .expect("write version Lua"); if script_count > 1 { @@ -700,6 +701,78 @@ fn run_getter_repo_validate(world: &mut CliWorld) { world.json = None; } +#[given(expr = "repository {string} is registered with priority {int}")] +fn registered_repository(world: &mut CliWorld, repo_id: String, priority: i32) { + run_getter_repo_add_with_priority(world, &repo_id, priority); +} + +#[given(expr = "package {string} is actively tracked")] +fn actively_tracked_package(world: &mut CliWorld, package_id: String) { + let db = MainDb::open(world.data_dir.as_ref().expect("data dir").join("main.db")).unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: package_id.parse().unwrap(), + enabled: true, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); +} + +#[when(expr = "I run getter app show for {string} with that inventory")] +fn run_app_show(world: &mut CliWorld, package_id: String) { + run_app_command(world, "show", package_id, true); +} + +#[when(expr = "I run getter app check for {string} with that inventory")] +fn run_app_check(world: &mut CliWorld, package_id: String) { + run_app_command(world, "check", package_id, true); +} + +#[when(expr = "I run getter app check for {string} without inventory")] +fn run_app_check_without_inventory(world: &mut CliWorld, package_id: String) { + run_app_command(world, "check", package_id, false); +} + +fn run_app_command(world: &mut CliWorld, command: &str, package_id: String, inventory: bool) { + let mut args = vec!["app".to_owned(), command.to_owned(), package_id]; + if inventory { + args.extend([ + "--inventory".to_owned(), + world + .inventory + .as_ref() + .expect("inventory") + .display() + .to_string(), + ]); + } + world.output = Some(run_getter(world, args)); + world.json = None; +} + +#[then(expr = "the app result names {string} and reports installed version {string}")] +fn app_result(world: &mut CliWorld, name: String, version: String) { + let json = current_json(world); + assert_eq!(json["data"]["name"], name); + assert_eq!(json["data"]["installed_version"], version); +} + +#[then("the app check reports an available update and issues an action")] +fn app_check_available(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["data"]["app"]["update_status"], "available"); + assert!(json["data"]["action"]["action_id"].is_string()); +} + +#[then(expr = "the command fails with stable error {string}")] +fn stable_error(world: &mut CliWorld, code: String) { + let json = current_json(world); + assert_eq!(json["ok"], false); + assert_eq!(json["error"]["code"], code); +} + #[when("I run getter package eval for that fixture package")] fn run_getter_package_eval(world: &mut CliWorld) { let repo_id = world diff --git a/crates/getter-cli/tests/features/cli/app_inspect_check.feature b/crates/getter-cli/tests/features/cli/app_inspect_check.feature new file mode 100644 index 0000000..f40badd --- /dev/null +++ b/crates/getter-cli/tests/features/cli/app_inspect_check.feature @@ -0,0 +1,22 @@ +Feature: Inspect and actively check a tracked app + Getter users can inspect cached app state and explicitly refresh an installed app. + + Background: + Given an initialized getter data directory + And a package-directory repository "official" with package "android/app/fdroid" + And repository "official" is registered with priority 0 + And package "android/app/fdroid" is actively tracked + + Scenario: Show cached app state + Given an installed inventory with Android app "org.fdroid.fdroid" labeled "F-Droid" + When I run getter app show for "android/app/fdroid" with that inventory + Then the app result names "F-Droid" and reports installed version "1.0.0" + + Scenario: Check an installed app for updates + Given an installed inventory with Android app "org.fdroid.fdroid" labeled "F-Droid" + When I run getter app check for "android/app/fdroid" with that inventory + Then the app check reports an available update and issues an action + + Scenario: Check requires installed inventory + When I run getter app check for "android/app/fdroid" without inventory + Then the command fails with stable error "inventory.invalid" diff --git a/crates/getter-operations/src/app.rs b/crates/getter-operations/src/app.rs new file mode 100644 index 0000000..6558150 --- /dev/null +++ b/crates/getter-operations/src/app.rs @@ -0,0 +1,186 @@ +//! Shared app inspection and explicit update-check operations. + +#[cfg(feature = "lua")] +use crate::github_releases::GithubReleaseTransport; +use crate::startup::{self, StartupAppSummary}; +use getter_core::autogen::{validate_installed_inventory, InstalledInventory}; +#[cfg(feature = "lua")] +use getter_core::runtime::GetterRuntime; +use getter_core::runtime::IssuedAction; +use getter_core::PackageId; +use getter_storage::{MainDb, StorageError, StoredTrackedPackage}; +use serde::{Deserialize, Serialize}; +use std::path::Path; +#[cfg(feature = "lua")] +use std::rc::Rc; + +#[derive(Debug, thiserror::Error)] +pub enum AppOperationError { + #[error("app is not tracked: {0}")] + Untracked(PackageId), + #[error("app is disabled: {0}")] + Disabled(PackageId), + #[error("invalid installed inventory: {0}")] + Inventory(String), + #[error("app storage failed: {0}")] + Storage(#[from] StorageError), + #[error("app startup model failed: {0}")] + Startup(#[from] startup::StartupError), + #[cfg(feature = "lua")] + #[error("app update check failed: {0}")] + Runtime(#[from] crate::runtime::RuntimeOperationError), +} + +impl AppOperationError { + pub fn code(&self) -> &'static str { + match self { + Self::Untracked(_) => "app.untracked", + Self::Disabled(_) => "app.disabled", + Self::Inventory(_) => "inventory.invalid", + Self::Storage(_) => "storage.error", + Self::Startup(error) => match error { + startup::StartupError::Io(_) => "startup.io_error", + startup::StartupError::Storage(_) => "storage.error", + startup::StartupError::Inventory(_) => "inventory.invalid", + }, + #[cfg(feature = "lua")] + Self::Runtime(error) => error.code(), + } + } + + pub fn message(&self) -> &'static str { + match self { + Self::Untracked(_) => "Getter app is not tracked", + Self::Disabled(_) => "Getter app is disabled", + Self::Inventory(_) => "Installed inventory is invalid", + Self::Storage(_) => "Getter storage operation failed", + Self::Startup(_) => "Getter startup operation failed", + #[cfg(feature = "lua")] + Self::Runtime(error) => error.message(), + } + } + + pub fn detail(&self) -> String { + match self { + Self::Untracked(id) | Self::Disabled(id) => id.to_string(), + Self::Inventory(detail) => detail.clone(), + Self::Storage(error) => error.to_string(), + Self::Startup(error) => error.to_string(), + #[cfg(feature = "lua")] + Self::Runtime(error) => error.to_string(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct AppCheckResult { + pub app: StartupAppSummary, + /// Getter-issued action valid only in the runtime that performed this check. + pub action: Option, +} + +fn tracked_package( + db: &MainDb, + package_id: &PackageId, +) -> Result { + let tracked = db + .tracked_packages()? + .into_iter() + .find(|package| &package.package_id == package_id) + .ok_or_else(|| AppOperationError::Untracked(package_id.clone()))?; + if !tracked.enabled { + return Err(AppOperationError::Disabled(package_id.clone())); + } + Ok(tracked) +} + +pub fn inspect_app( + data_dir: &Path, + inventory: InstalledInventory, + package_id: &PackageId, +) -> Result { + validate_installed_inventory(&inventory) + .map_err(|error| AppOperationError::Inventory(error.to_string()))?; + startup::bootstrap_data_dir(data_dir)?; + let db = MainDb::open(data_dir.join("main.db"))?; + let tracked = tracked_package(&db, package_id)?; + let repositories = db.repositories()?; + Ok(startup::app_summary( + data_dir, + &repositories, + &inventory, + tracked, + )) +} + +#[cfg(feature = "lua")] +pub fn check_app( + runtime: &mut GetterRuntime, + data_dir: &Path, + inventory: InstalledInventory, + package_id: &PackageId, +) -> Result { + check_app_with_github_transport( + runtime, + data_dir, + inventory, + package_id, + Some(Rc::new( + crate::github_releases::UreqGithubReleaseTransport::new(), + )), + ) +} + +#[cfg(feature = "lua")] +pub fn check_app_with_github_transport( + runtime: &mut GetterRuntime, + data_dir: &Path, + inventory: InstalledInventory, + package_id: &PackageId, + github_release_transport: Option>, +) -> Result { + validate_installed_inventory(&inventory) + .map_err(|error| AppOperationError::Inventory(error.to_string()))?; + let db = MainDb::open(data_dir.join("main.db"))?; + let tracked = tracked_package(&db, package_id)?; + let repository_id = tracked.repository_id.clone(); + let mut request = crate::runtime::RegisteredPackageUpdateActionRequest { + package_id: package_id.clone(), + repository_id, + installed_version: None, + pin_version: tracked.pin_version.clone(), + }; + let evaluated = crate::runtime::evaluate_registered_package( + data_dir, + &db, + &request, + crate::provider_cache::ProviderCacheMode::ForceRefresh, + github_release_transport, + )?; + let mut app = startup::app_summary_from_resolved( + tracked, + Some(evaluated.package.repository.to_string()), + &inventory, + evaluated.package.clone(), + ); + app.diagnostics + .extend( + evaluated + .diagnostics + .iter() + .map(|diagnostic| startup::StartupDiagnostic { + code: diagnostic.code.clone(), + message: diagnostic.message.clone(), + }), + ); + let installed_version = app.installed_version.clone().ok_or_else(|| { + AppOperationError::Inventory(format!("package {package_id} is not installed")) + })?; + request.installed_version = Some(installed_version); + let checked = + crate::runtime::issue_action_from_registered_evaluation(runtime, request, evaluated)?; + Ok(AppCheckResult { + app, + action: checked.action, + }) +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index c90e8ed..d9502d9 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -5,6 +5,7 @@ //! provide raw facts to callers, but the rules for repository coverage, //! generated package files, manifests, and tracked state live here. +pub mod app; pub mod autogen; pub mod download; pub mod fdroid_autogen; diff --git a/crates/getter-operations/src/provider_cache.rs b/crates/getter-operations/src/provider_cache.rs index d74e511..be64a28 100644 --- a/crates/getter-operations/src/provider_cache.rs +++ b/crates/getter-operations/src/provider_cache.rs @@ -6,6 +6,7 @@ //! diagnostics. use getter_storage::{CacheDb, ProviderResponseUpsert, StorageError, StoredProviderResponse}; +use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use sha2::{Digest, Sha512}; @@ -42,7 +43,7 @@ pub struct ProviderResponseRefresh { pub freshness_json: Value, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ProviderCacheDiagnostic { pub code: String, pub message: String, diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index fd08d33..9c2d6b4 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -18,7 +18,7 @@ use crate::lua_provider_host::{ ProviderBackedPackageEvalConfig, }; #[cfg(feature = "lua")] -use crate::provider_cache::ProviderCacheMode; +use crate::provider_cache::{ProviderCacheDiagnostic, ProviderCacheMode}; #[cfg(feature = "lua")] use getter_core::repository::{ package_directory_cache_key, RepositoryLoadError, RepositoryPackageDirectoryLayout, @@ -38,6 +38,8 @@ use getter_providers::StaticPackageUpdatesProvider; #[cfg(feature = "lua")] use getter_storage::{MainDb, StorageError}; use serde::Deserialize; +#[cfg(feature = "lua")] +use serde::Serialize; use serde_json::{json, Value}; #[cfg(feature = "lua")] use std::path::PathBuf; @@ -188,7 +190,7 @@ pub fn issue_action_from_registered_package_json( } #[cfg(feature = "lua")] -fn issue_action_from_registered_package_json_with_github_transport( +pub fn issue_action_from_registered_package_json_with_github_transport( runtime: &mut GetterRuntime, data_dir: &std::path::Path, db: &MainDb, @@ -196,7 +198,51 @@ fn issue_action_from_registered_package_json_with_github_transport( github_release_transport: Option>, ) -> Result { let request: RegisteredPackageUpdateActionRequest = parse_request(request_json)?; - let evaluated = evaluate_registered_package(data_dir, db, &request, github_release_transport)?; + let result = issue_action_from_registered_package_with_github_transport( + runtime, + data_dir, + db, + request, + github_release_transport, + )?; + serde_json::to_value(result) + .map_err(|source| RuntimeOperationError::Serialize(source.to_string())) +} + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Serialize)] +pub(crate) struct RegisteredPackageActionResult { + pub package: getter_core::ResolvedPackage, + pub update: getter_core::update::OfflineUpdateCheckResult, + pub action: Option, + pub provider_calls: Vec, + pub runtime_hooks: Vec, +} + +#[cfg(feature = "lua")] +pub(crate) fn issue_action_from_registered_package_with_github_transport( + runtime: &mut GetterRuntime, + data_dir: &std::path::Path, + db: &MainDb, + request: RegisteredPackageUpdateActionRequest, + github_release_transport: Option>, +) -> Result { + let evaluated = evaluate_registered_package( + data_dir, + db, + &request, + ProviderCacheMode::UseCached, + github_release_transport, + )?; + issue_action_from_registered_evaluation(runtime, request, evaluated) +} + +#[cfg(feature = "lua")] +pub(crate) fn issue_action_from_registered_evaluation( + runtime: &mut GetterRuntime, + request: RegisteredPackageUpdateActionRequest, + evaluated: RegisteredPackageEvaluation, +) -> Result { let candidates = StaticPackageUpdatesProvider.check_updates(&evaluated.package); let update = check_updates_offline( evaluated.package.id.clone(), @@ -206,25 +252,23 @@ fn issue_action_from_registered_package_json_with_github_transport( pin_version: request.pin_version, }, )?; - let action = if update.actions.is_empty() { - None - } else { - Some(runtime.issue_action(SealedActionPlan { + let action = (!update.actions.is_empty()).then(|| { + runtime.issue_action(SealedActionPlan { package_id: update.package_id.clone(), actions: update.actions.clone(), lua_object: PackageVersionLuaObject { object_id: format!("package-update:{}", update.package_id), dependency_digest: evaluated.dependency_digest, }, - })) - }; - Ok(json!({ - "package": evaluated.package, - "update": update, - "action": action.map(issued_action_json), - "provider_calls": evaluated.provider_calls, - "runtime_hooks": evaluated.runtime_hooks, - })) + }) + }); + Ok(RegisteredPackageActionResult { + package: evaluated.package, + update, + action, + provider_calls: evaluated.provider_calls, + runtime_hooks: evaluated.runtime_hooks, + }) } pub fn submit_action_json( @@ -438,14 +482,14 @@ struct OfflineUpdateActionRequest { #[cfg(feature = "lua")] #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] -struct RegisteredPackageUpdateActionRequest { - package_id: PackageId, +pub(crate) struct RegisteredPackageUpdateActionRequest { + pub package_id: PackageId, #[serde(default)] - repository_id: Option, + pub repository_id: Option, #[serde(default)] - installed_version: Option, + pub installed_version: Option, #[serde(default, alias = "ignored_version")] - pin_version: Option, + pub pin_version: Option, } #[derive(Debug, Deserialize)] @@ -490,18 +534,20 @@ struct CleanTasksRequest { } #[cfg(feature = "lua")] -struct RegisteredPackageEvaluation { - package: getter_core::ResolvedPackage, +pub(crate) struct RegisteredPackageEvaluation { + pub package: getter_core::ResolvedPackage, + pub diagnostics: Vec, dependency_digest: String, provider_calls: Vec, runtime_hooks: Vec, } #[cfg(feature = "lua")] -fn evaluate_registered_package( +pub(crate) fn evaluate_registered_package( data_dir: &std::path::Path, db: &MainDb, request: &RegisteredPackageUpdateActionRequest, + cache_mode: ProviderCacheMode, github_release_transport: Option>, ) -> Result { let repositories = db.repositories()?; @@ -532,7 +578,7 @@ fn evaluate_registered_package( &metadata, script, ProviderBackedPackageEvalConfig { - mode: ProviderCacheMode::UseCached, + mode: cache_mode, fdroid_endpoint_id: None, fdroid_endpoint_url: None, fdroid_index_xml: None, @@ -548,8 +594,17 @@ fn evaluate_registered_package( "repo:{}:package:{}:hash:{}", cache_key.repository_id, cache_key.package_id, cache_key.package_file_hash ); + let diagnostics = provider_eval + .provider_calls + .iter() + .filter_map(|call| call.get("diagnostics")) + .filter_map(Value::as_array) + .flatten() + .filter_map(|diagnostic| serde_json::from_value(diagnostic.clone()).ok()) + .collect(); return Ok(RegisteredPackageEvaluation { package: provider_eval.package, + diagnostics, dependency_digest, provider_calls: provider_eval.provider_calls, runtime_hooks: provider_eval.runtime_hooks, diff --git a/crates/getter-operations/src/startup.rs b/crates/getter-operations/src/startup.rs index 090d7f6..c2a650a 100644 --- a/crates/getter-operations/src/startup.rs +++ b/crates/getter-operations/src/startup.rs @@ -248,7 +248,7 @@ fn diagnostic(code: &str, message: impl Into) -> StartupDiagnostic { } #[cfg_attr(not(feature = "lua"), allow(unused_mut, unused_variables))] -fn app_summary( +pub(crate) fn app_summary( data_dir: &Path, repositories: &[StoredRepository], inventory: &InstalledInventory, @@ -261,16 +261,23 @@ fn app_summary( None if tracked.package_resolution == StoredPackageResolution::OfficialRepositoryPackage => { - repositories.iter().find(|repository| { - let root = repository - .path - .as_ref() - .map(PathBuf::from) - .unwrap_or_else(|| data_dir.join("repo").join(repository.id.as_str())); - RepositoryPackageDirectoryLayout::load(&root) - .ok() - .is_some_and(|layout| layout.package(&tracked.package_id).is_some()) - }) + repositories + .iter() + .filter(|repository| { + let root = repository + .path + .as_ref() + .map(PathBuf::from) + .unwrap_or_else(|| data_dir.join("repo").join(repository.id.as_str())); + RepositoryPackageDirectoryLayout::load(&root) + .ok() + .is_some_and(|layout| layout.package(&tracked.package_id).is_some()) + }) + .max_by(|left, right| { + left.priority + .cmp(&right.priority) + .then_with(|| right.id.as_str().cmp(left.id.as_str())) + }) } None => None, }; @@ -279,6 +286,7 @@ fn app_summary( .repository_id .as_ref() .and_then(|id| repositories.iter().find(|repo| &repo.id == id)); + let resolved_repository_id = repository.map(|repository| repository.id.to_string()); let mut name = None; let mut installed_target = None; let mut warning = StartupWarning::default(); @@ -363,6 +371,49 @@ fn app_summary( )); } + finish_app_summary( + tracked, + resolved_repository_id, + name, + warning, + installed_target, + candidates, + diagnostics, + inventory, + ) +} + +#[cfg(feature = "lua")] +pub(crate) fn app_summary_from_resolved( + tracked: StoredTrackedPackage, + repository_id: Option, + inventory: &InstalledInventory, + resolved: getter_core::ResolvedPackage, +) -> StartupAppSummary { + let mut warning = StartupWarning::default(); + warning.free_network = resolved.permissions.free_network; + finish_app_summary( + tracked, + repository_id, + Some(resolved.name), + warning, + resolved.installed.first().map(target_dto), + resolved.updates, + Vec::new(), + inventory, + ) +} + +fn finish_app_summary( + tracked: StoredTrackedPackage, + resolved_repository_id: Option, + name: Option, + warning: StartupWarning, + installed_target: Option, + candidates: Vec, + mut diagnostics: Vec, + inventory: &InstalledInventory, +) -> StartupAppSummary { let installed_version = installed_target .as_ref() .and_then(|target| inventory_version(inventory, target)); @@ -411,7 +462,7 @@ fn app_summary( }; StartupAppSummary { package_id: tracked.package_id.to_string(), - repository_id: tracked.repository_id.map(|id| id.to_string()), + repository_id: resolved_repository_id, name, favorite: tracked.favorite, pin_version: tracked.pin_version, diff --git a/crates/getter-operations/tests/app.rs b/crates/getter-operations/tests/app.rs new file mode 100644 index 0000000..c076862 --- /dev/null +++ b/crates/getter-operations/tests/app.rs @@ -0,0 +1,338 @@ +#![cfg(feature = "lua")] + +use getter_core::autogen::{InstalledInventory, InstalledInventoryItem}; +use getter_core::repository::RepositoryMetadata; +use getter_core::runtime::GetterRuntime; +use getter_core::{RepositoryId, RepositoryPriority}; +use getter_operations::app::{ + check_app, check_app_with_github_transport, inspect_app, AppOperationError, +}; +use getter_operations::github_releases::{ + GithubReleaseTransport, GithubReleaseTransportError, GithubReleaseTransportRequest, + GithubReleaseTransportResponse, +}; +use getter_storage::{ + CacheDb, MainDb, ProviderResponseUpsert, StoredPackageResolution, TrackedPackageUpsert, +}; +use serde_json::json; +use std::cell::RefCell; +use std::fs; +use std::rc::Rc; + +fn write_package(root: &std::path::Path, name: &str, latest: &str) { + fs::create_dir_all(root).unwrap(); + fs::write(root.join("Manifest"), "").unwrap(); + fs::write(root.join("metadata.jsonc"), format!(r#"{{"type":"android:app","display_name":"{name}","android":{{"package_name":"com.example"}}}}"#)).unwrap(); + fs::write(root.join("1.lua"), format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{ {{ version="{latest}", artifacts={{{{name="app.apk",url="https://example.invalid/app.apk"}}}} }} }} }}"#)).unwrap(); +} + +fn register(data: &std::path::Path, repo: &str, priority: i32, explicit: bool, enabled: bool) { + let db = MainDb::open(data.join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new(repo).unwrap(), + name: repo.into(), + priority: RepositoryPriority::new(priority), + api_version: "v1".into(), + }, + Some(&data.join("repo").join(repo)), + None, + ) + .unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/com.example".parse().unwrap(), + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + repository_id: explicit.then(|| RepositoryId::new(repo).unwrap()), + enabled, + favorite: false, + pin_version: None, + }) + .unwrap(); +} + +const GITHUB_RELEASES: &str = r#"[{"tag_name":"2","draft":false,"prerelease":false,"assets":[{"name":"example.apk","browser_download_url":"https://example.invalid/example-v2.apk"}]}]"#; + +struct MockGithubTransport { + requests: RefCell, +} + +struct FailingGithubTransport; + +impl GithubReleaseTransport for FailingGithubTransport { + fn fetch_releases( + &self, + _request: &GithubReleaseTransportRequest<'_>, + ) -> Result { + Err(GithubReleaseTransportError::Transport("offline".into())) + } +} + +impl GithubReleaseTransport for MockGithubTransport { + fn fetch_releases( + &self, + _request: &GithubReleaseTransportRequest<'_>, + ) -> Result { + *self.requests.borrow_mut() += 1; + Ok(GithubReleaseTransportResponse { + body: GITHUB_RELEASES.into(), + etag: None, + last_modified: None, + }) + } +} + +fn write_github_package(root: &std::path::Path) { + fs::create_dir_all(root).unwrap(); + fs::write(root.join("Manifest"), "14be2aa0ad6385271f005345095584d79556562f96cdcb7c25f500f5f35246fccacb3bbfbc38f8df48c512cee853928aad8b4fb7138f4bf9c8b5835b7e46838a github\n").unwrap(); + fs::write( + root.join("metadata.jsonc"), + r#"{"type":"android:app","android":{"package_name":"com.example"}}"#, + ) + .unwrap(); + fs::write( + root.join("9999.lua"), + r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name = "Example", + android_package = "com.example", + owner = "owner", + repo = "repo", + asset = { include = "[.]apk$" }, +}"#, + ) + .unwrap(); +} + +fn inventory(version: &str) -> InstalledInventory { + InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "com.example".into(), + label: None, + version_name: Some(version.into()), + version_code: None, + }]) +} + +#[test] +fn inspect_uses_repository_priority_and_is_cache_only() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + write_package( + &temp.path().join("repo/low/android/com.example"), + "Low", + "2", + ); + write_package( + &temp.path().join("repo/high/android/com.example"), + "High", + "3", + ); + register(temp.path(), "low", 0, false, true); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("high").unwrap(), + name: "high".into(), + priority: RepositoryPriority::new(10), + api_version: "v1".into(), + }, + Some(&temp.path().join("repo/high")), + None, + ) + .unwrap(); + + let app = inspect_app( + temp.path(), + inventory("1"), + &"android/com.example".parse().unwrap(), + ) + .unwrap(); + assert_eq!(app.repository_id.as_deref(), Some("high")); + assert_eq!(app.name.as_deref(), Some("High")); + assert_eq!(app.latest_version.as_deref(), Some("3")); +} + +#[test] +fn inspect_honors_explicit_binding_and_reports_missing_inventory() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + write_package( + &temp.path().join("repo/low/android/com.example"), + "Low", + "2", + ); + register(temp.path(), "low", 0, true, true); + let app = inspect_app( + temp.path(), + InstalledInventory::new(vec![]), + &"android/com.example".parse().unwrap(), + ) + .unwrap(); + assert_eq!(app.repository_id.as_deref(), Some("low")); + assert_eq!( + app.update_status, + getter_operations::startup::UpdateStatus::NotInstalled + ); + assert!(app + .diagnostics + .iter() + .any(|d| d.code == "startup.installed_inventory_missing")); +} + +#[test] +fn inspect_rejects_untracked_and_disabled_packages() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + let id = "android/com.example".parse().unwrap(); + assert!(matches!( + inspect_app(temp.path(), InstalledInventory::new(vec![]), &id), + Err(AppOperationError::Untracked(_)) + )); + write_package( + &temp.path().join("repo/official/android/com.example"), + "Example", + "2", + ); + register(temp.path(), "official", 0, true, false); + assert!(matches!( + inspect_app(temp.path(), InstalledInventory::new(vec![]), &id), + Err(AppOperationError::Disabled(_)) + )); +} + +#[test] +fn inspect_reports_missing_package_definition_without_inventing_details() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + register(temp.path(), "official", 0, true, true); + let app = inspect_app( + temp.path(), + InstalledInventory::new(vec![]), + &"android/com.example".parse().unwrap(), + ) + .unwrap(); + assert_eq!(app.name, None); + assert_eq!(app.latest_version, None); + assert_eq!( + app.update_status, + getter_operations::startup::UpdateStatus::NotInstalled + ); + assert!(!app.diagnostics.is_empty()); +} + +#[test] +fn check_preserves_stale_cache_refresh_diagnostics_on_the_app() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + write_github_package(&temp.path().join("repo/official/android/com.example")); + register(temp.path(), "official", 0, false, true); + let config = getter_operations::github_releases::GithubReleaseConfig { + api_base_url: getter_operations::github_releases::DEFAULT_GITHUB_API_BASE_URL.into(), + owner: "owner".into(), + repo: "repo".into(), + }; + CacheDb::open(temp.path().join("cache.db")) + .unwrap() + .upsert_provider_response(&ProviderResponseUpsert { + cache_key: config.cache_key(), + provider: "github".into(), + response_json: serde_json::from_str(GITHUB_RELEASES).unwrap(), + source_response_sha512: vec!["14be2aa0ad6385271f005345095584d79556562f96cdcb7c25f500f5f35246fccacb3bbfbc38f8df48c512cee853928aad8b4fb7138f4bf9c8b5835b7e46838a".into()], + provenance_schema_version: Some("provider-response-provenance-v1".into()), + freshness_json: json!({}), + }) + .unwrap(); + + let checked = check_app_with_github_transport( + &mut GetterRuntime::new(), + temp.path(), + inventory("1"), + &"android/com.example".parse().unwrap(), + Some(Rc::new(FailingGithubTransport)), + ) + .unwrap(); + + let codes = checked + .app + .diagnostics + .iter() + .map(|diagnostic| diagnostic.code.as_str()) + .collect::>(); + assert!(codes.contains(&"cache.refresh_failed")); + assert!(codes.contains(&"used_stale_cache")); + assert!(checked + .app + .diagnostics + .iter() + .all(|diagnostic| !diagnostic.message.contains("api.github.com"))); +} + +#[test] +fn check_refreshes_cold_github_cache_before_matching_inventory_and_issuing_action() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + write_github_package(&temp.path().join("repo/official/android/com.example")); + register(temp.path(), "official", 0, false, true); + let transport = Rc::new(MockGithubTransport { + requests: RefCell::new(0), + }); + let mut runtime = GetterRuntime::new(); + + let checked = check_app_with_github_transport( + &mut runtime, + temp.path(), + inventory("1"), + &"android/com.example".parse().unwrap(), + Some(transport.clone()), + ) + .unwrap(); + + assert_eq!(*transport.requests.borrow(), 1); + assert_eq!(checked.app.installed_version.as_deref(), Some("1")); + assert_eq!(checked.app.latest_version.as_deref(), Some("2")); + assert_eq!( + checked.app.update_status, + getter_operations::startup::UpdateStatus::Available + ); + let action = checked.action.expect("update action"); + assert_eq!(action.package_id.to_string(), "android/com.example"); +} + +#[test] +fn check_issues_current_process_action_only_when_update_exists() { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + write_package( + &temp.path().join("repo/official/android/com.example"), + "Example", + "2", + ); + register(temp.path(), "official", 0, true, true); + let mut runtime = GetterRuntime::new(); + let updated = check_app( + &mut runtime, + temp.path(), + inventory("1"), + &"android/com.example".parse().unwrap(), + ) + .unwrap(); + assert_eq!( + updated.app.update_status, + getter_operations::startup::UpdateStatus::Available + ); + assert!(updated.action.is_some()); + + let current = check_app( + &mut runtime, + temp.path(), + inventory("2"), + &"android/com.example".parse().unwrap(), + ) + .unwrap(); + assert_eq!( + current.app.update_status, + getter_operations::startup::UpdateStatus::UpToDate + ); + assert!(current.action.is_none()); +} From 3365386ab980a3d24e1a88a8358a6180f32aa5e0 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Wed, 15 Jul 2026 23:06:57 +0800 Subject: [PATCH 68/71] feat(download): stage package artifacts --- crates/getter-cli/src/lib.rs | 24 +- crates/getter-cli/tests/app_cli.rs | 91 +++++ crates/getter-cli/tests/bdd_cli.rs | 93 +++++ .../features/cli/app_inspect_check.feature | 13 + crates/getter-core/src/lib.rs | 1 + crates/getter-core/src/manifest.rs | 108 +++++ crates/getter-core/tests/package_manifest.rs | 62 +++ crates/getter-operations/src/app.rs | 375 ++++++++++++++++- .../getter-operations/src/fdroid_autogen.rs | 71 +++- .../getter-operations/src/github_autogen.rs | 128 +++++- .../getter-operations/src/lua_http_policy.rs | 46 +-- .../src/lua_provider_host.rs | 45 +-- crates/getter-operations/src/runtime.rs | 2 + .../tests/artifact_staging.rs | 381 ++++++++++++++++++ 14 files changed, 1345 insertions(+), 95 deletions(-) create mode 100644 crates/getter-core/src/manifest.rs create mode 100644 crates/getter-core/tests/package_manifest.rs create mode 100644 crates/getter-operations/tests/artifact_staging.rs diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 391419d..470a360 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -66,6 +66,9 @@ pub enum CliCommand { package_id: PackageId, inventory: Option, }, + AppDownload { + package_id: PackageId, + }, HubList, RepoList, RepoAdd { @@ -477,19 +480,20 @@ where } [domain, command] if domain == "app" && command == "list" => CliCommand::AppList, [domain, command, package_id] - if domain == "app" && (command == "show" || command == "check") => + if domain == "app" + && (command == "show" || command == "check" || command == "download") => { let package_id = parse_package_id(package_id)?; - if command == "show" { - CliCommand::AppShow { + match command.as_str() { + "show" => CliCommand::AppShow { package_id, inventory: None, - } - } else { - CliCommand::AppCheck { + }, + "check" => CliCommand::AppCheck { package_id, inventory: None, - } + }, + _ => CliCommand::AppDownload { package_id }, } } [domain, command, package_id, flag, inventory] @@ -829,6 +833,11 @@ fn execute(invocation: CliInvocation) -> Result { ) .map_err(|error| CliError::Storage(error.to_string())) } + CliCommand::AppDownload { package_id } => { + let result = getter_operations::app::download_app(&invocation.data_dir, &package_id) + .map_err(map_app_error)?; + serde_json::to_value(result).map_err(|error| CliError::Storage(error.to_string())) + } CliCommand::AppCheck { package_id, inventory, @@ -2401,6 +2410,7 @@ impl CliCommand { Self::AppList => "app list", Self::AppShow { .. } => "app show", Self::AppCheck { .. } => "app check", + Self::AppDownload { .. } => "app download", Self::HubList => "hub list", Self::RepoList => "repo list", Self::RepoAdd { .. } => "repo add", diff --git a/crates/getter-cli/tests/app_cli.rs b/crates/getter-cli/tests/app_cli.rs index a3123de..aa354d1 100644 --- a/crates/getter-cli/tests/app_cli.rs +++ b/crates/getter-cli/tests/app_cli.rs @@ -4,6 +4,8 @@ use getter_core::{RepositoryId, RepositoryPriority}; use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; use serde_json::{json, Value}; use std::fs; +use std::io::{Read, Write}; +use std::net::TcpListener; fn fixture(latest: &str) -> tempfile::TempDir { let temp = tempfile::tempdir().unwrap(); @@ -44,6 +46,95 @@ return package_version {{ updates = {{ {{ version="{latest}", artifacts={{{{name temp } +#[test] +fn app_download_stages_once_then_reuses_the_verified_artifact() { + let bytes = b"deterministic artifact bytes"; + let digest = "3ac60f26821afbddb63263c8ec6eae881d2d2e77e2674c5c8c84e6404077b3a9"; + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let server = std::thread::spawn(move || { + let (mut stream, _) = listener.accept().unwrap(); + let mut request = [0; 1024]; + let _ = stream.read(&mut request).unwrap(); + write!( + stream, + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", + bytes.len() + ) + .unwrap(); + stream.write_all(bytes).unwrap(); + }); + let temp = fixture("2"); + let package = temp.path().join("repo/official/android/com.example"); + fs::write(package.join("Manifest"), format!("{digest} app.apk\n")).unwrap(); + fs::write( + package.join("1.lua"), + format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{ {{ version="2", artifacts={{{{name="app.apk",url="http://{address}/app.apk",file_name="app.apk"}}}} }} }} }}"#), + ) + .unwrap(); + + let first = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "download", + "android/com.example", + ]); + assert_eq!(first.exit_code, ExitCode::Success); + let first_json: Value = serde_json::from_str(&first.stdout).unwrap(); + assert_eq!(first_json["data"]["artifacts"][0]["status"], "downloaded"); + server.join().unwrap(); + + let second = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "download", + "android/com.example", + ]); + assert_eq!(second.exit_code, ExitCode::Success); + let second_json: Value = serde_json::from_str(&second.stdout).unwrap(); + assert_eq!(second_json["data"]["artifacts"][0]["status"], "reused"); + assert_eq!( + second_json["data"]["artifacts"][0]["path"], + first_json["data"]["artifacts"][0]["path"] + ); +} + +#[test] +fn app_download_rejects_transport_controls_and_maps_manifest_errors_stably() { + let temp = fixture("2"); + let controlled = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "download", + "android/com.example", + "--url", + "https://attacker.invalid/app.apk", + ]); + assert_eq!(controlled.exit_code, ExitCode::Usage); + let controlled_body: Value = serde_json::from_str(&controlled.stdout).unwrap(); + assert_eq!(controlled_body["error"]["code"], "cli.usage"); + + let missing = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "download", + "android/com.example", + ]); + assert_eq!(missing.exit_code, ExitCode::GenericFailure); + let missing_body: Value = serde_json::from_str(&missing.stdout).unwrap(); + assert_eq!(missing_body["error"]["code"], "artifact.manifest_missing"); + assert_ne!(missing_body["error"]["code"], "storage.error"); +} + #[test] fn app_show_is_normal_cache_only_detail_command() { let temp = fixture("2"); diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 10a762f..2e480bc 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -3,6 +3,8 @@ use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; use rusqlite::Connection; use serde_json::Value; use std::fs; +use std::io::{Read, Write}; +use std::net::TcpListener; use std::path::PathBuf; use std::process::Command; use tempfile::TempDir; @@ -27,6 +29,8 @@ struct CliWorld { fixture_repo_id: Option, fixture_repo_path: Option, fixture_package_id: Option, + local_artifact_server: Option>, + first_download_json: Option, output: Option, json: Option, } @@ -735,6 +739,80 @@ fn run_app_check_without_inventory(world: &mut CliWorld, package_id: String) { run_app_command(world, "check", package_id, false); } +#[given("the package has a deterministic locally served artifact with a Manifest checksum")] +fn deterministic_local_artifact(world: &mut CliWorld) { + let bytes = b"BDD deterministic artifact"; + let digest = "9951fef5dd31f26c142ff2dcb6de2c56a58a3873c0211fdbaaa9e792d22d4e59"; + let listener = TcpListener::bind("127.0.0.1:0").expect("bind local fixture server"); + let address = listener.local_addr().expect("fixture address"); + world.local_artifact_server = Some(std::thread::spawn(move || { + let (mut stream, _) = listener.accept().expect("fixture request"); + let mut request = [0; 1024]; + let _ = stream.read(&mut request).expect("read fixture request"); + write!( + stream, + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", + bytes.len() + ) + .expect("write fixture response headers"); + stream.write_all(bytes).expect("write fixture response"); + })); + let package_dir = world + .fixture_repo_path + .as_ref() + .expect("fixture repository") + .join( + world + .fixture_package_id + .as_ref() + .expect("fixture package") + .replace('/', std::path::MAIN_SEPARATOR_STR), + ); + fs::write(package_dir.join("Manifest"), format!("{digest} app.apk\n")) + .expect("write artifact Manifest"); + fs::write( + package_dir.join("1.20.0.lua"), + format!(r#"#!/bin/upa-lua v1 +return package_version {{ installed = {{ {{ kind = "android_package", package_name = "org.fdroid.fdroid" }} }}, updates = {{ {{ version = "1.20.0", artifacts = {{ {{ name = "app.apk", url = "http://{address}/app.apk", file_name = "app.apk" }} }} }} }} }}"#), + ) + .expect("write local artifact package"); +} + +#[when(expr = "I run getter app download for {string}")] +fn run_app_download(world: &mut CliWorld, package_id: String) { + run_app_command(world, "download", package_id, false); +} + +#[when(expr = "I run getter app download twice for {string}")] +fn run_app_download_twice(world: &mut CliWorld, package_id: String) { + run_app_command(world, "download", package_id.clone(), false); + let first = world.output.as_ref().expect("first output"); + assert_success(first); + world.first_download_json = Some(parse_stdout(first)); + world + .local_artifact_server + .take() + .expect("fixture server") + .join() + .expect("fixture server succeeds"); + run_app_command(world, "download", package_id, false); +} + +#[when(expr = "I run getter app download for {string} with a URL control")] +fn run_app_download_with_url_control(world: &mut CliWorld, package_id: String) { + world.output = Some(run_getter( + world, + [ + "app".to_owned(), + "download".to_owned(), + package_id, + "--url".to_owned(), + "https://attacker.invalid/app.apk".to_owned(), + ], + )); + world.json = None; +} + fn run_app_command(world: &mut CliWorld, command: &str, package_id: String, inventory: bool) { let mut args = vec!["app".to_owned(), command.to_owned(), package_id]; if inventory { @@ -766,6 +844,21 @@ fn app_check_available(world: &mut CliWorld) { assert!(json["data"]["action"]["action_id"].is_string()); } +#[then("the first download is downloaded and the second is reused from the same path")] +fn download_then_reuse(world: &mut CliWorld) { + let first = world + .first_download_json + .clone() + .expect("first download result"); + let second = current_json(world).clone(); + assert_eq!(first["data"]["artifacts"][0]["status"], "downloaded"); + assert_eq!(second["data"]["artifacts"][0]["status"], "reused"); + assert_eq!( + first["data"]["artifacts"][0]["path"], + second["data"]["artifacts"][0]["path"] + ); +} + #[then(expr = "the command fails with stable error {string}")] fn stable_error(world: &mut CliWorld, code: String) { let json = current_json(world); diff --git a/crates/getter-cli/tests/features/cli/app_inspect_check.feature b/crates/getter-cli/tests/features/cli/app_inspect_check.feature index f40badd..841ecf3 100644 --- a/crates/getter-cli/tests/features/cli/app_inspect_check.feature +++ b/crates/getter-cli/tests/features/cli/app_inspect_check.feature @@ -20,3 +20,16 @@ Feature: Inspect and actively check a tracked app Scenario: Check requires installed inventory When I run getter app check for "android/app/fdroid" without inventory Then the command fails with stable error "inventory.invalid" + + Scenario: Download requires an artifact Manifest checksum + When I run getter app download for "android/app/fdroid" + Then the command fails with stable error "artifact.manifest_missing" + + Scenario: Download stages an artifact and reuses it on the next invocation + Given the package has a deterministic locally served artifact with a Manifest checksum + When I run getter app download twice for "android/app/fdroid" + Then the first download is downloaded and the second is reused from the same path + + Scenario: Download rejects transport controls + When I run getter app download for "android/app/fdroid" with a URL control + Then the command fails with stable error "cli.usage" diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 3fce46b..0360c2b 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -14,6 +14,7 @@ pub mod autogen; pub mod diagnostics; #[cfg(feature = "lua")] pub mod lua; +pub mod manifest; pub mod repository; pub mod runtime; pub mod task; diff --git a/crates/getter-core/src/manifest.rs b/crates/getter-core/src/manifest.rs new file mode 100644 index 0000000..6a716c5 --- /dev/null +++ b/crates/getter-core/src/manifest.rs @@ -0,0 +1,108 @@ +use std::collections::HashMap; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ManifestAlgorithm { + Sha256, + Sha512, +} + +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct PackageManifest { + entries: HashMap<(ManifestAlgorithm, String), Vec>, +} + +#[derive(Debug, thiserror::Error, PartialEq, Eq)] +pub enum ManifestError { + #[error("malformed Manifest line {line}: {reason}")] + Malformed { line: usize, reason: String }, + #[error("duplicate Manifest entry for '{file_name}'")] + Duplicate { file_name: String }, +} + +impl PackageManifest { + pub fn parse(content: &str) -> Result { + let mut entries: HashMap<(ManifestAlgorithm, String), Vec> = HashMap::new(); + for (index, raw_line) in content.lines().enumerate() { + let line_number = index + 1; + let line = raw_line.trim(); + if line.is_empty() { + continue; + } + let Some(separator) = line.find(char::is_whitespace) else { + return Err(ManifestError::Malformed { + line: line_number, + reason: "expected ' '".to_owned(), + }); + }; + let digest = &line[..separator]; + let file_name = line[separator..].trim_start(); + if file_name.is_empty() || file_name.chars().any(char::is_control) { + return Err(ManifestError::Malformed { + line: line_number, + reason: "file name is empty or contains a control character".to_owned(), + }); + } + let algorithm = match digest.len() { + 64 => ManifestAlgorithm::Sha256, + 128 => ManifestAlgorithm::Sha512, + _ => { + return Err(ManifestError::Malformed { + line: line_number, + reason: "digest must be 64-hex SHA-256 or 128-hex SHA-512".to_owned(), + }) + } + }; + if !digest.bytes().all(|byte| byte.is_ascii_hexdigit()) { + return Err(ManifestError::Malformed { + line: line_number, + reason: "digest contains non-hex characters".to_owned(), + }); + } + let digest = digest.to_ascii_lowercase(); + let key = (algorithm, file_name.to_owned()); + let existing = entries.entry(key).or_default(); + match algorithm { + ManifestAlgorithm::Sha256 if existing.contains(&digest) => { + return Err(ManifestError::Duplicate { + file_name: file_name.to_owned(), + }); + } + ManifestAlgorithm::Sha512 if existing.contains(&digest) => continue, + _ => existing.push(digest), + } + } + Ok(Self { entries }) + } + + pub fn artifact_sha256(&self, file_name: &str) -> Option<&str> { + self.artifact_sha256_members(file_name) + .first() + .map(String::as_str) + } + + pub fn artifact_sha256_members(&self, file_name: &str) -> &[String] { + self.digests(file_name, ManifestAlgorithm::Sha256) + .unwrap_or_default() + } + + pub fn source_sha512(&self, file_name: &str) -> Option<&str> { + self.digests(file_name, ManifestAlgorithm::Sha512) + .and_then(|digests| digests.first()) + .map(String::as_str) + } + + pub fn contains_sha512(&self, digest: &str) -> bool { + self.entries.iter().any(|((algorithm, _), digests)| { + *algorithm == ManifestAlgorithm::Sha512 + && digests + .iter() + .any(|entry| entry.eq_ignore_ascii_case(digest)) + }) + } + + fn digests(&self, file_name: &str, algorithm: ManifestAlgorithm) -> Option<&[String]> { + self.entries + .get(&(algorithm, file_name.to_owned())) + .map(Vec::as_slice) + } +} diff --git a/crates/getter-core/tests/package_manifest.rs b/crates/getter-core/tests/package_manifest.rs new file mode 100644 index 0000000..d890869 --- /dev/null +++ b/crates/getter-core/tests/package_manifest.rs @@ -0,0 +1,62 @@ +use getter_core::manifest::{ManifestError, PackageManifest}; + +#[test] +fn parses_mixed_source_sha512_and_artifact_sha256_entries() { + let source = "a".repeat(128); + let artifact = "B".repeat(64); + let manifest = PackageManifest::parse(&format!( + "{source} github-releases.json\n{artifact} release/path/App.apk\n" + )) + .unwrap(); + + assert_eq!( + manifest.artifact_sha256("release/path/App.apk").unwrap(), + "b".repeat(64) + ); + assert_eq!( + manifest.source_sha512("github-releases.json").unwrap(), + source + ); +} + +#[test] +fn duplicate_artifact_filename_is_rejected_even_when_digest_matches() { + let digest = "a".repeat(64); + let error = + PackageManifest::parse(&format!("{digest} app.apk\n{digest} app.apk\n")).unwrap_err(); + + assert!(matches!(error, ManifestError::Duplicate { .. })); +} + +#[test] +fn sha256_and_multiple_sha512_memberships_can_share_one_filename() { + let artifact = "a".repeat(64); + let first_source = "b".repeat(128); + let second_source = "c".repeat(128); + let manifest = PackageManifest::parse(&format!( + "{artifact} app.apk\n{first_source} app.apk\n{second_source} app.apk\n" + )) + .unwrap(); + + assert_eq!(manifest.artifact_sha256("app.apk"), Some(artifact.as_str())); + assert!(manifest.contains_sha512(&first_source)); + assert!(manifest.contains_sha512(&second_source)); +} + +#[test] +fn preserves_multiple_distinct_sha256_memberships_for_one_filename() { + let first = "a".repeat(64); + let second = "b".repeat(64); + let manifest = PackageManifest::parse(&format!("{first} app.apk\n{second} app.apk\n")).unwrap(); + + assert_eq!( + manifest.artifact_sha256_members("app.apk"), + &[first, second] + ); +} + +#[test] +fn malformed_lines_are_rejected() { + let error = PackageManifest::parse("not-a-digest app.apk\n").unwrap_err(); + assert!(matches!(error, ManifestError::Malformed { line: 1, .. })); +} diff --git a/crates/getter-operations/src/app.rs b/crates/getter-operations/src/app.rs index 6558150..2550d19 100644 --- a/crates/getter-operations/src/app.rs +++ b/crates/getter-operations/src/app.rs @@ -5,14 +5,26 @@ use crate::github_releases::GithubReleaseTransport; use crate::startup::{self, StartupAppSummary}; use getter_core::autogen::{validate_installed_inventory, InstalledInventory}; #[cfg(feature = "lua")] +use getter_core::manifest::{ManifestError, PackageManifest}; +#[cfg(feature = "lua")] +use getter_core::repository::PACKAGE_MANIFEST_FILE; +#[cfg(feature = "lua")] use getter_core::runtime::GetterRuntime; use getter_core::runtime::IssuedAction; +#[cfg(feature = "lua")] +use getter_core::update::compare_versions; use getter_core::PackageId; +#[cfg(feature = "lua")] +use getter_core::UpdateArtifact; use getter_storage::{MainDb, StorageError, StoredTrackedPackage}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "lua")] +use sha2::{Digest, Sha256}; use std::path::Path; #[cfg(feature = "lua")] -use std::rc::Rc; +use std::path::PathBuf; +#[cfg(feature = "lua")] +use std::{fs, io::Write, rc::Rc}; #[derive(Debug, thiserror::Error)] pub enum AppOperationError { @@ -29,6 +41,40 @@ pub enum AppOperationError { #[cfg(feature = "lua")] #[error("app update check failed: {0}")] Runtime(#[from] crate::runtime::RuntimeOperationError), + #[cfg(feature = "lua")] + #[error("selected package has no download candidates")] + NoCandidates, + #[cfg(feature = "lua")] + #[error("package Manifest is missing")] + ManifestMissing, + #[cfg(feature = "lua")] + #[error("package Manifest is invalid: {0}")] + ManifestInvalid(ManifestError), + #[cfg(feature = "lua")] + #[error("artifact '{artifact}' filename '{file_name}' has no matching SHA-256 Manifest entry")] + ManifestArtifactMissing { artifact: String, file_name: String }, + #[cfg(feature = "lua")] + #[error("artifact '{artifact}' filename '{file_name}' has multiple SHA-256 Manifest entries and no digest hint")] + ManifestArtifactAmbiguous { artifact: String, file_name: String }, + #[cfg(feature = "lua")] + #[error("artifact '{artifact}' carries an invalid SHA-256 digest")] + ArtifactSha256Invalid { artifact: String }, + #[cfg(feature = "lua")] + #[error("artifact filenames '{first}' and '{second}' resolve to the same staging path")] + ArtifactPathCollision { first: String, second: String }, + #[cfg(feature = "lua")] + #[error("artifact '{artifact}' SHA-256 mismatch: expected {expected}, got {actual}")] + Sha256Mismatch { + artifact: String, + expected: String, + actual: String, + }, + #[cfg(feature = "lua")] + #[error("artifact staging I/O failed: {0}")] + ArtifactIo(#[from] std::io::Error), + #[cfg(feature = "lua")] + #[error("artifact download failed: {0}")] + ArtifactTransport(#[from] crate::download::RuntimeDownloadTransportError), } impl AppOperationError { @@ -45,6 +91,29 @@ impl AppOperationError { }, #[cfg(feature = "lua")] Self::Runtime(error) => error.code(), + #[cfg(feature = "lua")] + Self::NoCandidates => "app.no_candidates", + #[cfg(feature = "lua")] + Self::ManifestMissing => "artifact.manifest_missing", + #[cfg(feature = "lua")] + Self::ManifestInvalid(source) => match source { + ManifestError::Malformed { .. } => "artifact.manifest_malformed", + ManifestError::Duplicate { .. } => "artifact.manifest_duplicate", + }, + #[cfg(feature = "lua")] + Self::ManifestArtifactMissing { .. } => "artifact.manifest_missing", + #[cfg(feature = "lua")] + Self::ManifestArtifactAmbiguous { .. } => "artifact.manifest_ambiguous", + #[cfg(feature = "lua")] + Self::ArtifactSha256Invalid { .. } => "artifact.sha256_invalid", + #[cfg(feature = "lua")] + Self::ArtifactPathCollision { .. } => "artifact.path_collision", + #[cfg(feature = "lua")] + Self::Sha256Mismatch { .. } => "artifact.sha256_mismatch", + #[cfg(feature = "lua")] + Self::ArtifactIo(_) => "artifact.io_error", + #[cfg(feature = "lua")] + Self::ArtifactTransport(_) => "artifact.transport_error", } } @@ -57,6 +126,26 @@ impl AppOperationError { Self::Startup(_) => "Getter startup operation failed", #[cfg(feature = "lua")] Self::Runtime(error) => error.message(), + #[cfg(feature = "lua")] + Self::NoCandidates => "Getter app has no download candidate", + #[cfg(feature = "lua")] + Self::ManifestMissing => "Getter package Manifest is missing", + #[cfg(feature = "lua")] + Self::ManifestInvalid(_) => "Getter package Manifest is invalid", + #[cfg(feature = "lua")] + Self::ManifestArtifactMissing { .. } => "Getter artifact is missing from Manifest", + #[cfg(feature = "lua")] + Self::ManifestArtifactAmbiguous { .. } => "Getter artifact Manifest entry is ambiguous", + #[cfg(feature = "lua")] + Self::ArtifactSha256Invalid { .. } => "Getter artifact SHA-256 is invalid", + #[cfg(feature = "lua")] + Self::ArtifactPathCollision { .. } => "Getter artifact staging paths collide", + #[cfg(feature = "lua")] + Self::Sha256Mismatch { .. } => "Getter artifact SHA-256 did not match", + #[cfg(feature = "lua")] + Self::ArtifactIo(_) => "Getter artifact staging failed", + #[cfg(feature = "lua")] + Self::ArtifactTransport(_) => "Getter artifact download failed", } } @@ -68,6 +157,18 @@ impl AppOperationError { Self::Startup(error) => error.to_string(), #[cfg(feature = "lua")] Self::Runtime(error) => error.to_string(), + #[cfg(feature = "lua")] + Self::NoCandidates => self.to_string(), + #[cfg(feature = "lua")] + Self::ManifestMissing + | Self::ManifestInvalid(_) + | Self::ManifestArtifactMissing { .. } + | Self::ManifestArtifactAmbiguous { .. } + | Self::ArtifactSha256Invalid { .. } + | Self::ArtifactPathCollision { .. } + | Self::Sha256Mismatch { .. } + | Self::ArtifactIo(_) + | Self::ArtifactTransport(_) => self.to_string(), } } } @@ -79,6 +180,32 @@ pub struct AppCheckResult { pub action: Option, } +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AppDownloadResult { + pub package_id: PackageId, + pub repository_id: String, + pub version: String, + pub artifacts: Vec, +} + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct StagedArtifact { + pub name: String, + pub path: PathBuf, + pub sha256: String, + pub status: String, +} + +#[cfg(feature = "lua")] +struct PreparedArtifact { + artifact: UpdateArtifact, + digest: String, + final_path: PathBuf, + temporary_path: PathBuf, +} + fn tracked_package( db: &MainDb, package_id: &PackageId, @@ -113,6 +240,252 @@ pub fn inspect_app( )) } +#[cfg(feature = "lua")] +pub fn download_app( + data_dir: &Path, + package_id: &PackageId, +) -> Result { + download_app_with_transports( + data_dir, + package_id, + Some(Rc::new( + crate::github_releases::UreqGithubReleaseTransport::new(), + )), + Rc::new(crate::download::UreqRuntimeDownloadTransport::new()), + ) +} + +#[cfg(feature = "lua")] +pub fn download_app_with_transports( + data_dir: &Path, + package_id: &PackageId, + github_release_transport: Option>, + download_transport: Rc, +) -> Result { + startup::bootstrap_data_dir(data_dir)?; + let db = MainDb::open(data_dir.join("main.db"))?; + let tracked = tracked_package(&db, package_id)?; + let evaluation = crate::runtime::evaluate_registered_package( + data_dir, + &db, + &crate::runtime::RegisteredPackageUpdateActionRequest { + package_id: package_id.clone(), + repository_id: tracked.repository_id, + installed_version: None, + pin_version: None, + }, + crate::provider_cache::ProviderCacheMode::ForceRefresh, + github_release_transport, + )?; + let candidate = evaluation + .package + .updates + .iter() + .max_by(|left, right| compare_versions(&left.version, &right.version)) + .cloned() + .ok_or(AppOperationError::NoCandidates)?; + + let manifest_path = evaluation.package_path.join(PACKAGE_MANIFEST_FILE); + let manifest_content = fs::read_to_string(&manifest_path).map_err(|error| { + if error.kind() == std::io::ErrorKind::NotFound { + AppOperationError::ManifestMissing + } else { + AppOperationError::ArtifactIo(error) + } + })?; + let manifest = + PackageManifest::parse(&manifest_content).map_err(AppOperationError::ManifestInvalid)?; + let downloads = data_dir.join("downloads"); + let prepared = candidate + .artifacts + .iter() + .map(|artifact| prepare_artifact(&downloads, &manifest, artifact)) + .collect::, _>>()?; + let mut staging_paths = std::collections::HashMap::new(); + for artifact in &prepared { + let file_name = artifact + .artifact + .file_name + .clone() + .unwrap_or_else(|| artifact.artifact.name.clone()); + for path in [&artifact.final_path, &artifact.temporary_path] { + if let Some(first) = staging_paths.insert(path.clone(), file_name.clone()) { + return Err(AppOperationError::ArtifactPathCollision { + first, + second: file_name, + }); + } + } + } + if prepared + .iter() + .any(|artifact| !artifact.final_path.exists()) + { + fs::create_dir_all(&downloads)?; + } + + let artifacts = prepared + .into_iter() + .map(|artifact| stage_artifact(artifact, download_transport.as_ref())) + .collect::, _>>()?; + Ok(AppDownloadResult { + package_id: package_id.clone(), + repository_id: evaluation.package.repository.to_string(), + version: candidate.version, + artifacts, + }) +} + +#[cfg(feature = "lua")] +fn prepare_artifact( + downloads: &Path, + manifest: &PackageManifest, + artifact: &UpdateArtifact, +) -> Result { + let file_name = artifact + .file_name + .as_deref() + .unwrap_or(artifact.name.as_str()); + let members = manifest.artifact_sha256_members(file_name); + let digest = if let Some(hint) = artifact.sha256.as_deref() { + if hint.len() != 64 || !hint.bytes().all(|byte| byte.is_ascii_hexdigit()) { + return Err(AppOperationError::ArtifactSha256Invalid { + artifact: artifact.name.clone(), + }); + } + let hint = hint.to_ascii_lowercase(); + if !members.iter().any(|member| member == &hint) { + return Err(AppOperationError::ManifestArtifactMissing { + artifact: artifact.name.clone(), + file_name: file_name.to_owned(), + }); + } + hint + } else { + match members { + [] => { + return Err(AppOperationError::ManifestArtifactMissing { + artifact: artifact.name.clone(), + file_name: file_name.to_owned(), + }); + } + [digest] => digest.clone(), + _ => { + return Err(AppOperationError::ManifestArtifactAmbiguous { + artifact: artifact.name.clone(), + file_name: file_name.to_owned(), + }); + } + } + }; + let name = sanitized_artifact_file_name(file_name); + let final_path = downloads.join(format!("{digest}-{name}")); + let temporary_path = PathBuf::from(format!( + "{}.download", + final_path.as_os_str().to_string_lossy() + )); + Ok(PreparedArtifact { + artifact: artifact.clone(), + final_path, + temporary_path, + digest, + }) +} + +#[cfg(feature = "lua")] +fn stage_artifact( + prepared: PreparedArtifact, + transport: &dyn crate::download::RuntimeDownloadTransport, +) -> Result { + if prepared.final_path.exists() { + return Ok(staged_artifact(&prepared, "reused")); + } + let mut sink = StagingSink::new(&prepared.temporary_path)?; + transport.fetch( + &crate::download::RuntimeDownloadTransportRequest { + url: &prepared.artifact.url, + }, + &mut sink, + )?; + let actual = sink.finish()?; + if actual != prepared.digest { + return Err(AppOperationError::Sha256Mismatch { + artifact: prepared.artifact.name, + expected: prepared.digest, + actual, + }); + } + fs::rename(&prepared.temporary_path, &prepared.final_path)?; + Ok(staged_artifact(&prepared, "downloaded")) +} + +#[cfg(feature = "lua")] +fn staged_artifact(prepared: &PreparedArtifact, status: &str) -> StagedArtifact { + StagedArtifact { + name: prepared.artifact.name.clone(), + path: prepared.final_path.clone(), + sha256: prepared.digest.clone(), + status: status.to_owned(), + } +} + +#[cfg(feature = "lua")] +struct StagingSink { + file: fs::File, + hasher: Sha256, +} + +#[cfg(feature = "lua")] +impl StagingSink { + fn new(path: &Path) -> Result { + Ok(Self { + file: fs::File::create(path)?, + hasher: Sha256::new(), + }) + } + + fn finish(mut self) -> Result { + self.file.flush()?; + self.file.sync_all()?; + Ok(format!("{:x}", self.hasher.finalize())) + } +} + +#[cfg(feature = "lua")] +impl crate::download::RuntimeDownloadSink for StagingSink { + fn set_total_bytes(&mut self, _total_bytes: Option) -> Result<(), String> { + Ok(()) + } + + fn write_chunk(&mut self, chunk: &[u8]) -> Result<(), String> { + self.file + .write_all(chunk) + .map_err(|error| error.to_string())?; + self.hasher.update(chunk); + Ok(()) + } +} + +#[cfg(feature = "lua")] +fn sanitized_artifact_file_name(raw: &str) -> String { + let sanitized = raw + .trim() + .chars() + .map(|character| { + if character.is_control() || matches!(character, '/' | '\\') { + '_' + } else { + character + } + }) + .collect::(); + if sanitized.is_empty() || sanitized == "." || sanitized == ".." { + "artifact.bin".to_owned() + } else { + sanitized + } +} + #[cfg(feature = "lua")] pub fn check_app( runtime: &mut GetterRuntime, diff --git a/crates/getter-operations/src/fdroid_autogen.rs b/crates/getter-operations/src/fdroid_autogen.rs index 3e3ea1a..a6b3eae 100644 --- a/crates/getter-operations/src/fdroid_autogen.rs +++ b/crates/getter-operations/src/fdroid_autogen.rs @@ -22,6 +22,7 @@ use getter_providers::FdroidApp; use getter_storage::{CacheDb, MainDb}; use serde::Deserialize; use serde_json::{json, Value}; +use std::collections::HashSet; use std::path::{Path, PathBuf}; pub fn preview_fdroid_packages_json( @@ -301,7 +302,7 @@ fn fdroid_generated_files( "type": "android:app", "android": { "package_name": app.package_name }, }))?; - let manifest = fdroid_manifest(source_response_sha512); + let manifest = fdroid_manifest(source_response_sha512, app); let version_lua = fdroid_version_lua(app); Ok(vec![ generated_file("metadata.jsonc", metadata), @@ -310,11 +311,26 @@ fn fdroid_generated_files( ]) } -fn fdroid_manifest(source_response_sha512: &[String]) -> String { - source_response_sha512 - .iter() - .map(|digest| format!("{digest} fdroid-index.xml\n")) - .collect() +fn fdroid_manifest(source_response_sha512: &[String], app: &FdroidApp) -> String { + let mut memberships = HashSet::new(); + let mut manifest = String::new(); + for digest in source_response_sha512 { + let membership = (digest.clone(), "fdroid-index.xml".to_owned()); + if memberships.insert(membership.clone()) { + manifest.push_str(&format!("{} {}\n", membership.0, membership.1)); + } + } + for release in &app.packages { + if let Some(digest) = release.sha256.as_deref().filter(|digest| { + digest.len() == 64 && digest.bytes().all(|byte| byte.is_ascii_hexdigit()) + }) { + let membership = (digest.to_ascii_lowercase(), release.apk_name.clone()); + if memberships.insert(membership.clone()) { + manifest.push_str(&format!("{} {}\n", membership.0, membership.1)); + } + } + } + manifest } fn fdroid_version_lua(app: &FdroidApp) -> String { @@ -460,8 +476,10 @@ mod tests { assert_eq!( manifest, format!( - "{} fdroid-index.xml\n", - source_response_sha512(FDROID_FIXTURE) + "{} fdroid-index.xml\n{} org.fdroid.fdroid_1020000.apk\n{} org.fdroid.fdroid_1019000.apk\n", + source_response_sha512(FDROID_FIXTURE), + "a".repeat(64), + "b".repeat(64), ) ); assert_eq!( @@ -475,6 +493,43 @@ mod tests { assert!(!temp.path().join("repo/autogen").exists()); } + #[test] + fn manifest_deduplicates_reused_artifact_membership() { + let digest = "A".repeat(64); + let app = FdroidApp { + package_name: "org.example.app".to_owned(), + name: None, + summary: None, + packages: vec![ + getter_providers::FdroidRelease { + version: "2".to_owned(), + version_code: Some(2), + apk_name: "app.apk".to_owned(), + sha256: Some(digest.clone()), + size: None, + }, + getter_providers::FdroidRelease { + version: "1".to_owned(), + version_code: Some(1), + apk_name: "app.apk".to_owned(), + sha256: Some(digest.clone()), + size: None, + }, + ], + }; + + let manifest = fdroid_manifest(&["b".repeat(128)], &app); + + getter_core::manifest::PackageManifest::parse(&manifest).unwrap(); + assert_eq!( + manifest + .lines() + .filter(|line| *line == format!("{} app.apk", digest.to_ascii_lowercase())) + .count(), + 1 + ); + } + #[test] fn provider_module_generation_rejects_non_default_endpoint() { let app = FdroidApp { diff --git a/crates/getter-operations/src/github_autogen.rs b/crates/getter-operations/src/github_autogen.rs index bd20d0f..554186e 100644 --- a/crates/getter-operations/src/github_autogen.rs +++ b/crates/getter-operations/src/github_autogen.rs @@ -25,6 +25,7 @@ use getter_providers::{ use getter_storage::{CacheDb, MainDb}; use serde::Deserialize; use serde_json::{json, Value}; +use std::collections::HashSet; use std::path::{Path, PathBuf}; pub fn preview_github_android_package_json( @@ -155,6 +156,7 @@ where } else { candidates.push(github_candidate_json( &result.source_response_sha512, + &update_candidates, &package_id, GithubGeneratedPackageInput { owner: &owner, @@ -265,11 +267,12 @@ struct GithubGeneratedPackageInput<'a> { fn github_candidate_json( source_response_sha512: &[String], + update_candidates: &[getter_core::UpdateCandidate], package_id: &PackageId, input: GithubGeneratedPackageInput<'_>, ) -> AutogenOperationResult { let relative_path = package_relative_path(package_id); - let files = github_generated_files(source_response_sha512, &input)?; + let files = github_generated_files(source_response_sha512, update_candidates, &input)?; let record = AutogenRecord { version: AUTOGEN_RECORD_VERSION, generator: GITHUB_AUTOGEN_GENERATOR.to_owned(), @@ -325,6 +328,7 @@ fn github_candidate_json( fn github_generated_files( source_response_sha512: &[String], + update_candidates: &[getter_core::UpdateCandidate], input: &GithubGeneratedPackageInput<'_>, ) -> AutogenOperationResult> { if source_response_sha512.is_empty() { @@ -338,7 +342,7 @@ fn github_generated_files( "display_name": input.display_name, "android": { "package_name": input.android_package }, }))?; - let manifest = github_manifest(source_response_sha512); + let manifest = github_manifest(source_response_sha512, update_candidates); let version_lua = github_version_lua(input); Ok(vec![ generated_file("metadata.jsonc", metadata), @@ -347,11 +351,31 @@ fn github_generated_files( ]) } -fn github_manifest(source_response_sha512: &[String]) -> String { - source_response_sha512 - .iter() - .map(|digest| format!("{digest} github-releases.json\n")) - .collect() +fn github_manifest( + source_response_sha512: &[String], + update_candidates: &[getter_core::UpdateCandidate], +) -> String { + let mut memberships = HashSet::new(); + let mut manifest = String::new(); + for digest in source_response_sha512 { + let membership = (digest.clone(), "github-releases.json".to_owned()); + if memberships.insert(membership.clone()) { + manifest.push_str(&format!("{} {}\n", membership.0, membership.1)); + } + } + for candidate in update_candidates { + for artifact in &candidate.artifacts { + if let (Some(file_name), Some(digest)) = (&artifact.file_name, &artifact.sha256) { + if digest.len() == 64 && digest.bytes().all(|byte| byte.is_ascii_hexdigit()) { + let membership = (digest.to_ascii_lowercase(), file_name.clone()); + if memberships.insert(membership.clone()) { + manifest.push_str(&format!("{} {}\n", membership.0, membership.1)); + } + } + } + } + } + manifest } fn github_version_lua(input: &GithubGeneratedPackageInput<'_>) -> String { @@ -563,6 +587,95 @@ mod tests { assert!(!temp.path().join("repo/autogen").exists()); } + #[test] + fn manifest_covers_valid_digests_for_every_filtered_candidate() { + let first = getter_core::UpdateCandidate { + version: "1".to_owned(), + version_code: None, + channel: Some("stable".to_owned()), + changelog: None, + source: Some("github".to_owned()), + artifacts: vec![getter_core::UpdateArtifact { + name: "first.apk".to_owned(), + url: "https://example.invalid/first.apk".to_owned(), + content_type: None, + file_name: Some("first.apk".to_owned()), + sha256: Some("A".repeat(64)), + size: None, + }], + }; + let second = getter_core::UpdateCandidate { + version: "2".to_owned(), + version_code: None, + channel: Some("stable".to_owned()), + changelog: None, + source: Some("github".to_owned()), + artifacts: vec![ + getter_core::UpdateArtifact { + name: "second.apk".to_owned(), + url: "https://example.invalid/second.apk".to_owned(), + content_type: None, + file_name: Some("second.apk".to_owned()), + sha256: Some("B".repeat(64)), + size: None, + }, + getter_core::UpdateArtifact { + name: "invalid.apk".to_owned(), + url: "https://example.invalid/invalid.apk".to_owned(), + content_type: None, + file_name: Some("invalid.apk".to_owned()), + sha256: Some("not-a-digest".to_owned()), + size: None, + }, + ], + }; + + let repeated = getter_core::UpdateCandidate { + version: "3".to_owned(), + version_code: None, + channel: Some("stable".to_owned()), + changelog: None, + source: Some("github".to_owned()), + artifacts: vec![getter_core::UpdateArtifact { + name: "first.apk".to_owned(), + url: "https://example.invalid/new-first.apk".to_owned(), + content_type: None, + file_name: Some("first.apk".to_owned()), + sha256: Some("A".repeat(64)), + size: None, + }], + }; + let distinct = getter_core::UpdateCandidate { + version: "4".to_owned(), + version_code: None, + channel: Some("stable".to_owned()), + changelog: None, + source: Some("github".to_owned()), + artifacts: vec![getter_core::UpdateArtifact { + name: "first.apk".to_owned(), + url: "https://example.invalid/distinct-first.apk".to_owned(), + content_type: None, + file_name: Some("first.apk".to_owned()), + sha256: Some("D".repeat(64)), + size: None, + }], + }; + + let manifest = github_manifest(&["c".repeat(128)], &[first, second, repeated, distinct]); + + getter_core::manifest::PackageManifest::parse(&manifest).unwrap(); + assert_eq!( + manifest + .lines() + .filter(|line| *line == format!("{} first.apk", "a".repeat(64))) + .count(), + 1 + ); + assert!(manifest.contains(&format!("{} first.apk\n", "d".repeat(64)))); + assert!(manifest.contains(&format!("{} second.apk\n", "b".repeat(64)))); + assert!(!manifest.contains("invalid.apk")); + } + #[test] fn preview_without_fixture_refreshes_github_releases_through_getter_transport() { let temp = tempfile::tempdir().unwrap(); @@ -688,6 +801,7 @@ mod tests { exclude: None, }; let error = github_generated_files( + &[], &[], &GithubGeneratedPackageInput { owner: "DUpdateSystem", diff --git a/crates/getter-operations/src/lua_http_policy.rs b/crates/getter-operations/src/lua_http_policy.rs index da254ce..b3d11a4 100644 --- a/crates/getter-operations/src/lua_http_policy.rs +++ b/crates/getter-operations/src/lua_http_policy.rs @@ -18,7 +18,7 @@ use getter_core::repository::{ use getter_core::{RepositoryId, ResolvedPackage}; use sha2::{Digest, Sha512}; use std::cell::RefCell; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeMap; use std::fs; use std::path::{Path, PathBuf}; use std::rc::Rc; @@ -148,20 +148,14 @@ pub fn evaluate_package_directory_script_with_fixture_http_and_runtime_hooks( } #[derive(Debug, Clone)] -struct PackageManifest { - sha512: BTreeSet, -} +struct PackageManifest(getter_core::manifest::PackageManifest); impl PackageManifest { fn load(path: impl AsRef) -> Result { let path = path.as_ref(); let source = match fs::read_to_string(path) { Ok(source) => source, - Err(source) if source.kind() == std::io::ErrorKind::NotFound => { - return Ok(Self { - sha512: BTreeSet::new(), - }) - } + Err(source) if source.kind() == std::io::ErrorKind::NotFound => String::new(), Err(source) => { return Err(LuaHttpPolicyError::ReadManifest { path: path.to_path_buf(), @@ -169,39 +163,19 @@ impl PackageManifest { }) } }; - let mut sha512 = BTreeSet::new(); - for (line_index, raw_line) in source.lines().enumerate() { - let line = raw_line.trim(); - if line.is_empty() { - continue; - } - let hash = line - .split_whitespace() - .next() - .expect("non-empty line has token"); - if !is_sha512_hex(hash) { - return Err(LuaHttpPolicyError::InvalidManifest { - path: path.to_path_buf(), - reason: format!( - "line {} must start with a 128-character SHA-512 hex digest", - line_index + 1 - ), - }); - } - sha512.insert(hash.to_ascii_lowercase()); - } - Ok(Self { sha512 }) + getter_core::manifest::PackageManifest::parse(&source) + .map(Self) + .map_err(|source| LuaHttpPolicyError::InvalidManifest { + path: path.to_path_buf(), + reason: source.to_string(), + }) } fn contains(&self, digest: &str) -> bool { - self.sha512.contains(digest) + self.0.contains_sha512(digest) } } -fn is_sha512_hex(value: &str) -> bool { - value.len() == 128 && value.bytes().all(|byte| byte.is_ascii_hexdigit()) -} - fn sha512_hex(body: &[u8]) -> String { let mut hasher = Sha512::new(); hasher.update(body); diff --git a/crates/getter-operations/src/lua_provider_host.rs b/crates/getter-operations/src/lua_provider_host.rs index 4e0f8da..e68b349 100644 --- a/crates/getter-operations/src/lua_provider_host.rs +++ b/crates/getter-operations/src/lua_provider_host.rs @@ -45,7 +45,6 @@ use serde::Deserialize; use serde_json::{json, Map, Value}; use sha2::{Digest, Sha512}; use std::cell::RefCell; -use std::collections::BTreeSet; use std::path::{Path, PathBuf}; use std::rc::Rc; @@ -1021,20 +1020,14 @@ fn lua_table_to_json_value(table: Table) -> mlua::Result { } #[derive(Debug, Clone)] -struct PackageManifest { - sha512: BTreeSet, -} +struct PackageManifest(getter_core::manifest::PackageManifest); impl PackageManifest { fn load(path: impl AsRef) -> Result { let path = path.as_ref(); let source = match std::fs::read_to_string(path) { Ok(source) => source, - Err(source) if source.kind() == std::io::ErrorKind::NotFound => { - return Ok(Self { - sha512: BTreeSet::new(), - }) - } + Err(source) if source.kind() == std::io::ErrorKind::NotFound => String::new(), Err(source) => { return Err(LuaProviderHostOperationError::ReadManifest { path: path.to_path_buf(), @@ -1042,28 +1035,12 @@ impl PackageManifest { }) } }; - let mut sha512 = BTreeSet::new(); - for (line_index, raw_line) in source.lines().enumerate() { - let line = raw_line.trim(); - if line.is_empty() { - continue; - } - let hash = line - .split_whitespace() - .next() - .expect("non-empty line has token"); - if !is_sha512_hex(hash) { - return Err(LuaProviderHostOperationError::InvalidManifest { - path: path.to_path_buf(), - reason: format!( - "line {} must start with a 128-character SHA-512 hex digest", - line_index + 1 - ), - }); - } - sha512.insert(hash.to_ascii_lowercase()); - } - Ok(Self { sha512 }) + getter_core::manifest::PackageManifest::parse(&source) + .map(Self) + .map_err(|source| LuaProviderHostOperationError::InvalidManifest { + path: path.to_path_buf(), + reason: source.to_string(), + }) } fn allows_body(&self, body: &str) -> bool { @@ -1071,14 +1048,10 @@ impl PackageManifest { } fn allows_sha512(&self, digest: &str) -> bool { - is_sha512_hex(digest) && self.sha512.contains(&digest.to_ascii_lowercase()) + self.0.contains_sha512(digest) } } -fn is_sha512_hex(value: &str) -> bool { - value.len() == 128 && value.bytes().all(|byte| byte.is_ascii_hexdigit()) -} - fn sha512_hex(body: &[u8]) -> String { let mut hasher = Sha512::new(); hasher.update(body); diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index 9c2d6b4..b82cc76 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -536,6 +536,7 @@ struct CleanTasksRequest { #[cfg(feature = "lua")] pub(crate) struct RegisteredPackageEvaluation { pub package: getter_core::ResolvedPackage, + pub package_path: PathBuf, pub diagnostics: Vec, dependency_digest: String, provider_calls: Vec, @@ -604,6 +605,7 @@ pub(crate) fn evaluate_registered_package( .collect(); return Ok(RegisteredPackageEvaluation { package: provider_eval.package, + package_path: package_directory.path.clone(), diagnostics, dependency_digest, provider_calls: provider_eval.provider_calls, diff --git a/crates/getter-operations/tests/artifact_staging.rs b/crates/getter-operations/tests/artifact_staging.rs new file mode 100644 index 0000000..f2fc6fe --- /dev/null +++ b/crates/getter-operations/tests/artifact_staging.rs @@ -0,0 +1,381 @@ +#![cfg(feature = "lua")] + +use getter_core::repository::RepositoryMetadata; +use getter_core::{RepositoryId, RepositoryPriority}; +use getter_operations::app::download_app_with_transports; +use getter_operations::download::{ + RuntimeDownloadSink, RuntimeDownloadTransport, RuntimeDownloadTransportError, + RuntimeDownloadTransportRequest, +}; +use getter_operations::github_releases::{ + GithubReleaseTransport, GithubReleaseTransportError, GithubReleaseTransportRequest, + GithubReleaseTransportResponse, +}; +use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; +use sha2::{Digest, Sha256}; +use std::cell::RefCell; +use std::collections::HashMap; +use std::fs; +use std::rc::Rc; + +struct BytesTransport { + bodies: HashMap>, + requests: RefCell>, +} + +impl RuntimeDownloadTransport for BytesTransport { + fn fetch( + &self, + request: &RuntimeDownloadTransportRequest, + sink: &mut dyn RuntimeDownloadSink, + ) -> Result<(), RuntimeDownloadTransportError> { + self.requests.borrow_mut().push(request.url.to_owned()); + let body = self.bodies.get(request.url).unwrap(); + sink.set_total_bytes(Some(body.len() as u64)) + .map_err(RuntimeDownloadTransportError::Sink)?; + sink.write_chunk(body) + .map_err(RuntimeDownloadTransportError::Sink) + } +} + +struct GithubMock { + body: Vec, + requests: RefCell, +} + +impl GithubReleaseTransport for GithubMock { + fn fetch_releases( + &self, + _request: &GithubReleaseTransportRequest<'_>, + ) -> Result { + *self.requests.borrow_mut() += 1; + Ok(GithubReleaseTransportResponse { + body: String::from_utf8(self.body.clone()).unwrap(), + etag: None, + last_modified: None, + }) + } +} + +fn digest(bytes: &[u8]) -> String { + format!("{:x}", Sha256::digest(bytes)) +} + +fn fixture(script: &str, manifest: &str, pin: Option<&str>) -> tempfile::TempDir { + let temp = tempfile::tempdir().unwrap(); + let package = temp.path().join("repo/official/android/com.example"); + fs::create_dir_all(&package).unwrap(); + fs::write(package.join("Manifest"), manifest).unwrap(); + fs::write( + package.join("metadata.jsonc"), + r#"{"type":"android:app","display_name":"Example","android":{"package_name":"com.example"}}"#, + ) + .unwrap(); + fs::write(package.join("1.lua"), script).unwrap(); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".into(), + priority: RepositoryPriority::new(0), + api_version: "1".into(), + }, + Some(&temp.path().join("repo/official")), + None, + ) + .unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/com.example".parse().unwrap(), + enabled: true, + favorite: false, + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + pin_version: pin.map(str::to_owned), + }) + .unwrap(); + temp +} + +#[test] +fn stages_multiple_artifacts_from_manifest_in_declaration_order_and_ignores_baselines() { + let first = b"apk bytes"; + let second = b"symbols bytes"; + let script = r#"#!/bin/upa-lua v1 +return package_version { updates = { + { version="2", artifacts={{name="old",url="mock://old",file_name="old.apk"}} }, + { version="10", artifacts={ + {name="apk",url="mock://apk",file_name="release/path/Example APK.apk"}, + {name="symbols",url="mock://symbols",file_name="symbols.zip"} + } } +} }"#; + let manifest = format!( + "{} release/path/Example APK.apk\n{} symbols.zip\n", + digest(first), + digest(second) + ); + let temp = fixture(script, &manifest, Some("999")); + let transport = Rc::new(BytesTransport { + bodies: HashMap::from([ + ("mock://apk".into(), first.to_vec()), + ("mock://symbols".into(), second.to_vec()), + ]), + requests: RefCell::new(Vec::new()), + }); + + let result = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + transport.clone(), + ) + .unwrap(); + + assert_eq!(result.version, "10"); + assert_eq!( + result + .artifacts + .iter() + .map(|a| a.name.as_str()) + .collect::>(), + ["apk", "symbols"] + ); + assert!(result.artifacts[0] + .path + .ends_with(format!("{}-release_path_Example APK.apk", digest(first)))); + assert_eq!( + &*transport.requests.borrow(), + &["mock://apk", "mock://symbols"] + ); + assert_eq!(fs::read(&result.artifacts[0].path).unwrap(), first); +} + +#[test] +fn invalid_manifest_preflight_prevents_every_artifact_request() { + for manifest in [ + "", + "not-a-digest app.apk\n", + &format!("{} app.apk\n{} app.apk\n", "a".repeat(64), "a".repeat(64)), + ] { + let script = r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version="2", artifacts={{name="app",url="mock://app",file_name="app.apk"}} }} }"#; + let temp = fixture(script, manifest, None); + let transport = Rc::new(BytesTransport { + bodies: HashMap::new(), + requests: RefCell::new(Vec::new()), + }); + + let error = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + transport.clone(), + ) + .unwrap_err(); + assert!( + error.code().starts_with("artifact.manifest_") + || error.to_string().contains("package Manifest is invalid") + || format!("{error:?}").contains("InvalidManifest"), + "unexpected error: {error:?}" + ); + assert!(transport.requests.borrow().is_empty()); + assert!(!temp.path().join("downloads").exists()); + } +} + +#[test] +fn manifest_digest_hint_selects_one_of_multiple_filename_members() { + let bytes = b"selected release bytes"; + let selected = digest(bytes); + let script = format!( + r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{ {{ version="2", artifacts={{{{name="app",url="mock://app",file_name="app.apk",sha256="{selected}"}}}} }} }} }}"# + ); + let manifest = format!("{} app.apk\n{selected} app.apk\n", "a".repeat(64)); + let temp = fixture(&script, &manifest, None); + let transport = Rc::new(BytesTransport { + bodies: HashMap::from([("mock://app".into(), bytes.to_vec())]), + requests: RefCell::new(Vec::new()), + }); + + let result = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + transport, + ) + .unwrap(); + + assert_eq!(result.artifacts[0].sha256, selected); +} + +#[test] +fn multiple_filename_members_without_digest_hint_are_ambiguous() { + let script = r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version="2", artifacts={{name="app",url="mock://app",file_name="app.apk"}} }} }"#; + let manifest = format!("{} app.apk\n{} app.apk\n", "a".repeat(64), "b".repeat(64)); + let temp = fixture(script, &manifest, None); + let transport = Rc::new(BytesTransport { + bodies: HashMap::new(), + requests: RefCell::new(Vec::new()), + }); + + let error = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + transport.clone(), + ) + .unwrap_err(); + + assert_eq!(error.code(), "artifact.manifest_ambiguous"); + assert!(transport.requests.borrow().is_empty()); +} + +#[test] +fn final_and_temporary_namespace_cross_collision_fails_preflight() { + let first_digest = digest(b"same bytes"); + let second_digest = first_digest.clone(); + let second_name = "app.apk.download"; + let script = format!( + r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{ {{ version="2", artifacts={{ + {{name="first",url="mock://first",file_name="app.apk"}}, + {{name="second",url="mock://second",file_name="{second_name}"}} +}} }} }} }}"# + ); + let manifest = format!("{first_digest} app.apk\n{second_digest} {second_name}\n"); + let temp = fixture(&script, &manifest, None); + let transport = Rc::new(BytesTransport { + bodies: HashMap::new(), + requests: RefCell::new(Vec::new()), + }); + + let error = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + transport.clone(), + ) + .unwrap_err(); + + assert_eq!(error.code(), "artifact.path_collision"); + assert!(transport.requests.borrow().is_empty()); +} + +#[test] +fn reuses_final_without_transport_and_mismatch_leaves_only_temporary_file() { + let expected = digest(b"good"); + let script = r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version="2", artifacts={{name="app",url="mock://app",file_name="dir/app.apk"}} }} }"#; + let manifest = format!("{expected} dir/app.apk\n"); + let temp = fixture(script, &manifest, None); + let final_path = temp + .path() + .join(format!("downloads/{expected}-dir_app.apk")); + fs::create_dir_all(final_path.parent().unwrap()).unwrap(); + fs::write(&final_path, b"owned marker").unwrap(); + let no_call = Rc::new(BytesTransport { + bodies: HashMap::new(), + requests: RefCell::new(Vec::new()), + }); + let reused = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + no_call.clone(), + ) + .unwrap(); + assert_eq!(reused.artifacts[0].status, "reused"); + assert!(no_call.requests.borrow().is_empty()); + + fs::remove_file(&final_path).unwrap(); + let temporary_path = std::path::PathBuf::from(format!("{}.download", final_path.display())); + fs::write(&temporary_path, b"old partial").unwrap(); + let mismatch = Rc::new(BytesTransport { + bodies: HashMap::from([("mock://app".into(), b"bad".to_vec())]), + requests: RefCell::new(Vec::new()), + }); + let error = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + mismatch, + ) + .unwrap_err(); + assert_eq!(error.code(), "artifact.sha256_mismatch"); + assert!(!final_path.exists()); + assert_eq!(fs::read(temporary_path).unwrap(), b"bad"); +} + +#[test] +fn distinct_artifact_filenames_that_flatten_to_one_staging_path_fail_preflight() { + let bytes = b"same"; + let sha256 = digest(bytes); + let temp = fixture( + r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version="2", artifacts={ + {name="slash",url="https://example.invalid/slash",file_name="dir/app.apk"}, + {name="flat",url="https://example.invalid/flat",file_name="dir_app.apk"}, + } }} }"#, + &format!("{sha256} dir/app.apk\n{sha256} dir_app.apk\n"), + None, + ); + let transport = Rc::new(BytesTransport { + bodies: HashMap::from([ + ("https://example.invalid/slash".to_owned(), bytes.to_vec()), + ("https://example.invalid/flat".to_owned(), bytes.to_vec()), + ]), + requests: RefCell::new(Vec::new()), + }); + + let error = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + transport.clone(), + ) + .unwrap_err(); + + assert_eq!(error.code(), "artifact.path_collision", "{error:?}"); + assert!(transport.requests.borrow().is_empty()); + assert!(!temp.path().join("downloads").exists()); +} + +#[test] +fn cold_github_refresh_uses_provider_digest_via_manifest() { + let bytes = b"github apk"; + let sha = digest(bytes); + let script = r#"#!/bin/upa-lua v1 +local github_android = require("luaclass.github_android_apk") +return github_android.package { + name="Example", android_package="com.example", owner="example", repo="app" +}"#; + let releases = format!( + r#"[{{"tag_name":"v2","name":"v2","body":"","draft":false,"prerelease":false,"assets":[{{"id":1,"name":"app.apk","content_type":"application/vnd.android.package-archive","size":10,"browser_download_url":"mock://github-apk","digest":"sha256:{sha}"}}]}}]"# + ); + let source_sha512 = format!("{:x}", sha2::Sha512::digest(releases.as_bytes())); + let temp = fixture( + script, + &format!("{source_sha512} github-releases.json\n{sha} app.apk\n"), + None, + ); + let github = Rc::new(GithubMock { + body: releases.into_bytes(), + requests: RefCell::new(0), + }); + let transport = Rc::new(BytesTransport { + bodies: HashMap::from([("mock://github-apk".into(), bytes.to_vec())]), + requests: RefCell::new(Vec::new()), + }); + + let result = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + Some(github.clone()), + transport, + ) + .unwrap(); + assert_eq!(result.version, "v2"); + assert_eq!(*github.requests.borrow(), 1); + assert_eq!(fs::read(&result.artifacts[0].path).unwrap(), bytes); +} From 2fc69e535e4535a42f6c1de5169da1a732f364f1 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Thu, 16 Jul 2026 11:43:53 +0800 Subject: [PATCH 69/71] feat(install): run package-declared installers --- Cargo.lock | 12 +- crates/getter-cli/src/lib.rs | 84 +++- crates/getter-cli/src/main.rs | 2 +- crates/getter-cli/tests/app_cli.rs | 147 +++++++ crates/getter-cli/tests/bdd_cli.rs | 73 ++++ .../features/cli/app_inspect_check.feature | 10 + crates/getter-core/src/lib.rs | 52 +++ crates/getter-core/src/lua.rs | 275 +++++++++++- .../src/luaclass/fdroid_android.lua | 5 + .../src/luaclass/github_android_apk.lua | 5 + crates/getter-core/src/luaclass/installer.lua | 11 + crates/getter-core/src/update.rs | 2 + crates/getter-operations/Cargo.toml | 1 + crates/getter-operations/src/app.rs | 392 ++++++++++++++++- .../getter-operations/src/github_autogen.rs | 4 + crates/getter-operations/src/runtime.rs | 1 + crates/getter-operations/tests/installer.rs | 408 ++++++++++++++++++ crates/getter-providers/src/lib.rs | 3 + 18 files changed, 1466 insertions(+), 21 deletions(-) create mode 100644 crates/getter-core/src/luaclass/installer.lua create mode 100644 crates/getter-operations/tests/installer.rs diff --git a/Cargo.lock b/Cargo.lock index b0add8a..fe86385 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -657,6 +657,7 @@ dependencies = [ "tempfile", "thiserror 1.0.69", "ureq", + "which 8.0.3", ] [[package]] @@ -961,7 +962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3a8e7962a5368d5f264d045a5a255e90f9aa3fc1941ae15a8d2940d42cac671" dependencies = [ "cc", - "which", + "which 7.0.3", ] [[package]] @@ -1917,6 +1918,15 @@ dependencies = [ "winsafe", ] +[[package]] +name = "which" +version = "8.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c789537cf2f7f55be8e6192f92e464174ee55f91af622777f7f1ceb0dbccd03e" +dependencies = [ + "libc", +] + [[package]] name = "winapi-util" version = "0.1.11" diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 470a360..8d949c7 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -39,6 +39,7 @@ use getter_storage::{ use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use std::fs; +use std::io::Write; use std::path::{Path, PathBuf}; const MAIN_DB_FILE: &str = "main.db"; @@ -69,6 +70,9 @@ pub enum CliCommand { AppDownload { package_id: PackageId, }, + AppInstall { + package_id: PackageId, + }, HubList, RepoList, RepoAdd { @@ -414,7 +418,20 @@ where I: IntoIterator, S: Into, { - match run_inner(args) { + let mut captured = Vec::new(); + let mut output = run_with_command_sink(args, &mut captured); + if !captured.is_empty() { + output.stderr = String::from_utf8_lossy(&captured).into_owned(); + } + output +} + +pub fn run_with_command_sink(args: I, sink: &mut dyn Write) -> CliOutput +where + I: IntoIterator, + S: Into, +{ + match run_inner(args, sink) { Ok((command_name, data)) => CliOutput { exit_code: ExitCode::Success, stdout: success_envelope(&command_name, data), @@ -435,18 +452,47 @@ where } } -fn run_inner(args: I) -> Result<(String, Value), (String, CliError)> +fn run_inner(args: I, sink: &mut dyn Write) -> Result<(String, Value), (String, CliError)> where I: IntoIterator, S: Into, { let invocation = parse_args(args).map_err(|error| ("unknown".to_owned(), error))?; let command_name = invocation.command.name().to_owned(); - execute(invocation) + execute(invocation, sink) .map(|data| (command_name.clone(), data)) .map_err(|error| (command_name, error)) } +fn format_resolved_command(command: &Value) -> String { + fn quote(value: &str) -> String { + if !value.is_empty() + && value + .bytes() + .all(|byte| byte.is_ascii_alphanumeric() || b"/._-".contains(&byte)) + { + value.to_owned() + } else { + format!("'{}'", value.replace('\'', "'\\''")) + } + } + let executable = command + .get("executable") + .and_then(Value::as_str) + .unwrap_or(""); + let mut words = vec![quote(executable)]; + words.extend( + command + .get("args") + .and_then(Value::as_array) + .into_iter() + .flatten() + .filter_map(Value::as_str) + .map(quote), + ); + format!("installer command: {}\n", words.join(" ")) +} + pub fn parse_args(args: I) -> Result where I: IntoIterator, @@ -481,7 +527,10 @@ where [domain, command] if domain == "app" && command == "list" => CliCommand::AppList, [domain, command, package_id] if domain == "app" - && (command == "show" || command == "check" || command == "download") => + && (command == "show" + || command == "check" + || command == "download" + || command == "install") => { let package_id = parse_package_id(package_id)?; match command.as_str() { @@ -493,7 +542,8 @@ where package_id, inventory: None, }, - _ => CliCommand::AppDownload { package_id }, + "download" => CliCommand::AppDownload { package_id }, + _ => CliCommand::AppInstall { package_id }, } } [domain, command, package_id, flag, inventory] @@ -790,7 +840,7 @@ fn map_app_error(error: getter_operations::app::AppOperationError) -> CliError { } } -fn execute(invocation: CliInvocation) -> Result { +fn execute(invocation: CliInvocation, command_sink: &mut dyn Write) -> Result { match invocation.command { CliCommand::Startup { inventory } => { let inventory = match inventory { @@ -838,6 +888,27 @@ fn execute(invocation: CliInvocation) -> Result { .map_err(map_app_error)?; serde_json::to_value(result).map_err(|error| CliError::Storage(error.to_string())) } + CliCommand::AppInstall { package_id } => { + struct StreamingObserver<'a>(std::cell::RefCell<&'a mut dyn Write>); + impl getter_operations::app::CommandObserver for StreamingObserver<'_> { + fn before_execute( + &self, + command: &getter_operations::app::ResolvedInstallerCommand, + ) -> std::io::Result<()> { + let value = serde_json::to_value(command).expect("command serializes"); + let mut sink = self.0.borrow_mut(); + sink.write_all(format_resolved_command(&value).as_bytes())?; + sink.flush() + } + } + let result = getter_operations::app::install_app_with_observer( + &invocation.data_dir, + &package_id, + &StreamingObserver(std::cell::RefCell::new(command_sink)), + ) + .map_err(map_app_error)?; + serde_json::to_value(result).map_err(|error| CliError::Storage(error.to_string())) + } CliCommand::AppCheck { package_id, inventory, @@ -2411,6 +2482,7 @@ impl CliCommand { Self::AppShow { .. } => "app show", Self::AppCheck { .. } => "app check", Self::AppDownload { .. } => "app download", + Self::AppInstall { .. } => "app install", Self::HubList => "hub list", Self::RepoList => "repo list", Self::RepoAdd { .. } => "repo add", diff --git a/crates/getter-cli/src/main.rs b/crates/getter-cli/src/main.rs index dc49483..53b4fbe 100644 --- a/crates/getter-cli/src/main.rs +++ b/crates/getter-cli/src/main.rs @@ -1,5 +1,5 @@ fn main() { - let output = getter_cli::run(std::env::args()); + let output = getter_cli::run_with_command_sink(std::env::args(), &mut std::io::stderr()); if !output.stdout.is_empty() { print!("{}", output.stdout); } diff --git a/crates/getter-cli/tests/app_cli.rs b/crates/getter-cli/tests/app_cli.rs index aa354d1..9991497 100644 --- a/crates/getter-cli/tests/app_cli.rs +++ b/crates/getter-cli/tests/app_cli.rs @@ -6,6 +6,34 @@ use serde_json::{json, Value}; use std::fs; use std::io::{Read, Write}; use std::net::TcpListener; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn fake_installer(temp: &Path) -> PathBuf { + let source = temp.join("fake_installer.rs"); + let executable = temp.join(format!("fake installer{}", std::env::consts::EXE_SUFFIX)); + fs::write( + &source, + r#"fn main() { + let arguments: Vec<_> = std::env::args().skip(1).collect(); + for argument in &arguments { + println!("{argument}"); + } + if arguments.iter().any(|argument| argument == "--fail") { + std::process::exit(23); + } +}"#, + ) + .unwrap(); + let status = Command::new("rustc") + .arg(&source) + .arg("-o") + .arg(&executable) + .status() + .expect("rustc must be available while running Rust tests"); + assert!(status.success(), "compile platform-native fake installer"); + executable +} fn fixture(latest: &str) -> tempfile::TempDir { let temp = tempfile::tempdir().unwrap(); @@ -104,6 +132,125 @@ return package_version {{ updates = {{ {{ version="2", artifacts={{{{name="app.a ); } +#[test] +fn app_download_ignores_missing_installer() { + let temp = fixture("2"); + let package = temp.path().join("repo/official/android/com.example"); + fs::write(package.join("Manifest"), "").unwrap(); + fs::write( + package.join("1.lua"), + r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version="2", install =nil }} }"#, + ) + .unwrap(); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "download", + "android/com.example", + ]); + assert_eq!(output.exit_code, ExitCode::Success); +} + +#[test] +fn app_install_rejects_product_command_controls() { + let temp = fixture("2"); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "install", + "android/com.example", + "--executable", + "pm", + ]); + assert_eq!(output.exit_code, ExitCode::Usage); +} + +#[test] +fn app_install_executes_declared_command_and_reports_resolved_argv() { + let bytes = b"install artifact"; + let digest = "462c010ad28c00ae578bcfa52d985c8cb00a89accdd51ac9f05fec5872a7ae25"; + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let server = std::thread::spawn(move || { + let (mut stream, _) = listener.accept().unwrap(); + let mut request = [0; 1024]; + let _ = stream.read(&mut request).unwrap(); + write!( + stream, + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", + bytes.len() + ) + .unwrap(); + stream.write_all(bytes).unwrap(); + }); + let temp = fixture("2"); + let executable = fake_installer(temp.path()); + let package = temp.path().join("repo/official/android/com.example"); + fs::write(package.join("Manifest"), format!("{digest} app.apk\n")).unwrap(); + fs::write(package.join("1.lua"), format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates={{{{version="2",artifacts={{{{name="app",file_name="app.apk",url="http://{address}/app.apk"}}}},install ={{executable={:?},args={{"install",{{artifact="app"}},"$HOME;literal"}}}}}}}} }}"#, executable.to_string_lossy())).unwrap(); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "install", + "android/com.example", + ]); + server.join().unwrap(); + assert_eq!(output.exit_code, ExitCode::Success, "{}", output.stdout); + let json: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(json["data"]["command"]["args"][0], "install"); + assert_eq!(json["data"]["command"]["args"][2], "$HOME;literal"); + assert!(output.stderr.contains("installer command:")); + assert!(output.stderr.contains("'")); +} + +#[test] +fn app_install_captures_streamed_command_when_child_fails() { + let bytes = b"install artifact"; + let digest = "462c010ad28c00ae578bcfa52d985c8cb00a89accdd51ac9f05fec5872a7ae25"; + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let server = std::thread::spawn(move || { + let (mut stream, _) = listener.accept().unwrap(); + let mut request = [0; 1024]; + let _ = stream.read(&mut request).unwrap(); + write!( + stream, + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", + bytes.len() + ) + .unwrap(); + stream.write_all(bytes).unwrap(); + }); + let temp = fixture("2"); + let executable = fake_installer(temp.path()); + let package = temp.path().join("repo/official/android/com.example"); + fs::write(package.join("Manifest"), format!("{digest} app.apk\n")).unwrap(); + fs::write(package.join("1.lua"), format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates={{{{version="2",artifacts={{{{name="app",file_name="app.apk",url="http://{address}/app.apk"}}}},install ={{executable={:?},args={{"--fail",{{artifact="app"}}}}}}}}}} }}"#, executable.to_string_lossy())).unwrap(); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "install", + "android/com.example", + ]); + server.join().unwrap(); + assert_eq!(output.exit_code, ExitCode::GenericFailure); + let body: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(body["error"]["code"], "installer.command_failed"); + assert!(output.stderr.contains("--fail")); + assert!(output.stderr.contains("app.apk")); +} + #[test] fn app_download_rejects_transport_controls_and_maps_manifest_errors_stably() { let temp = fixture("2"); diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 2e480bc..6812b32 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -778,6 +778,67 @@ return package_version {{ installed = {{ {{ kind = "android_package", package_na .expect("write local artifact package"); } +#[given("the package installer uses a fake executable")] +fn fake_package_installer(world: &mut CliWorld) { + deterministic_local_artifact(world); + let temp = world.temp.as_ref().unwrap().path(); + let source_path = temp.join("fake_installer.rs"); + let executable = temp.join(format!("fake-installer{}", std::env::consts::EXE_SUFFIX)); + fs::write( + &source_path, + r#"fn main() { + for argument in std::env::args().skip(1) { + println!("{argument}"); + } +}"#, + ) + .unwrap(); + let status = Command::new("rustc") + .arg(&source_path) + .arg("-o") + .arg(&executable) + .status() + .expect("rustc must be available while running Rust tests"); + assert!(status.success(), "compile platform-native fake installer"); + let package_dir = world.fixture_repo_path.as_ref().unwrap().join( + world + .fixture_package_id + .as_ref() + .unwrap() + .replace('/', std::path::MAIN_SEPARATOR_STR), + ); + let source = fs::read_to_string(package_dir.join("1.20.0.lua")).unwrap(); + let source = source.replacen("artifacts = { { name", &format!("install = {{ executable = {:?}, args = {{ \"install\", {{ artifact = \"app.apk\" }}, \"$HOME;literal\" }} }}, artifacts = {{ {{ name", executable.to_string_lossy()), 1); + fs::write(package_dir.join("1.20.0.lua"), source).unwrap(); +} + +#[given("the package installer names a missing executable")] +fn missing_package_installer(world: &mut CliWorld) { + deterministic_local_artifact(world); + let package_dir = world.fixture_repo_path.as_ref().unwrap().join( + world + .fixture_package_id + .as_ref() + .unwrap() + .replace('/', std::path::MAIN_SEPARATOR_STR), + ); + let source = fs::read_to_string(package_dir.join("1.20.0.lua")).unwrap(); + let source = source.replacen( + "artifacts = { { name", + "install = { executable = \"getter-definitely-missing-installer\", args = {} }, artifacts = { { name", + 1, + ); + fs::write(package_dir.join("1.20.0.lua"), source).unwrap(); +} + +#[when(expr = "I run getter app install for {string}")] +fn run_app_install(world: &mut CliWorld, package_id: String) { + run_app_command(world, "install", package_id, false); + if let Some(server) = world.local_artifact_server.take() { + server.join().unwrap(); + } +} + #[when(expr = "I run getter app download for {string}")] fn run_app_download(world: &mut CliWorld, package_id: String) { run_app_command(world, "download", package_id, false); @@ -859,6 +920,18 @@ fn download_then_reuse(world: &mut CliWorld) { ); } +#[then("the install succeeds with exact literal and artifact argv")] +fn install_exact_argv(world: &mut CliWorld) { + let json = current_json(world); + assert_eq!(json["data"]["status"], "succeeded"); + assert_eq!(json["data"]["command"]["args"][0], "install"); + assert!(json["data"]["command"]["args"][1] + .as_str() + .unwrap() + .starts_with('/')); + assert_eq!(json["data"]["command"]["args"][2], "$HOME;literal"); +} + #[then(expr = "the command fails with stable error {string}")] fn stable_error(world: &mut CliWorld, code: String) { let json = current_json(world); diff --git a/crates/getter-cli/tests/features/cli/app_inspect_check.feature b/crates/getter-cli/tests/features/cli/app_inspect_check.feature index 841ecf3..6d5c381 100644 --- a/crates/getter-cli/tests/features/cli/app_inspect_check.feature +++ b/crates/getter-cli/tests/features/cli/app_inspect_check.feature @@ -30,6 +30,16 @@ Feature: Inspect and actively check a tracked app When I run getter app download twice for "android/app/fdroid" Then the first download is downloaded and the second is reused from the same path + Scenario: Install executes exact declared argv + Given the package installer uses a fake executable + When I run getter app install for "android/app/fdroid" + Then the install succeeds with exact literal and artifact argv + + Scenario: Install reports a missing package-declared command + Given the package installer names a missing executable + When I run getter app install for "android/app/fdroid" + Then the command fails with stable error "installer.command_not_found" + Scenario: Download rejects transport controls When I run getter app download for "android/app/fdroid" with a URL control Then the command fails with stable error "cli.usage" diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index 0360c2b..ee7d976 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -277,6 +277,56 @@ pub struct PackagePermissions { pub free_network: bool, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct InstallerCommand { + pub executable: String, + #[serde(default)] + pub args: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum InstallerArg { + Literal(String), + Artifact(InstallerArtifactReference), +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct InstallerArtifactReference { + pub artifact: String, +} + +/// A getter-owned, deferred installer declaration. Package evaluation preserves +/// this JSON value; install operations validate it as an [`InstallerCommand`]. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(transparent)] +pub struct InstallerDeclaration(serde_json::Value); + +impl InstallerDeclaration { + pub fn new(value: serde_json::Value) -> Self { + Self(value) + } + + pub fn as_json(&self) -> &serde_json::Value { + &self.0 + } + + pub fn parse(&self) -> Result { + let mut value = self.0.clone(); + if let Some(args) = value + .as_object_mut() + .and_then(|object| object.get_mut("args")) + { + if args.as_object().is_some_and(serde_json::Map::is_empty) { + *args = serde_json::Value::Array(Vec::new()); + } + } + serde_json::from_value(value) + } +} + /// Candidate version/update discovered by provider/package logic. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct UpdateCandidate { @@ -291,6 +341,8 @@ pub struct UpdateCandidate { pub source: Option, #[serde(default)] pub artifacts: Vec, + #[serde(default)] + pub install: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index fa6044a..bb54f43 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -5,8 +5,8 @@ use crate::repository::{ PackageVersionScript, RepositoryLoadError, LUA_API_SHEBANG_V1, REPOSITORY_LUACLASS_DIR, }; use crate::{ - InstalledTarget, PackageId, PackagePermissions, RepositoryId, ResolvedPackage, UpdateArtifact, - UpdateCandidate, + InstalledTarget, InstallerDeclaration, PackageId, PackagePermissions, RepositoryId, + ResolvedPackage, UpdateArtifact, UpdateCandidate, }; use mlua::{Function, Lua, Table, Value}; use serde_json::{Map, Number, Value as JsonValue}; @@ -16,6 +16,7 @@ use std::path::{Path, PathBuf}; const BUILTIN_LUACLASS_MODULES: &[(&str, &str)] = &[ ("android", include_str!("luaclass/android.lua")), + ("installer", include_str!("luaclass/installer.lua")), ( "fdroid_android", include_str!("luaclass/fdroid_android.lua"), @@ -715,6 +716,36 @@ fn package_from_version_json( }) } +fn reject_unknown_keys( + path: &Path, + object: &Map, + kind: &str, + allowed: &[&str], +) -> Result<(), LuaPackageError> { + if let Some(key) = object.keys().find(|key| !allowed.contains(&key.as_str())) { + return Err(LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("unknown {kind} field '{key}'"), + }); + } + Ok(()) +} + +fn required_nonempty_string<'a>( + path: &Path, + object: &'a Map, + field: &str, +) -> Result<&'a str, LuaPackageError> { + let value = required_string(path, object, field)?; + if value.is_empty() { + return Err(LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("string field '{field}' must not be empty"), + }); + } + Ok(value) +} + fn required_string<'a>( path: &Path, object: &'a Map, @@ -785,7 +816,25 @@ fn parse_update_candidates( path: path.to_path_buf(), message: "updates entries must be objects".to_owned(), })?; + reject_unknown_keys( + path, + object, + "update candidate", + &[ + "version", + "version_code", + "changelog", + "channel", + "source", + "artifacts", + "install", + ], + )?; let artifacts = parse_update_artifacts(path, object.get("artifacts"))?; + let install = object + .get("install") + .cloned() + .map(InstallerDeclaration::new); Ok(UpdateCandidate { version: required_string(path, object, "version")?.to_owned(), version_code: optional_i64(path, object, "version_code")?, @@ -793,6 +842,7 @@ fn parse_update_candidates( channel: optional_string(object, "channel"), source: optional_string(object, "source"), artifacts, + install, }) }) .collect() @@ -809,15 +859,22 @@ fn parse_update_artifacts( path: path.to_path_buf(), message: "field 'artifacts' must be an array".to_owned(), })?; - array + let artifacts = array .iter() .map(|item| { let object = item.as_object().ok_or_else(|| LuaPackageError::Schema { path: path.to_path_buf(), message: "artifact entries must be objects".to_owned(), })?; + reject_unknown_keys( + path, + object, + "artifact", + &["name", "url", "content_type", "file_name", "sha256", "size"], + )?; + let name = required_nonempty_string(path, object, "name")?.to_owned(); Ok(UpdateArtifact { - name: required_string(path, object, "name")?.to_owned(), + name, url: required_string(path, object, "url")?.to_owned(), content_type: optional_string(object, "content_type"), file_name: optional_string(object, "file_name"), @@ -825,7 +882,17 @@ fn parse_update_artifacts( size: optional_u64(path, object, "size")?, }) }) - .collect() + .collect::, _>>()?; + let mut names = std::collections::HashSet::new(); + for artifact in &artifacts { + if !names.insert(&artifact.name) { + return Err(LuaPackageError::Schema { + path: path.to_path_buf(), + message: format!("duplicate artifact name '{}'", artifact.name), + }); + } + } + Ok(artifacts) } fn optional_string(object: &Map, field: &str) -> Option { @@ -949,6 +1016,80 @@ mod tests { fs::write(package_dir.join("9999.lua"), script_source).unwrap(); } + #[test] + fn preserves_chosen_installer_table_declaration_without_eager_validation() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +local installer = require("luaclass.installer") +return package_version { + updates = {{ + version = "2", + artifacts = {{ name = "base", url = "https://example.invalid/base.apk" }}, + install = installer.command { + executable = "fake-installer", + args = { "install", installer.artifact("base"), "literal $HOME;not-shell" }, + }, + }}, +}"#, + ); + + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + assert_eq!( + package.updates[0].install.as_ref().unwrap().as_json(), + &serde_json::json!({ + "executable": "fake-installer", + "args": ["install", {"artifact": "base"}, "literal $HOME;not-shell"] + }) + ); + } + + #[test] + fn rejects_unknown_candidate_and_artifact_keys_and_invalid_artifact_names() { + for (case, source) in [ + ("candidate", r#"updates = {{ version="2", surprise=true }}"#), + ( + "artifact", + r#"updates = {{ version="2", artifacts={{name="a",url="u",surprise=true}} }}"#, + ), + ( + "empty artifact", + r#"updates = {{ version="2", artifacts={{name="",url="u"}} }}"#, + ), + ( + "duplicate artifact", + r#"updates = {{ version="2", artifacts={{name="a",url="u"},{name="a",url="v"}} }}"#, + ), + ] { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + &format!("#!/bin/upa-lua v1\nreturn package_version {{ {source} }}"), + ); + let error = evaluate_single_package_directory(temp.path(), "official").unwrap_err(); + assert!( + matches!(error, LuaPackageError::Schema { .. }), + "{case}: {error}" + ); + } + } + + #[test] + fn rejects_legacy_installer_candidate_field() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version = "2", installer = {} }} }"#, + ); + let error = evaluate_single_package_directory(temp.path(), "official").unwrap_err(); + assert!(matches!(error, LuaPackageError::Schema { .. })); + assert!(error + .to_string() + .contains("unknown update candidate field 'installer'")); + } + #[test] fn evaluates_json_like_lua_package_table() { let temp = tempfile::tempdir().unwrap(); @@ -1729,6 +1870,130 @@ return package_version { name = first .. "|" .. second } ); } + #[test] + fn fdroid_luaclass_propagates_declared_install_to_every_candidate_without_defaulting() { + for declared in [false, true] { + let temp = tempfile::tempdir().unwrap(); + let install = if declared { + r#", install = { executable = "pm", args = { "install" } }"# + } else { + "" + }; + write_simple_android_package( + temp.path(), + &format!( + r#"#!/bin/upa-lua v1 +local fdroid = require("luaclass.fdroid_android") +return fdroid.package {{ package_name = "com.example"{install} }} +"#, + ), + ); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("official").unwrap(), + package_dir, + &metadata, + script, + |lua| { + let candidates = lua.create_table()?; + for index in 1..=2 { + let candidate = lua.create_table()?; + candidate.set("version", index.to_string())?; + candidates.raw_set(index, candidate)?; + } + let fdroid = lua.create_table()?; + fdroid.set( + "update_candidates", + lua.create_function(move |lua, _: Table| { + let result = lua.create_table()?; + result.set("candidates", candidates.clone())?; + Ok(result) + })?, + )?; + let provider = lua.create_table()?; + provider.set("fdroid", fdroid)?; + let getter = lua.create_table()?; + getter.set("provider", provider)?; + lua.globals().set("getter", getter) + }, + ) + .unwrap(); + assert_eq!(package.updates.len(), 2); + assert!(package.updates.iter().all(|candidate| { + candidate + .install + .as_ref() + .map(|install| install.as_json().clone()) + == declared.then(|| serde_json::json!({"executable":"pm","args":["install"]})) + })); + } + } + + #[test] + fn github_luaclass_propagates_declared_install_to_every_candidate_without_defaulting() { + for declared in [false, true] { + let temp = tempfile::tempdir().unwrap(); + let install = if declared { + r#", install = { executable = "pm", args = { "install" } }"# + } else { + "" + }; + write_simple_android_package( + temp.path(), + &format!( + r#"#!/bin/upa-lua v1 +local github = require("luaclass.github_android_apk") +return github.package {{ owner = "owner", repo = "repo"{install} }} +"#, + ), + ); + let layout = RepositoryPackageDirectoryLayout::load(temp.path()).unwrap(); + let package_dir = &layout.packages[0]; + let metadata = layout.package_metadata(package_dir).unwrap(); + let script = layout.unambiguous_version_script(package_dir).unwrap(); + let package = evaluate_package_directory_script_with_host_bindings( + &RepositoryId::new("official").unwrap(), + package_dir, + &metadata, + script, + |lua| { + let candidates = lua.create_table()?; + for index in 1..=2 { + let candidate = lua.create_table()?; + candidate.set("version", index.to_string())?; + candidates.raw_set(index, candidate)?; + } + let github = lua.create_table()?; + github.set( + "release_candidates", + lua.create_function(move |lua, _: Table| { + let result = lua.create_table()?; + result.set("candidates", candidates.clone())?; + Ok(result) + })?, + )?; + let provider = lua.create_table()?; + provider.set("github", github)?; + let getter = lua.create_table()?; + getter.set("provider", provider)?; + lua.globals().set("getter", getter) + }, + ) + .unwrap(); + assert_eq!(package.updates.len(), 2); + assert!(package.updates.iter().all(|candidate| { + candidate + .install + .as_ref() + .map(|install| install.as_json().clone()) + == declared.then(|| serde_json::json!({"executable":"pm","args":["install"]})) + })); + } + } + #[test] fn package_directory_http_get_rejects_unsupported_options() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-core/src/luaclass/fdroid_android.lua b/crates/getter-core/src/luaclass/fdroid_android.lua index 7029ffe..77b4994 100644 --- a/crates/getter-core/src/luaclass/fdroid_android.lua +++ b/crates/getter-core/src/luaclass/fdroid_android.lua @@ -41,6 +41,11 @@ function fdroid.package(spec) endpoint_id = optional_string(spec, "endpoint_id"), } local result = fdroid_update_candidates()(host_spec) + if spec.install ~= nil then + for _, candidate in ipairs(result.candidates) do + candidate.install = spec.install + end + end return package_version { source_priority = { "fdroid" }, updates = result.candidates, diff --git a/crates/getter-core/src/luaclass/github_android_apk.lua b/crates/getter-core/src/luaclass/github_android_apk.lua index 753f7f0..799204d 100644 --- a/crates/getter-core/src/luaclass/github_android_apk.lua +++ b/crates/getter-core/src/luaclass/github_android_apk.lua @@ -66,6 +66,11 @@ function github_android.package(spec) endpoint_id = optional_string(spec, "endpoint_id"), } local result = github_release_candidates()(host_spec) + if spec.install ~= nil then + for _, candidate in ipairs(result.candidates) do + candidate.install = spec.install + end + end local package = { name = spec.name, source_priority = { "github" }, diff --git a/crates/getter-core/src/luaclass/installer.lua b/crates/getter-core/src/luaclass/installer.lua new file mode 100644 index 0000000..7f3aff9 --- /dev/null +++ b/crates/getter-core/src/luaclass/installer.lua @@ -0,0 +1,11 @@ +local installer = {} + +function installer.artifact(name) + return { artifact = name } +end + +function installer.command(spec) + return spec +end + +return installer diff --git a/crates/getter-core/src/update.rs b/crates/getter-core/src/update.rs index 4315365..d07ee50 100644 --- a/crates/getter-core/src/update.rs +++ b/crates/getter-core/src/update.rs @@ -509,6 +509,7 @@ mod tests { channel: None, source: None, artifacts: Vec::new(), + install: None, }], UpdateSelectionPolicy::default(), ) @@ -553,6 +554,7 @@ mod tests { sha256: None, size: None, }], + install: None, } } } diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index 6ce6120..0ec16b8 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -19,6 +19,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" sha2 = "0.10" thiserror = "1" +which = "=8.0.3" ureq = { version = "3.3.0", default-features = false, features = ["gzip", "rustls"] } [dev-dependencies] diff --git a/crates/getter-operations/src/app.rs b/crates/getter-operations/src/app.rs index 2550d19..0a18e60 100644 --- a/crates/getter-operations/src/app.rs +++ b/crates/getter-operations/src/app.rs @@ -15,7 +15,7 @@ use getter_core::runtime::IssuedAction; use getter_core::update::compare_versions; use getter_core::PackageId; #[cfg(feature = "lua")] -use getter_core::UpdateArtifact; +use getter_core::{InstallerArg, InstallerCommand, UpdateArtifact}; use getter_storage::{MainDb, StorageError, StoredTrackedPackage}; use serde::{Deserialize, Serialize}; #[cfg(feature = "lua")] @@ -75,6 +75,27 @@ pub enum AppOperationError { #[cfg(feature = "lua")] #[error("artifact download failed: {0}")] ArtifactTransport(#[from] crate::download::RuntimeDownloadTransportError), + #[cfg(feature = "lua")] + #[error("selected candidate has no installer command")] + InstallerMissing, + #[cfg(feature = "lua")] + #[error("installer declaration schema is invalid: {0}")] + InstallerSchema(String), + #[cfg(feature = "lua")] + #[error("installer artifact reference '{0}' is unknown")] + InstallerArtifactUnknown(String), + #[cfg(feature = "lua")] + #[error("installer artifact name '{0}' is duplicated")] + InstallerArtifactDuplicate(String), + #[cfg(feature = "lua")] + #[error("installer executable '{0}' was not found")] + InstallerCommandNotFound(String), + #[cfg(feature = "lua")] + #[error("installer command could not be spawned: {0}")] + InstallerCommandSpawnFailed(std::io::Error), + #[cfg(feature = "lua")] + #[error("installer command failed with exit code {0}")] + InstallerCommandFailed(i32), } impl AppOperationError { @@ -114,6 +135,20 @@ impl AppOperationError { Self::ArtifactIo(_) => "artifact.io_error", #[cfg(feature = "lua")] Self::ArtifactTransport(_) => "artifact.transport_error", + #[cfg(feature = "lua")] + Self::InstallerMissing => "installer.missing", + #[cfg(feature = "lua")] + Self::InstallerSchema(_) => "installer.schema_invalid", + #[cfg(feature = "lua")] + Self::InstallerArtifactUnknown(_) => "installer.artifact_unknown", + #[cfg(feature = "lua")] + Self::InstallerArtifactDuplicate(_) => "installer.artifact_duplicate", + #[cfg(feature = "lua")] + Self::InstallerCommandNotFound(_) => "installer.command_not_found", + #[cfg(feature = "lua")] + Self::InstallerCommandSpawnFailed(_) => "installer.command_spawn_failed", + #[cfg(feature = "lua")] + Self::InstallerCommandFailed(_) => "installer.command_failed", } } @@ -146,6 +181,20 @@ impl AppOperationError { Self::ArtifactIo(_) => "Getter artifact staging failed", #[cfg(feature = "lua")] Self::ArtifactTransport(_) => "Getter artifact download failed", + #[cfg(feature = "lua")] + Self::InstallerMissing => "Getter package installer is missing", + #[cfg(feature = "lua")] + Self::InstallerSchema(_) => "Getter package installer schema is invalid", + #[cfg(feature = "lua")] + Self::InstallerArtifactUnknown(_) => "Getter installer references an unknown artifact", + #[cfg(feature = "lua")] + Self::InstallerArtifactDuplicate(_) => "Getter installer artifact name is duplicated", + #[cfg(feature = "lua")] + Self::InstallerCommandNotFound(_) => "Getter installer command was not found", + #[cfg(feature = "lua")] + Self::InstallerCommandSpawnFailed(_) => "Getter installer command could not be started", + #[cfg(feature = "lua")] + Self::InstallerCommandFailed(_) => "Getter installer command failed", } } @@ -168,7 +217,14 @@ impl AppOperationError { | Self::ArtifactPathCollision { .. } | Self::Sha256Mismatch { .. } | Self::ArtifactIo(_) - | Self::ArtifactTransport(_) => self.to_string(), + | Self::ArtifactTransport(_) + | Self::InstallerMissing + | Self::InstallerSchema(_) + | Self::InstallerArtifactUnknown(_) + | Self::InstallerArtifactDuplicate(_) + | Self::InstallerCommandNotFound(_) + | Self::InstallerCommandSpawnFailed(_) + | Self::InstallerCommandFailed(_) => self.to_string(), } } } @@ -189,6 +245,124 @@ pub struct AppDownloadResult { pub artifacts: Vec, } +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct ResolvedInstallerCommand { + pub executable: PathBuf, + pub args: Vec, +} + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RunnerOutput { + pub status: i32, + pub stdout: Vec, + pub stderr: Vec, +} + +#[cfg(feature = "lua")] +pub trait CommandResolver { + fn resolve(&self, executable: &str) -> Option; +} +#[cfg(feature = "lua")] +pub trait CommandRunner { + fn run(&self, command: &ResolvedInstallerCommand) -> std::io::Result; +} + +#[cfg(feature = "lua")] +pub trait CommandObserver { + fn before_execute(&self, command: &ResolvedInstallerCommand) -> std::io::Result<()>; +} + +#[cfg(feature = "lua")] +pub struct NoopCommandObserver; +#[cfg(feature = "lua")] +impl CommandObserver for NoopCommandObserver { + fn before_execute(&self, _: &ResolvedInstallerCommand) -> std::io::Result<()> { + Ok(()) + } +} + +#[cfg(feature = "lua")] +pub struct PathCommandResolver; +#[cfg(feature = "lua")] +impl CommandResolver for PathCommandResolver { + fn resolve(&self, executable: &str) -> Option { + which::which(executable).ok() + } +} + +#[cfg(feature = "lua")] +pub struct ProcessCommandRunner; +#[cfg(feature = "lua")] +impl CommandRunner for ProcessCommandRunner { + fn run(&self, command: &ResolvedInstallerCommand) -> std::io::Result { + let mut child = std::process::Command::new(&command.executable) + .args(&command.args) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .spawn()?; + let stdout = child.stdout.take().ok_or_else(|| { + std::io::Error::other("installer runner failed to capture child stdout") + })?; + let stderr = child.stderr.take().ok_or_else(|| { + std::io::Error::other("installer runner failed to capture child stderr") + })?; + + let stdout_reader = std::thread::spawn(move || drain_bounded(stdout)); + let stderr_reader = std::thread::spawn(move || drain_bounded(stderr)); + let status = child.wait(); + let stdout = join_output_reader(stdout_reader, "stdout")?; + let stderr = join_output_reader(stderr_reader, "stderr")?; + let status = status?; + + Ok(RunnerOutput { + status: status.code().unwrap_or(-1), + stdout, + stderr, + }) + } +} + +#[cfg(feature = "lua")] +fn drain_bounded(mut stream: impl std::io::Read) -> std::io::Result> { + const LIMIT: usize = 64 * 1024; + let mut captured = Vec::with_capacity(LIMIT); + let mut buffer = [0_u8; 8 * 1024]; + loop { + let read = stream.read(&mut buffer)?; + if read == 0 { + return Ok(captured); + } + let remaining = LIMIT.saturating_sub(captured.len()); + captured.extend_from_slice(&buffer[..read.min(remaining)]); + } +} + +#[cfg(feature = "lua")] +fn join_output_reader( + reader: std::thread::JoinHandle>>, + stream: &str, +) -> std::io::Result> { + reader + .join() + .map_err(|_| std::io::Error::other(format!("installer runner {stream} reader panicked")))? +} + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AppInstallResult { + pub package_id: PackageId, + pub repository_id: String, + pub version: String, + pub artifacts: Vec, + pub command: ResolvedInstallerCommand, + pub status: String, + pub exit_code: i32, + pub stdout: String, + pub stderr: String, +} + #[cfg(feature = "lua")] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct StagedArtifact { @@ -262,6 +436,22 @@ pub fn download_app_with_transports( github_release_transport: Option>, download_transport: Rc, ) -> Result { + prepare_app_with_transports( + data_dir, + package_id, + github_release_transport, + download_transport, + ) + .map(|(result, _)| result) +} + +#[cfg(feature = "lua")] +fn prepare_app_with_transports( + data_dir: &Path, + package_id: &PackageId, + github_release_transport: Option>, + download_transport: Rc, +) -> Result<(AppDownloadResult, getter_core::UpdateCandidate), AppOperationError> { startup::bootstrap_data_dir(data_dir)?; let db = MainDb::open(data_dir.join("main.db"))?; let tracked = tracked_package(&db, package_id)?; @@ -295,7 +485,7 @@ pub fn download_app_with_transports( })?; let manifest = PackageManifest::parse(&manifest_content).map_err(AppOperationError::ManifestInvalid)?; - let downloads = data_dir.join("downloads"); + let downloads = absolute_staging_root(data_dir)?; let prepared = candidate .artifacts .iter() @@ -328,14 +518,200 @@ pub fn download_app_with_transports( .into_iter() .map(|artifact| stage_artifact(artifact, download_transport.as_ref())) .collect::, _>>()?; - Ok(AppDownloadResult { - package_id: package_id.clone(), - repository_id: evaluation.package.repository.to_string(), - version: candidate.version, - artifacts, + Ok(( + AppDownloadResult { + package_id: package_id.clone(), + repository_id: evaluation.package.repository.to_string(), + version: candidate.version.clone(), + artifacts, + }, + candidate, + )) +} + +#[cfg(feature = "lua")] +pub fn install_app( + data_dir: &Path, + package_id: &PackageId, +) -> Result { + install_app_with_observer(data_dir, package_id, &NoopCommandObserver) +} + +#[cfg(feature = "lua")] +pub fn install_app_with_observer( + data_dir: &Path, + package_id: &PackageId, + observer: &dyn CommandObserver, +) -> Result { + install_app_with_dependencies_and_observer( + data_dir, + package_id, + Some(Rc::new( + crate::github_releases::UreqGithubReleaseTransport::new(), + )), + Rc::new(crate::download::UreqRuntimeDownloadTransport::new()), + &PathCommandResolver, + &ProcessCommandRunner, + observer, + ) +} + +#[cfg(feature = "lua")] +pub fn install_app_with_dependencies( + data_dir: &Path, + package_id: &PackageId, + github_release_transport: Option>, + download_transport: Rc, + resolver: &dyn CommandResolver, + runner: &dyn CommandRunner, +) -> Result { + install_app_with_dependencies_and_observer( + data_dir, + package_id, + github_release_transport, + download_transport, + resolver, + runner, + &NoopCommandObserver, + ) +} + +#[cfg(feature = "lua")] +pub fn install_app_with_dependencies_and_observer( + data_dir: &Path, + package_id: &PackageId, + github_release_transport: Option>, + download_transport: Rc, + resolver: &dyn CommandResolver, + runner: &dyn CommandRunner, + observer: &dyn CommandObserver, +) -> Result { + let (download, candidate) = prepare_app_with_transports( + data_dir, + package_id, + github_release_transport, + download_transport, + )?; + let installer = candidate + .install + .ok_or(AppOperationError::InstallerMissing)? + .parse() + .map_err(|error| AppOperationError::InstallerSchema(error.to_string()))?; + validate_installer_command(&installer)?; + let command = resolve_installer(&installer, &download.artifacts, resolver)?; + observer + .before_execute(&command) + .map_err(AppOperationError::InstallerCommandSpawnFailed)?; + let output = runner + .run(&command) + .map_err(AppOperationError::InstallerCommandSpawnFailed)?; + let stdout = bounded_output(&output.stdout); + let stderr = bounded_output(&output.stderr); + if output.status != 0 { + return Err(AppOperationError::InstallerCommandFailed(output.status)); + } + Ok(AppInstallResult { + package_id: download.package_id, + repository_id: download.repository_id, + version: download.version, + artifacts: download.artifacts, + command, + status: "succeeded".into(), + exit_code: output.status, + stdout, + stderr, }) } +#[cfg(feature = "lua")] +fn resolve_installer( + installer: &InstallerCommand, + artifacts: &[StagedArtifact], + resolver: &dyn CommandResolver, +) -> Result { + let executable = resolver + .resolve(&installer.executable) + .ok_or_else(|| AppOperationError::InstallerCommandNotFound(installer.executable.clone()))?; + let mut by_name = std::collections::HashMap::new(); + for artifact in artifacts { + if by_name + .insert(artifact.name.as_str(), &artifact.path) + .is_some() + { + return Err(AppOperationError::InstallerArtifactDuplicate( + artifact.name.clone(), + )); + } + } + let args = installer + .args + .iter() + .map(|arg| match arg { + InstallerArg::Literal(value) => Ok(value.clone()), + InstallerArg::Artifact(reference) => by_name + .get(reference.artifact.as_str()) + .map(|path| path.to_string_lossy().into_owned()) + .ok_or_else(|| { + AppOperationError::InstallerArtifactUnknown(reference.artifact.clone()) + }), + }) + .collect::, _>>()?; + Ok(ResolvedInstallerCommand { executable, args }) +} + +#[cfg(feature = "lua")] +fn validate_installer_command(installer: &InstallerCommand) -> Result<(), AppOperationError> { + if installer.executable.is_empty() { + return Err(AppOperationError::InstallerSchema( + "installer executable must not be empty".to_owned(), + )); + } + if installer.args.iter().any( + |arg| matches!(arg, InstallerArg::Artifact(reference) if reference.artifact.is_empty()), + ) { + return Err(AppOperationError::InstallerSchema( + "installer artifact reference must not be empty".to_owned(), + )); + } + Ok(()) +} + +#[cfg(feature = "lua")] +fn bounded_output(bytes: &[u8]) -> String { + const LIMIT: usize = 64 * 1024; + String::from_utf8_lossy(&bytes[..bytes.len().min(LIMIT)]).into_owned() +} + +#[cfg(all(test, feature = "lua"))] +mod installer_staging_tests { + use super::absolute_staging_root; + + #[test] + fn relative_nonexistent_data_dir_produces_absolute_canonical_staging_root() { + let name = format!("target/relative-staging-test-{}", std::process::id()); + let data_dir = std::path::Path::new(&name); + let _ = std::fs::remove_dir_all(data_dir); + let root = absolute_staging_root(data_dir).unwrap(); + assert!(root.is_absolute()); + assert_eq!(root.file_name().unwrap(), "downloads"); + assert!(root.parent().unwrap().is_dir()); + std::fs::remove_dir_all(data_dir).unwrap(); + } +} + +#[cfg(feature = "lua")] +fn absolute_staging_root(data_dir: &Path) -> Result { + let absolute_data_dir = if data_dir.is_absolute() { + data_dir.to_path_buf() + } else { + std::env::current_dir() + .map_err(AppOperationError::ArtifactIo)? + .join(data_dir) + }; + fs::create_dir_all(&absolute_data_dir)?; + Ok(fs::canonicalize(absolute_data_dir)?.join("downloads")) +} + #[cfg(feature = "lua")] fn prepare_artifact( downloads: &Path, diff --git a/crates/getter-operations/src/github_autogen.rs b/crates/getter-operations/src/github_autogen.rs index 554186e..f552253 100644 --- a/crates/getter-operations/src/github_autogen.rs +++ b/crates/getter-operations/src/github_autogen.rs @@ -603,6 +603,7 @@ mod tests { sha256: Some("A".repeat(64)), size: None, }], + install: None, }; let second = getter_core::UpdateCandidate { version: "2".to_owned(), @@ -628,6 +629,7 @@ mod tests { size: None, }, ], + install: None, }; let repeated = getter_core::UpdateCandidate { @@ -644,6 +646,7 @@ mod tests { sha256: Some("A".repeat(64)), size: None, }], + install: None, }; let distinct = getter_core::UpdateCandidate { version: "4".to_owned(), @@ -659,6 +662,7 @@ mod tests { sha256: Some("D".repeat(64)), size: None, }], + install: None, }; let manifest = github_manifest(&["c".repeat(128)], &[first, second, repeated, distinct]); diff --git a/crates/getter-operations/src/runtime.rs b/crates/getter-operations/src/runtime.rs index b82cc76..8bee8e2 100644 --- a/crates/getter-operations/src/runtime.rs +++ b/crates/getter-operations/src/runtime.rs @@ -1703,6 +1703,7 @@ return package_version { sha256: None, size: None, }], + install: None, }) .collect(), } diff --git a/crates/getter-operations/tests/installer.rs b/crates/getter-operations/tests/installer.rs new file mode 100644 index 0000000..7a48659 --- /dev/null +++ b/crates/getter-operations/tests/installer.rs @@ -0,0 +1,408 @@ +#![cfg(feature = "lua")] + +use getter_core::repository::RepositoryMetadata; +use getter_core::{RepositoryId, RepositoryPriority}; +use getter_operations::app::{ + download_app_with_transports, install_app_with_dependencies, + install_app_with_dependencies_and_observer, CommandObserver, CommandResolver, CommandRunner, + ResolvedInstallerCommand, RunnerOutput, +}; +use getter_operations::download::{ + RuntimeDownloadSink, RuntimeDownloadTransport, RuntimeDownloadTransportError, + RuntimeDownloadTransportRequest, +}; +use getter_storage::{MainDb, StoredPackageResolution, TrackedPackageUpsert}; +use sha2::{Digest, Sha256}; +use std::cell::RefCell; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::rc::Rc; + +struct Bytes(Vec); +impl RuntimeDownloadTransport for Bytes { + fn fetch( + &self, + _: &RuntimeDownloadTransportRequest, + sink: &mut dyn RuntimeDownloadSink, + ) -> Result<(), RuntimeDownloadTransportError> { + sink.write_chunk(&self.0) + .map_err(RuntimeDownloadTransportError::Sink) + } +} +struct Resolver(PathBuf); +impl CommandResolver for Resolver { + fn resolve(&self, executable: &str) -> Option { + (executable == "fake-installer").then(|| self.0.clone()) + } +} +#[derive(Default)] +struct Runner { + seen: RefCell>, +} +impl CommandRunner for Runner { + fn run(&self, command: &ResolvedInstallerCommand) -> std::io::Result { + *self.seen.borrow_mut() = Some(command.clone()); + Ok(RunnerOutput { + status: 0, + stdout: b"installed".to_vec(), + stderr: Vec::new(), + }) + } +} +fn digest(bytes: &[u8]) -> String { + format!("{:x}", Sha256::digest(bytes)) +} + +fn native_fixture(temp: &Path, name: &str, source_code: &str) -> PathBuf { + let source = temp.join(format!("{name}.rs")); + let executable = temp.join(format!("{name}{}", std::env::consts::EXE_SUFFIX)); + fs::write(&source, source_code).unwrap(); + let status = Command::new("rustc") + .arg(&source) + .arg("-o") + .arg(&executable) + .status() + .expect("rustc must be available while running Rust tests"); + assert!(status.success(), "compile platform-native fixture {name}"); + executable +} + +fn fake_installer(temp: &Path) -> PathBuf { + native_fixture( + temp, + "fake-installer", + r#"fn main() { + for argument in std::env::args().skip(1) { + println!("{argument}"); + } +}"#, + ) +} +fn fixture(install: &str, bytes: &[u8]) -> tempfile::TempDir { + let temp = tempfile::tempdir().unwrap(); + getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); + let package = temp.path().join("repo/official/android/com.example"); + fs::create_dir_all(&package).unwrap(); + fs::write( + package.join("metadata.jsonc"), + r#"{"type":"android:app","android":{"package_name":"com.example"}}"#, + ) + .unwrap(); + fs::write( + package.join("Manifest"), + format!("{} app.apk\n", digest(bytes)), + ) + .unwrap(); + fs::write(package.join("1.lua"), format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{{{ version="2", artifacts={{{{name="app",file_name="app.apk",url="mock://app"}}}}, install={install} }}}} }}"#)).unwrap(); + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_repository( + &RepositoryMetadata { + id: RepositoryId::new("official").unwrap(), + name: "Official".into(), + priority: RepositoryPriority::DEFAULT, + api_version: "v1".into(), + }, + Some(&temp.path().join("repo/official")), + None, + ) + .unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/com.example".parse().unwrap(), + enabled: true, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + temp +} + +#[test] +fn malformed_installer_does_not_block_download_but_fails_install_after_staging() { + let bytes = b"apk"; + let temp = fixture( + r#"{executable="fake-installer", args={}, surprise=true}"#, + bytes, + ); + let download = download_app_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + ) + .unwrap(); + assert!(download.artifacts[0].path.exists()); + + let error = install_app_with_dependencies( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &Runner::default(), + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.schema_invalid"); + assert!(download.artifacts[0].path.exists()); +} + +#[test] +fn resolves_artifact_to_staged_absolute_path_and_preserves_literal_argv() { + let bytes = b"apk"; + let temp = fixture( + r#"{executable="fake-installer",args={"install",{artifact="app"},"$HOME;literal"}}"#, + bytes, + ); + let runner = Runner::default(); + let result = install_app_with_dependencies( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &runner, + ) + .unwrap(); + let command = runner.seen.borrow().clone().unwrap(); + assert_eq!(command.executable, PathBuf::from("/fake/bin/installer")); + assert_eq!(command.args[0], "install"); + assert!(Path::new(&command.args[1]).is_absolute()); + assert_eq!(command.args[1], result.artifacts[0].path.to_string_lossy()); + assert_eq!(command.args[2], "$HOME;literal"); + assert_eq!(result.exit_code, 0); + assert_eq!(result.stdout, "installed"); +} + +#[test] +fn observer_runs_immediately_before_runner() { + struct Ordered { + events: Rc>>, + } + impl CommandObserver for Ordered { + fn before_execute(&self, _: &ResolvedInstallerCommand) -> std::io::Result<()> { + self.events.borrow_mut().push("observed"); + Ok(()) + } + } + impl CommandRunner for Ordered { + fn run(&self, _: &ResolvedInstallerCommand) -> std::io::Result { + self.events.borrow_mut().push("ran"); + Ok(RunnerOutput { + status: 0, + stdout: vec![], + stderr: vec![], + }) + } + } + let bytes = b"apk"; + let temp = fixture(r#"{executable="fake-installer",args={}}"#, bytes); + let events = Rc::new(RefCell::new(Vec::new())); + let ordered = Ordered { + events: events.clone(), + }; + install_app_with_dependencies_and_observer( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &ordered, + &ordered, + ) + .unwrap(); + assert_eq!(&*events.borrow(), &["observed", "ran"]); +} + +#[test] +fn rejects_unknown_reference_after_preserving_staged_file() { + let bytes = b"apk"; + let temp = fixture( + r#"{executable="fake-installer",args={{artifact="missing"}}}"#, + bytes, + ); + let error = install_app_with_dependencies( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &Runner::default(), + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.artifact_unknown"); + assert!(temp + .path() + .join("downloads") + .read_dir() + .unwrap() + .next() + .is_some()); +} + +#[test] +fn process_runner_executes_fake_file_with_exact_argv_without_shell_expansion() { + let bytes = b"apk"; + let temp = fixture( + r#"{executable="fake-installer",args={"install",{artifact="app"},"$HOME;literal"}}"#, + bytes, + ); + let executable = fake_installer(temp.path()); + let result = install_app_with_dependencies( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(executable), + &getter_operations::app::ProcessCommandRunner, + ) + .unwrap(); + let lines: Vec<_> = result.stdout.lines().collect(); + assert_eq!(lines[0], "install"); + assert_eq!(lines[1], result.artifacts[0].path.to_string_lossy()); + assert_eq!(lines[2], "$HOME;literal"); +} + +#[test] +fn process_runner_bounds_and_concurrently_drains_both_output_streams() { + const CAPTURE_LIMIT: usize = 64 * 1024; + + let temp = tempfile::tempdir().unwrap(); + let executable = native_fixture( + temp.path(), + "large-output", + r#"use std::io::{self, Write}; +fn main() { + let stdout = vec![b'o'; 2 * 1024 * 1024]; + let stderr = vec![b'e'; 2 * 1024 * 1024]; + let stdout_thread = std::thread::spawn(move || io::stdout().write_all(&stdout).unwrap()); + io::stderr().write_all(&stderr).unwrap(); + stdout_thread.join().unwrap(); +}"#, + ); + let output = getter_operations::app::ProcessCommandRunner + .run(&ResolvedInstallerCommand { + executable, + args: Vec::new(), + }) + .unwrap(); + + assert_eq!(output.status, 0); + assert_eq!(output.stdout.len(), CAPTURE_LIMIT); + assert_eq!(output.stderr.len(), CAPTURE_LIMIT); + assert!(output.stdout.iter().all(|byte| *byte == b'o')); + assert!(output.stderr.iter().all(|byte| *byte == b'e')); +} + +#[test] +fn command_not_found_is_stable_and_preserves_staged_file() { + let bytes = b"apk"; + let temp = fixture(r#"{executable="absent",args={}}"#, bytes); + let error = install_app_with_dependencies( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/unused")), + &Runner::default(), + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.command_not_found", "{error:?}"); + assert!(temp + .path() + .join("downloads") + .read_dir() + .unwrap() + .next() + .is_some()); +} + +#[test] +fn runner_io_error_is_stable_and_observer_precedes_attempt() { + struct Ordered { + events: Rc>>, + } + impl CommandObserver for Ordered { + fn before_execute(&self, _: &ResolvedInstallerCommand) -> std::io::Result<()> { + self.events.borrow_mut().push("observed"); + Ok(()) + } + } + impl CommandRunner for Ordered { + fn run(&self, _: &ResolvedInstallerCommand) -> std::io::Result { + self.events.borrow_mut().push("ran"); + Err(std::io::Error::new( + std::io::ErrorKind::PermissionDenied, + "fixture spawn failed", + )) + } + } + let bytes = b"apk"; + let temp = fixture(r#"{executable="fake-installer",args={}}"#, bytes); + let events = Rc::new(RefCell::new(Vec::new())); + let ordered = Ordered { + events: events.clone(), + }; + let error = install_app_with_dependencies_and_observer( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &ordered, + &ordered, + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.command_spawn_failed"); + assert_eq!(&*events.borrow(), &["observed", "ran"]); +} + +#[test] +fn nonzero_child_is_stable_and_observer_precedes_failure() { + struct Failed { + events: Rc>>, + } + impl CommandObserver for Failed { + fn before_execute(&self, _: &ResolvedInstallerCommand) -> std::io::Result<()> { + self.events.borrow_mut().push("observed"); + Ok(()) + } + } + impl CommandRunner for Failed { + fn run(&self, _: &ResolvedInstallerCommand) -> std::io::Result { + self.events.borrow_mut().push("ran"); + Ok(RunnerOutput { + status: 23, + stdout: Vec::new(), + stderr: b"failed".to_vec(), + }) + } + } + let bytes = b"apk"; + let temp = fixture(r#"{executable="fake-installer",args={}}"#, bytes); + let events = Rc::new(RefCell::new(Vec::new())); + let failed = Failed { + events: events.clone(), + }; + let error = install_app_with_dependencies_and_observer( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &failed, + &failed, + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.command_failed"); + assert_eq!(&*events.borrow(), &["observed", "ran"]); + assert!(temp + .path() + .join("downloads") + .read_dir() + .unwrap() + .next() + .is_some()); +} diff --git a/crates/getter-providers/src/lib.rs b/crates/getter-providers/src/lib.rs index 001eae5..362882d 100644 --- a/crates/getter-providers/src/lib.rs +++ b/crates/getter-providers/src/lib.rs @@ -92,6 +92,7 @@ impl FdroidRelease { sha256: self.sha256.clone(), size: self.size, }], + install: None, } } } @@ -405,6 +406,7 @@ fn github_release_update_candidate( channel: release.prerelease.then(|| "prerelease".to_owned()), source: Some("github".to_owned()), artifacts, + install: None, }) } @@ -513,6 +515,7 @@ mod tests { channel: Some("stable".to_owned()), source: Some("fixture".to_owned()), artifacts: Vec::new(), + install: None, }], }; From 989448975d891d5e5043ea0f42cf92e1fc1200a9 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Fri, 17 Jul 2026 14:37:47 +0800 Subject: [PATCH 70/71] feat(setup): onboard installed packages --- Cargo.lock | 415 ++++++++- crates/getter-cli/src/lib.rs | 82 +- crates/getter-cli/tests/bdd_cli.rs | 68 ++ .../features/cli/fresh_install_setup.feature | 15 + crates/getter-cli/tests/setup_cli.rs | 109 +++ crates/getter-operations/Cargo.toml | 12 + crates/getter-operations/src/autogen.rs | 820 ++++++++++++++++-- .../src/fdroid_signed_index.rs | 582 +++++++++++++ crates/getter-operations/src/lib.rs | 2 + crates/getter-operations/src/onboarding.rs | 595 +++++++++++++ crates/getter-operations/src/startup.rs | 12 +- .../tests/fdroid_signed_index.rs | 190 ++++ .../tests/fixtures/fdroid-official-index.jar | Bin 0 -> 3375358 bytes .../tests/fixtures/wrong-cert-index.jar | Bin 0 -> 2078 bytes crates/getter-operations/tests/onboarding.rs | 290 +++++++ crates/getter-operations/tests/startup.rs | 32 + crates/getter-storage/src/lib.rs | 95 +- 17 files changed, 3235 insertions(+), 84 deletions(-) create mode 100644 crates/getter-cli/tests/features/cli/fresh_install_setup.feature create mode 100644 crates/getter-cli/tests/setup_cli.rs create mode 100644 crates/getter-operations/src/fdroid_signed_index.rs create mode 100644 crates/getter-operations/src/onboarding.rs create mode 100644 crates/getter-operations/tests/fdroid_signed_index.rs create mode 100644 crates/getter-operations/tests/fixtures/fdroid-official-index.jar create mode 100644 crates/getter-operations/tests/fixtures/wrong-cert-index.jar create mode 100644 crates/getter-operations/tests/onboarding.rs diff --git a/Cargo.lock b/Cargo.lock index fe86385..9e8e34f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,18 +85,39 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "autocfg" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bitflags" version = "2.13.0" @@ -112,6 +133,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + [[package]] name = "bstr" version = "1.12.1" @@ -122,6 +152,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + [[package]] name = "bytecount" version = "0.6.9" @@ -197,6 +233,24 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + +[[package]] +name = "cms" +version = "0.3.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986e7713348fbe5322df18082d73a0ad53a2946dbb7a02452cf3622b83b91e7c" +dependencies = [ + "const-oid", + "der", + "spki", + "x509-cert", +] + [[package]] name = "colorchoice" version = "1.0.5" @@ -225,6 +279,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + [[package]] name = "convert_case" version = "0.10.0" @@ -250,6 +310,12 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -259,6 +325,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -293,6 +368,20 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-bigint" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a52aa3fcda4e6302a9f48734f234d35d4721b96f8fe07d073f07ce9df4f0271" +dependencies = [ + "cpubits", + "ctutils", + "num-traits", + "rand_core", + "serdect", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.7" @@ -303,6 +392,34 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "crypto-primes" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3633a51a39c69ebbaa4feaa694bd83d241e4093901c84a0963b19d9bb3f0cf8f" +dependencies = [ + "crypto-bigint", + "rand_core", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + [[package]] name = "cucumber" version = "0.23.0" @@ -360,6 +477,41 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "der" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d" +dependencies = [ + "const-oid", + "der_derive", + "flagset", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59600e2c2d636fde9b65e99cc6445ac770c63d3628195ff39932b8d6d7409903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -389,8 +541,30 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid", + "crypto-common 0.2.2", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -451,6 +625,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "flagset" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" + [[package]] name = "flate2" version = "1.1.9" @@ -461,6 +641,16 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "futures" version = "0.3.32" @@ -619,7 +809,7 @@ dependencies = [ "mlua", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "tempfile", "thiserror 1.0.69", "toml", @@ -645,19 +835,31 @@ dependencies = [ name = "getter-operations" version = "0.1.0" dependencies = [ + "base64", + "cms", + "der", + "fs2", + "getrandom 0.4.3", "getter-core", "getter-providers", "getter-storage", "json_comments", "mlua", + "rsa", "rusqlite", "serde", "serde_json", - "sha2", + "sha2 0.10.9", + "sha2 0.11.0", + "signature", + "spki", + "subtle", "tempfile", "thiserror 1.0.69", "ureq", "which 8.0.3", + "x509-cert", + "zip", ] [[package]] @@ -790,6 +992,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +[[package]] +name = "hybrid-array" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +dependencies = [ + "typenum", +] + [[package]] name = "ignore" version = "0.4.26" @@ -1117,6 +1328,15 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5" +[[package]] +name = "pem-rfc7468" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -1149,6 +1369,26 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pkcs1" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.33" @@ -1179,6 +1419,12 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "redox_syscall" version = "0.5.18" @@ -1260,6 +1506,24 @@ dependencies = [ "memchr", ] +[[package]] +name = "rsa" +version = "0.10.0-rc.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b2aa4ba0d89f73d1e332df05be0eeab8840351c36ca5654341dfdb57bb3caf" +dependencies = [ + "const-oid", + "crypto-bigint", + "crypto-primes", + "digest 0.11.3", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "zeroize", +] + [[package]] name = "rusqlite" version = "0.32.1" @@ -1498,6 +1762,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serdect" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha2" version = "0.10.9" @@ -1505,8 +1779,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -1515,6 +1800,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +[[package]] +name = "signature" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" +dependencies = [ + "digest 0.11.3", + "rand_core", +] + [[package]] name = "simd-adler32" version = "0.3.9" @@ -1560,6 +1855,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "spki" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "strsim" version = "0.11.1" @@ -1690,6 +1995,27 @@ dependencies = [ "syn", ] +[[package]] +name = "tls_codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio" version = "1.52.3" @@ -1927,6 +2253,22 @@ dependencies = [ "libc", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.11" @@ -1936,6 +2278,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-link" version = "0.2.1" @@ -2105,6 +2453,18 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "x509-cert" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "105ef4642d9cb137ef83d623d0e4bf08b8adf69e9918ca904a174adb6d3d038b" +dependencies = [ + "const-oid", + "der", + "spki", + "tls_codec", +] + [[package]] name = "zerocopy" version = "0.8.52" @@ -2130,9 +2490,52 @@ name = "zeroize" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "flate2", + "indexmap", + "memchr", + "thiserror 2.0.18", + "zopfli", +] [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] diff --git a/crates/getter-cli/src/lib.rs b/crates/getter-cli/src/lib.rs index 8d949c7..ea30aa2 100644 --- a/crates/getter-cli/src/lib.rs +++ b/crates/getter-cli/src/lib.rs @@ -27,6 +27,7 @@ use getter_operations::github_autogen; use getter_operations::github_latest_commit::{self, GithubLatestCommitOperationError}; use getter_operations::github_releases::{self, GithubReleaseOperationError}; use getter_operations::legacy_room::{self, LegacyRoomOperationError}; +use getter_operations::onboarding::{self, SetupAcceptance, SetupPreview}; use getter_operations::runtime as runtime_operations; use getter_operations::startup as startup_operations; use getter_storage::legacy_room::{ @@ -58,6 +59,13 @@ pub enum CliCommand { Startup { inventory: Option, }, + SetupPreview { + inventory: PathBuf, + }, + SetupApply { + preview: PathBuf, + acceptance: SetupAcceptance, + }, AppList, AppShow { package_id: PackageId, @@ -524,6 +532,21 @@ where inventory: Some(PathBuf::from(inventory)), } } + [domain, command, flag, inventory] + if domain == "setup" && command == "preview" && flag == "--inventory" => + { + CliCommand::SetupPreview { + inventory: PathBuf::from(inventory), + } + } + [domain, command, flag, preview, rest @ ..] + if domain == "setup" && command == "apply" && flag == "--preview" => + { + CliCommand::SetupApply { + preview: PathBuf::from(preview), + acceptance: parse_setup_acceptance(rest)?, + } + } [domain, command] if domain == "app" && command == "list" => CliCommand::AppList, [domain, command, package_id] if domain == "app" @@ -1046,6 +1069,33 @@ fn execute(invocation: CliInvocation, command_sink: &mut dyn Write) -> Result run_runtime_script(&invocation.data_dir, &script), + CliCommand::SetupPreview { inventory } => { + let db = open_main_db(&invocation.data_dir)?; + let cache = open_cache_db(&invocation.data_dir)?; + let inventory = read_installed_inventory(&inventory)?; + serde_json::to_value(onboarding::preview_setup_with_transport( + &invocation.data_dir, + &db, + &cache, + inventory, + &FixturelessSetupTransport, + )?) + .map_err(|error| CliError::Autogen(error.to_string())) + } + CliCommand::SetupApply { + preview, + acceptance, + } => { + let db = open_main_db(&invocation.data_dir)?; + let preview = read_setup_preview(&preview)?; + serde_json::to_value(onboarding::apply_setup_preview( + &invocation.data_dir, + &db, + &preview, + &acceptance, + )?) + .map_err(|error| CliError::Autogen(error.to_string())) + } CliCommand::AutogenInstalledPreview { inventory } => { let db = open_main_db(&invocation.data_dir)?; let inventory = read_installed_inventory(&inventory)?; @@ -1742,6 +1792,13 @@ fn parse_provider_github_latest_commit_args( Ok(parsed) } +fn parse_setup_acceptance(args: &[String]) -> Result { + match parse_autogen_acceptance(args)? { + AutogenAcceptance::AcceptAll => Ok(SetupAcceptance::AcceptAll), + AutogenAcceptance::Accept(ids) => Ok(SetupAcceptance::Accept(ids)), + } +} + fn parse_autogen_acceptance(args: &[String]) -> Result { match args { [flag] if flag == "--accept-all" => Ok(AutogenAcceptance::AcceptAll), @@ -1890,6 +1947,27 @@ fn read_github_commit_fixture(path: &Path) -> Result { }) } +struct FixturelessSetupTransport; + +impl onboarding::FdroidCatalogTransport for FixturelessSetupTransport { + fn fetch_official_index_xml(&self) -> Result { + Err("CLI setup preview does not perform live network access".to_owned()) + } +} + +fn read_setup_preview(path: &Path) -> Result { + let bytes = fs::read(path) + .map_err(|error| CliError::Autogen(format!("failed to read setup preview: {error}")))?; + let value: Value = serde_json::from_slice(&bytes) + .map_err(|error| CliError::Autogen(format!("invalid setup preview: {error}")))?; + let payload = value + .get("data") + .filter(|_| value.get("ok").and_then(Value::as_bool) == Some(true)) + .unwrap_or(&value); + serde_json::from_value(payload.clone()) + .map_err(|error| CliError::Autogen(format!("invalid setup preview: {error}"))) +} + fn read_installed_inventory(path: &Path) -> Result { let bytes = fs::read(path) .map_err(|source| CliError::Autogen(format!("failed to read inventory: {source}")))?; @@ -2432,7 +2510,7 @@ fn envelope_to_string(value: Value) -> String { } fn usage_text() -> String { - "Usage: getter --data-dir ]|app list|repo list|repo add [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo [--releases ] [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() + "Usage: getter --data-dir ]|setup preview --inventory |setup apply --preview (--accept-all|--accept ...)|app list|repo list|repo add [--priority ]|repo eval |repo validate |package eval [--repo ]|storage validate|version pin |version unpin |hub list|update check --fixture |runtime script --script |debug fake-task submit --request |debug fake-task run |debug fake-task list|debug fake-task cancel |debug fake-task events --after --limit |debug fake-task install-result --status |autogen installed preview --inventory |autogen installed apply --preview (--accept-all|--accept ...)|autogen fdroid preview --index [--package ...] [--inventory ]|autogen fdroid apply --preview (--accept-all|--accept ...)|autogen github preview --owner --repo --android-package --releases [--display-name ] [--asset-include ] [--asset-exclude ] [--include-prereleases]|autogen github apply --preview (--accept-all|--accept ...)|provider github releases --owner --repo [--releases ] [--asset-include ] [--asset-exclude ] [--include-prereleases] [--refresh]|provider github latest-commit --owner --repo --commit [--ref ] [--refresh]|autogen cleanup preview --inventory |autogen cleanup apply --preview (--accept-all|--accept ...)|legacy import-room-bundle |legacy import-room-db |legacy report-list>\nNote: `debug fake-task` commands are persisted fake-download scaffolding. ADR-0011 runtime task debugging uses `runtime script` and does not preserve task state across CLI invocations.\n".to_owned() } #[derive(Debug, Deserialize)] @@ -2478,6 +2556,8 @@ impl CliCommand { match self { Self::Init => "init", Self::Startup { .. } => "startup", + Self::SetupPreview { .. } => "setup preview", + Self::SetupApply { .. } => "setup apply", Self::AppList => "app list", Self::AppShow { .. } => "app show", Self::AppCheck { .. } => "app check", diff --git a/crates/getter-cli/tests/bdd_cli.rs b/crates/getter-cli/tests/bdd_cli.rs index 6812b32..bc18451 100644 --- a/crates/getter-cli/tests/bdd_cli.rs +++ b/crates/getter-cli/tests/bdd_cli.rs @@ -1184,6 +1184,57 @@ fn run_getter_runtime_script(world: &mut CliWorld) { world.json = None; } +#[when("I run getter setup preview for that inventory")] +fn run_getter_setup_preview(world: &mut CliWorld) { + let inventory = world.inventory.as_ref().expect("inventory exists"); + let output = run_getter( + world, + [ + "setup".to_owned(), + "preview".to_owned(), + "--inventory".to_owned(), + inventory.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter setup apply for that preview with accept-all")] +fn run_getter_setup_apply_accept_all(world: &mut CliWorld) { + let preview = world + .autogen_preview + .as_ref() + .expect("setup preview exists"); + let output = run_getter( + world, + [ + "setup".to_owned(), + "apply".to_owned(), + "--preview".to_owned(), + preview.to_string_lossy().to_string(), + "--accept-all".to_owned(), + ], + ); + world.output = Some(output); + world.json = None; +} + +#[when("I run getter startup for that inventory")] +fn run_getter_startup_for_inventory(world: &mut CliWorld) { + let inventory = world.inventory.as_ref().expect("inventory exists"); + let output = run_getter( + world, + [ + "startup".to_owned(), + "--inventory".to_owned(), + inventory.to_string_lossy().to_string(), + ], + ); + world.output = Some(output); + world.json = None; +} + #[when("I run getter autogen installed preview for that inventory")] fn run_getter_autogen_installed_preview(world: &mut CliWorld) { let inventory = world.inventory.as_ref().expect("inventory exists"); @@ -2015,6 +2066,23 @@ fn autogen_repository_has_not_been_written(world: &mut CliWorld) { ); } +#[then(expr = "setup state is {string}")] +fn setup_state_is(world: &mut CliWorld, state: String) { + assert_eq!(current_json(world)["data"]["setup"]["state"], state); +} + +#[then("I save the setup preview envelope to a file")] +fn save_setup_preview_envelope_to_file(world: &mut CliWorld) { + let temp = world.temp.as_ref().expect("tempdir exists"); + let preview = temp.path().join("setup-preview.json"); + fs::write( + &preview, + serde_json::to_vec_pretty(current_json(world)).expect("preview serializes"), + ) + .expect("write setup preview"); + world.autogen_preview = Some(preview); +} + #[then("I save the autogen preview to a file")] fn save_autogen_preview_to_file(world: &mut CliWorld) { let data = current_json(world) diff --git a/crates/getter-cli/tests/features/cli/fresh_install_setup.feature b/crates/getter-cli/tests/features/cli/fresh_install_setup.feature new file mode 100644 index 0000000..b2e052c --- /dev/null +++ b/crates/getter-cli/tests/features/cli/fresh_install_setup.feature @@ -0,0 +1,15 @@ +Feature: Fresh-install package setup + Scenario: User tracks an installed app through the unified setup flow + Given an initialized getter data directory + And an installed inventory with Android app "com.example.fallback" labeled "Fallback App" + When I run getter startup for that inventory + Then the command succeeds + And setup state is "needs_package_setup" + When I run getter setup preview for that inventory + Then the command succeeds + And I save the setup preview envelope to a file + When I run getter setup apply for that preview with accept-all + Then the command succeeds + When I run getter startup for that inventory + Then the command succeeds + And setup state is "ready" diff --git a/crates/getter-cli/tests/setup_cli.rs b/crates/getter-cli/tests/setup_cli.rs new file mode 100644 index 0000000..1042354 --- /dev/null +++ b/crates/getter-cli/tests/setup_cli.rs @@ -0,0 +1,109 @@ +use getter_cli::{run, ExitCode}; +use serde_json::{json, Value}; +use std::fs; + +#[test] +fn setup_preview_uses_product_safe_inventory_input_and_offline_fallback() { + let temp = tempfile::tempdir().unwrap(); + let inventory = temp.path().join("installed.json"); + fs::write(&inventory, json!({ + "format": "upgradeall-installed-inventory", "version": 1, + "items": [{"kind":"android_package","package_name":"com.example.app","label":"Example","version_name":"1.0","version_code":1}] + }).to_string()).unwrap(); + + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "setup", + "preview", + "--inventory", + inventory.to_str().unwrap(), + ]); + assert_eq!(output.exit_code, ExitCode::Success, "{}", output.stdout); + let envelope: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(envelope["command"], "setup preview"); + assert_eq!( + envelope["data"]["candidates"][0]["category"], + "installed_fallback" + ); + assert!(envelope["data"]["preview_id"] + .as_str() + .is_some_and(|value| value.len() == 64)); +} + +#[test] +fn setup_preview_envelope_can_be_applied_without_manual_json_extraction() { + let temp = tempfile::tempdir().unwrap(); + let inventory = temp.path().join("installed.json"); + let preview = temp.path().join("preview.json"); + fs::write( + &inventory, + json!({ + "format": "upgradeall-installed-inventory", + "version": 1, + "items": [{ + "kind": "android_package", + "package_name": "com.example.app", + "label": "Example", + "version_name": "1.0", + "version_code": 1 + }] + }) + .to_string(), + ) + .unwrap(); + + let preview_output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "setup", + "preview", + "--inventory", + inventory.to_str().unwrap(), + ]); + assert_eq!(preview_output.exit_code, ExitCode::Success); + fs::write(&preview, &preview_output.stdout).unwrap(); + + let apply_output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "setup", + "apply", + "--preview", + preview.to_str().unwrap(), + "--accept-all", + ]); + assert_eq!( + apply_output.exit_code, + ExitCode::Success, + "{}", + apply_output.stdout + ); + let envelope: Value = serde_json::from_str(&apply_output.stdout).unwrap(); + assert_eq!( + envelope["data"]["applied_package_ids"][0], + "android/app/com.example.app" + ); + assert!(temp + .path() + .join("repo/autogen/android/app/com.example.app/.autogen.jsonc") + .is_file()); +} + +#[test] +fn setup_request_rejects_provider_controls() { + let temp = tempfile::tempdir().unwrap(); + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "setup", + "preview", + "--endpoint", + "https://evil.invalid/index.xml", + ]); + assert_eq!(output.exit_code, ExitCode::Usage); +} diff --git a/crates/getter-operations/Cargo.toml b/crates/getter-operations/Cargo.toml index 0ec16b8..520a9d6 100644 --- a/crates/getter-operations/Cargo.toml +++ b/crates/getter-operations/Cargo.toml @@ -10,17 +10,29 @@ lua-provider-host-dev = ["lua"] rustls-platform-verifier = ["ureq/platform-verifier"] [dependencies] +base64 = "0.22" +cms = "=0.3.0-pre.2" +der = "0.8" +fs2 = "0.4" json_comments = "0.2" getter-core = { path = "../getter-core", default-features = false } getter-providers = { path = "../getter-providers" } getter-storage = { path = "../getter-storage" } +getrandom = "0.4" mlua = { version = "0.10", features = ["luajit", "vendored"], optional = true } serde = { version = "1", features = ["derive"] } +rsa = "0.10.0-rc" serde_json = "1" sha2 = "0.10" +sha2-11 = { package = "sha2", version = "0.11.0-rc" } +signature = "3.0.0-rc" +spki = "0.8" +subtle = "2.6" thiserror = "1" which = "=8.0.3" ureq = { version = "3.3.0", default-features = false, features = ["gzip", "rustls"] } +x509-cert = "0.3" +zip = { version = "2.4.2", default-features = false, features = ["deflate"] } [dev-dependencies] rusqlite = { version = "0.32", features = ["bundled"] } diff --git a/crates/getter-operations/src/autogen.rs b/crates/getter-operations/src/autogen.rs index 770d3bf..826e45f 100644 --- a/crates/getter-operations/src/autogen.rs +++ b/crates/getter-operations/src/autogen.rs @@ -6,6 +6,7 @@ //! repository coverage, package-local `.autogen.jsonc` ownership, file writes, //! cleanup, and tracked state updates. +use fs2::FileExt; use getter_core::autogen::{ content_hash, content_hash_bytes, plan_installed_autogen, record_file_key, render_autogen_record, AutogenCandidate, AutogenPlan, AutogenRecord, AutogenSkipReason, @@ -19,9 +20,11 @@ use getter_core::repository::{ }; use getter_core::{PackageId, RepositoryId, RepositoryPriority}; use getter_storage::{MainDb, StorageError}; +use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use std::collections::{BTreeMap, BTreeSet, HashMap}; -use std::fs; +use std::fs::{self, File}; +use std::io::Write; use std::path::{Component, Path, PathBuf}; #[derive(Debug, Clone, PartialEq, Eq)] @@ -60,6 +63,7 @@ pub fn build_installed_autogen_plan( db: &MainDb, inventory: &InstalledInventory, ) -> AutogenOperationResult { + recover_autogen_transactions(data_dir, db)?; let (target_alias, _target_path, target_priority) = generated_repository_config(data_dir)?; let covered = higher_priority_package_coverage(db, &target_alias, target_priority)?; let mut plan = plan_installed_autogen(inventory, &covered) @@ -106,6 +110,7 @@ pub fn cleanup_preview_json( db: &MainDb, inventory: &InstalledInventory, ) -> AutogenOperationResult { + recover_autogen_transactions(data_dir, db)?; let (target_alias, repo_path, _target_priority) = generated_repository_config(data_dir)?; if !repo_path.is_dir() { return Ok(cleanup_preview_response( @@ -172,6 +177,313 @@ pub fn cleanup_preview_json( )) } +pub(crate) struct AutogenApplySpec<'a> { + pub(crate) preview: &'a Value, + pub(crate) acceptance: &'a AutogenAcceptance, + pub(crate) expected_operation: &'static str, + pub(crate) expected_generator: &'static str, +} + +struct PreparedCandidate { + package_id: PackageId, + target_dir: PathBuf, + payload: PreviewCandidatePayload, +} + +const AUTOGEN_JOURNAL_FILE: &str = "journal.json"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +enum AutogenTransactionPhase { + Prepared, + BackupsDone, + SwapsDone, + DbCommitted, +} + +#[derive(Debug, Serialize, Deserialize)] +struct AutogenTransactionEntry { + package_id: String, + target: PathBuf, + backup: PathBuf, + staged: PathBuf, + existed: bool, +} + +#[derive(Debug, Serialize, Deserialize)] +struct AutogenTransactionJournal { + transaction_id: String, + phase: AutogenTransactionPhase, + package_ids: Vec, + entries: Vec, +} + +fn sync_directory(path: &Path) -> std::io::Result<()> { + File::open(path)?.sync_all() +} + +fn sync_tree(path: &Path) -> std::io::Result<()> { + for entry in fs::read_dir(path)? { + let entry = entry?; + if entry.file_type()?.is_dir() { + sync_tree(&entry.path())?; + } else { + File::open(entry.path())?.sync_all()?; + } + } + sync_directory(path) +} + +#[cfg(test)] +thread_local! { + static CRASH_AFTER_PHASE: std::cell::Cell = const { std::cell::Cell::new(0) }; +} + +#[cfg(test)] +fn simulate_crash_after(phase: AutogenTransactionPhase) -> Result<(), String> { + let value = match phase { + AutogenTransactionPhase::BackupsDone => 1, + AutogenTransactionPhase::SwapsDone => 2, + AutogenTransactionPhase::DbCommitted => 3, + AutogenTransactionPhase::Prepared => 0, + }; + if CRASH_AFTER_PHASE.get() == value { + return Err(format!("simulated process crash after {phase:?}")); + } + Ok(()) +} + +#[cfg(not(test))] +fn simulate_crash_after(_phase: AutogenTransactionPhase) -> Result<(), String> { + Ok(()) +} + +fn write_journal( + transaction_dir: &Path, + journal: &AutogenTransactionJournal, +) -> Result<(), String> { + let temporary = transaction_dir.join("journal.json.tmp"); + let destination = transaction_dir.join(AUTOGEN_JOURNAL_FILE); + let bytes = serde_json::to_vec_pretty(journal) + .map_err(|error| format!("failed to serialize autogen transaction journal: {error}"))?; + let mut file = File::create(&temporary) + .map_err(|error| format!("failed to create autogen transaction journal: {error}"))?; + file.write_all(&bytes) + .and_then(|()| file.sync_all()) + .map_err(|error| format!("failed to persist autogen transaction journal: {error}"))?; + fs::rename(&temporary, &destination) + .map_err(|error| format!("failed to publish autogen transaction journal: {error}"))?; + sync_directory(transaction_dir) + .map_err(|error| format!("failed to sync autogen transaction directory: {error}")) +} + +fn rollback_journal(journal: &AutogenTransactionJournal) -> Result<(), String> { + let mut failures = Vec::new(); + for entry in journal.entries.iter().rev() { + if entry.backup.exists() { + if entry.target.exists() { + if let Err(error) = fs::remove_dir_all(&entry.target) { + failures.push(format!( + "remove replacement '{}': {error}", + entry.target.display() + )); + continue; + } + } + if let Err(error) = fs::rename(&entry.backup, &entry.target) { + failures.push(format!( + "restore backup '{}' to '{}': {error}", + entry.backup.display(), + entry.target.display() + )); + } + } else if !entry.existed && entry.target.exists() { + if let Err(error) = fs::remove_dir_all(&entry.target) { + failures.push(format!( + "remove new target '{}': {error}", + entry.target.display() + )); + } + } else if entry.existed && !entry.target.exists() { + failures.push(format!( + "original target '{}' is missing and backup '{}' is unavailable", + entry.target.display(), + entry.backup.display() + )); + } + } + if failures.is_empty() { + Ok(()) + } else { + Err(failures.join("; ")) + } +} + +struct AutogenApplyLock { + file: File, +} + +impl Drop for AutogenApplyLock { + fn drop(&mut self) { + let _ = self.file.unlock(); + } +} + +fn lock_autogen_apply(data_dir: &Path) -> AutogenOperationResult { + fs::create_dir_all(data_dir).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to access Getter data directory: {error}")) + })?; + let file = File::options() + .read(true) + .write(true) + .create(true) + .open(data_dir.join(".autogen-apply.lock")) + .map_err(|error| { + AutogenOperationError::Autogen(format!("failed to open autogen lock: {error}")) + })?; + file.try_lock_exclusive().map_err(|error| { + AutogenOperationError::Autogen(format!( + "another autogen apply or recovery is active: {error}" + )) + })?; + Ok(AutogenApplyLock { file }) +} + +fn path_is_symlink(path: &Path) -> bool { + fs::symlink_metadata(path).is_ok_and(|metadata| metadata.file_type().is_symlink()) +} + +fn journal_target_is_contained(repo_path: &Path, target: &Path) -> bool { + if path_is_symlink(repo_path) { + return false; + } + let Ok(relative) = target.strip_prefix(repo_path) else { + return false; + }; + if relative.as_os_str().is_empty() + || relative + .components() + .any(|component| !matches!(component, Component::Normal(_))) + { + return false; + } + let mut current = repo_path.to_path_buf(); + for component in relative.components() { + current.push(component.as_os_str()); + if current.exists() && path_is_symlink(¤t) { + return false; + } + } + true +} + +fn validate_autogen_journal( + repo_path: &Path, + transaction_dir: &Path, + journal: &AutogenTransactionJournal, +) -> AutogenOperationResult<()> { + if transaction_dir.file_name().and_then(|name| name.to_str()) + != Some(journal.transaction_id.as_str()) + { + return Err(AutogenOperationError::Autogen( + "autogen journal transaction id does not match its directory".into(), + )); + } + let staged_root = transaction_dir.join("staged"); + let backup_root = transaction_dir.join("backups"); + for (index, entry) in journal.entries.iter().enumerate() { + if !journal_target_is_contained(repo_path, &entry.target) + || entry.backup != backup_root.join(index.to_string()) + || entry.staged != staged_root.join(index.to_string()) + { + return Err(AutogenOperationError::Autogen(format!( + "autogen journal contains unsafe paths for package '{}'", + entry.package_id + ))); + } + } + Ok(()) +} + +pub fn recover_autogen_transactions(data_dir: &Path, db: &MainDb) -> AutogenOperationResult<()> { + let _lock = lock_autogen_apply(data_dir)?; + recover_autogen_transactions_locked(data_dir, db) +} + +fn recover_autogen_transactions_locked(data_dir: &Path, db: &MainDb) -> AutogenOperationResult<()> { + let (_, repo_path, _) = generated_repository_config(data_dir)?; + if path_is_symlink(&repo_path) { + return Err(AutogenOperationError::Autogen( + "configured generated repository must not be a symbolic link".into(), + )); + } + if !repo_path.is_dir() { + return Ok(()); + } + let entries = fs::read_dir(&repo_path).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to scan autogen transactions: {error}")) + })?; + for entry in entries { + let entry = entry.map_err(|error| AutogenOperationError::Autogen(error.to_string()))?; + let name = entry.file_name(); + if !name.to_string_lossy().starts_with(".autogen-transaction-") + || !entry + .file_type() + .map_err(|error| AutogenOperationError::Autogen(error.to_string()))? + .is_dir() + { + continue; + } + let transaction_dir = entry.path(); + if path_is_symlink(&transaction_dir) { + return Err(AutogenOperationError::Autogen(format!( + "autogen transaction directory must not be a symbolic link: '{}'", + transaction_dir.display() + ))); + } + let journal_path = transaction_dir.join(AUTOGEN_JOURNAL_FILE); + if !journal_path.is_file() { + return Err(AutogenOperationError::Autogen(format!( + "autogen transaction '{}' has no durable journal", + transaction_dir.display() + ))); + } + let journal: AutogenTransactionJournal = + serde_json::from_slice(&fs::read(&journal_path).map_err(|error| { + AutogenOperationError::Autogen(format!( + "failed to read '{}': {error}", + journal_path.display() + )) + })?) + .map_err(|error| { + AutogenOperationError::Autogen(format!( + "invalid autogen journal '{}': {error}", + journal_path.display() + )) + })?; + validate_autogen_journal(&repo_path, &transaction_dir, &journal)?; + let committed = db.has_autogen_transaction_commit(&journal.transaction_id)?; + if !committed { + rollback_journal(&journal).map_err(|error| { + AutogenOperationError::Autogen(format!( + "failed to roll back autogen transaction '{}': {error}; transaction retained at '{}'", + journal.transaction_id, transaction_dir.display() + )) + })?; + } + fs::remove_dir_all(&transaction_dir).map_err(|error| { + AutogenOperationError::Autogen(format!( + "failed to clean recovered autogen transaction '{}': {error}", + transaction_dir.display() + )) + })?; + if committed { + db.delete_autogen_transaction_commit(&journal.transaction_id)?; + } + } + Ok(()) +} + pub fn apply_installed_preview( data_dir: &Path, db: &MainDb, @@ -193,56 +505,293 @@ pub(crate) fn apply_preview( db: &MainDb, preview: &Value, acceptance: &AutogenAcceptance, - expected_operation: &str, - expected_generator: &str, + expected_operation: &'static str, + expected_generator: &'static str, ) -> AutogenOperationResult { - if preview.get("operation").and_then(Value::as_str) != Some(expected_operation) { - return Err(AutogenOperationError::Autogen(format!( - "autogen preview operation must be '{expected_operation}'" - ))); + apply_preview_batch( + data_dir, + db, + &[AutogenApplySpec { + preview, + acceptance, + expected_operation, + expected_generator, + }], + ) +} + +pub(crate) fn apply_preview_batch( + data_dir: &Path, + db: &MainDb, + specs: &[AutogenApplySpec<'_>], +) -> AutogenOperationResult { + let _lock = lock_autogen_apply(data_dir)?; + apply_preview_batch_locked(data_dir, db, specs) +} + +fn apply_preview_batch_locked( + data_dir: &Path, + db: &MainDb, + specs: &[AutogenApplySpec<'_>], +) -> AutogenOperationResult { + if specs.is_empty() { + return Err(AutogenOperationError::Autogen( + "autogen apply batch must contain at least one preview".to_owned(), + )); } + recover_autogen_transactions_locked(data_dir, db)?; let (target_alias, repo_path, target_priority) = generated_repository_config(data_dir)?; - if preview.get("target_repo_id").and_then(Value::as_str) != Some(target_alias.as_str()) { - return Err(AutogenOperationError::Autogen(format!( - "{expected_operation} target_repo_id must be '{}'", - target_alias.as_str() - ))); + if path_is_symlink(&repo_path) { + return Err(AutogenOperationError::Autogen( + "configured generated repository must not be a symbolic link".into(), + )); } - ensure_generated_repository(&repo_path, db, &target_alias, target_priority)?; - let accepted = accepted_preview_candidates(preview, acceptance)?; - let mut applied = Vec::new(); + let mut prepared = Vec::new(); + let mut package_ids = BTreeSet::new(); + let mut target_paths = BTreeSet::new(); - for candidate in accepted { - let package_id = preview_package_id(candidate)?; - let relative_path = preview_relative_path(candidate)?; - let payload = - preview_candidate_payload(candidate, &package_id, &relative_path, expected_generator)?; - let target_dir = safe_join(&repo_path, &relative_path)?; - if target_dir.exists() { - read_owned_record(&target_dir, &package_id, &relative_path, expected_generator)?; - clear_directory_contents(&target_dir)?; - } else { - fs::create_dir_all(&target_dir).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to create generated package directory '{}': {source}", + for spec in specs { + if spec.preview.get("operation").and_then(Value::as_str) != Some(spec.expected_operation) { + return Err(AutogenOperationError::Autogen(format!( + "autogen preview operation must be '{}'", + spec.expected_operation + ))); + } + if spec.preview.get("target_repo_id").and_then(Value::as_str) != Some(target_alias.as_str()) + { + return Err(AutogenOperationError::Autogen(format!( + "{} target_repo_id must be '{}'", + spec.expected_operation, + target_alias.as_str() + ))); + } + for candidate in accepted_preview_candidates(spec.preview, spec.acceptance)? { + let package_id = preview_package_id(candidate)?; + let relative_path = preview_relative_path(candidate)?; + let payload = preview_candidate_payload( + candidate, + &package_id, + &relative_path, + spec.expected_generator, + )?; + let target_dir = safe_join(&repo_path, &relative_path)?; + if !package_ids.insert(package_id.to_string()) { + return Err(AutogenOperationError::Autogen(format!( + "duplicate accepted package id '{package_id}' in autogen apply batch" + ))); + } + if !target_paths.insert(target_dir.clone()) { + return Err(AutogenOperationError::Autogen(format!( + "duplicate accepted target path '{}' in autogen apply batch", target_dir.display() + ))); + } + if target_dir.exists() { + read_owned_record( + &target_dir, + &package_id, + &relative_path, + spec.expected_generator, + )?; + } + prepared.push(PreparedCandidate { + package_id, + target_dir, + payload, + }); + } + } + + let repo_existed = repo_path.exists(); + fs::create_dir_all(&repo_path).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create generated repository '{}': {source}", + repo_path.display() + )) + })?; + let transaction_dir = repo_path.join(format!( + ".autogen-transaction-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_nanos() + )); + let staged_root = transaction_dir.join("staged"); + let backup_root = transaction_dir.join("backups"); + fs::create_dir_all(&backup_root).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create autogen backup directory: {source}" + )) + })?; + for (index, candidate) in prepared.iter().enumerate() { + let staged = staged_root.join(index.to_string()); + fs::create_dir_all(&staged).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create autogen staging directory '{}': {source}", + staged.display() + )) + })?; + write_generated_package( + &staged, + &candidate.payload.files, + &candidate.payload.record_content, + )?; + } + sync_tree(&transaction_dir).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to sync staged autogen transaction: {source}" + )) + })?; + let transaction_id = transaction_dir + .file_name() + .unwrap() + .to_string_lossy() + .into_owned(); + let mut journal = AutogenTransactionJournal { + transaction_id: transaction_id.clone(), + phase: AutogenTransactionPhase::Prepared, + package_ids: prepared + .iter() + .map(|candidate| candidate.package_id.to_string()) + .collect(), + entries: prepared + .iter() + .enumerate() + .map(|(index, candidate)| AutogenTransactionEntry { + package_id: candidate.package_id.to_string(), + target: candidate.target_dir.clone(), + backup: backup_root.join(index.to_string()), + staged: staged_root.join(index.to_string()), + existed: candidate.target_dir.exists(), + }) + .collect(), + }; + write_journal(&transaction_dir, &journal).map_err(AutogenOperationError::Autogen)?; + sync_directory(&repo_path).map_err(|error| { + AutogenOperationError::Autogen(format!( + "failed to persist autogen transaction directory entry: {error}" + )) + })?; + + let result_package_ids = journal.package_ids.clone(); + let committed_result = || { + json!({ + "operation": "autogen.apply", + "target_repo_id": target_alias.as_str(), + "accepted_package_ids": result_package_ids, + "applied": result_package_ids.iter().map(|id| json!({ "package_id": id })).collect::>(), + "written": result_package_ids.len(), + "applied_count": result_package_ids.len(), + }) + }; + let apply_result = (|| -> AutogenOperationResult { + for entry in &journal.entries { + if let Some(parent) = entry.target.parent() { + fs::create_dir_all(parent).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to create generated package parent: {source}" + )) + })?; + } + if entry.existed { + fs::rename(&entry.target, &entry.backup).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to retain generated package backup '{}': {source}", + entry.target.display() + )) + })?; + sync_directory(&backup_root).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to sync autogen backup directory: {source}" + )) + })?; + if let Some(parent) = entry.target.parent() { + sync_directory(parent).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to sync generated package parent: {source}" + )) + })?; + } + } + } + journal.phase = AutogenTransactionPhase::BackupsDone; + write_journal(&transaction_dir, &journal).map_err(AutogenOperationError::Autogen)?; + simulate_crash_after(journal.phase).map_err(AutogenOperationError::Autogen)?; + + for entry in &journal.entries { + fs::rename(&entry.staged, &entry.target).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to atomically replace generated package '{}': {source}", + entry.target.display() )) })?; + if let Some(parent) = entry.target.parent() { + sync_directory(parent).map_err(|source| { + AutogenOperationError::Autogen(format!( + "failed to sync generated package swap: {source}" + )) + })?; + } } - write_generated_package(&target_dir, &payload.files, &payload.record_content)?; - db.upsert_generated_tracked_package_preserving_user_state(&package_id, &target_alias)?; - applied.push(json!({ - "package_id": package_id.to_string(), - "output_relative_path": relative_path, - })); + journal.phase = AutogenTransactionPhase::SwapsDone; + write_journal(&transaction_dir, &journal).map_err(AutogenOperationError::Autogen)?; + simulate_crash_after(journal.phase).map_err(AutogenOperationError::Autogen)?; + + let metadata = RepositoryMetadata { + id: target_alias.clone(), + name: DEFAULT_AUTOGEN_REPOSITORY_NAME.to_owned(), + priority: target_priority, + api_version: REPO_API_VERSION_V1.to_owned(), + }; + let ids = prepared + .iter() + .map(|candidate| candidate.package_id.clone()) + .collect::>(); + db.apply_generated_repository_batch_with_marker( + &metadata, + &repo_path, + &ids, + Some(&transaction_id), + )?; + journal.phase = AutogenTransactionPhase::DbCommitted; + write_journal(&transaction_dir, &journal).map_err(AutogenOperationError::Autogen)?; + simulate_crash_after(journal.phase).map_err(AutogenOperationError::Autogen)?; + Ok(committed_result()) + })(); + + if let Err(error) = apply_result { + if error.to_string().contains("simulated process crash") { + return Err(error); + } + if db.has_autogen_transaction_commit(&transaction_id)? { + // The database is the commit point. Leave journal/marker recovery + // artifacts for the next startup rather than reporting a false failure. + return Ok(committed_result()); + } + if let Err(rollback_error) = rollback_journal(&journal) { + return Err(AutogenOperationError::Autogen(format!( + "{error}; rollback also failed: {rollback_error}; transaction retained at '{}'", + transaction_dir.display() + ))); + } + fs::remove_dir_all(&transaction_dir).map_err(|cleanup_error| { + AutogenOperationError::Autogen(format!( + "{error}; rollback succeeded but cleanup failed: {cleanup_error}" + )) + })?; + if !repo_existed { + let _ = fs::remove_dir(&repo_path); + } + return Err(error); } - Ok(json!({ - "target_repo_id": target_alias.as_str(), - "target_repo_path": repo_path, - "applied_count": applied.len(), - "applied": applied, - })) + if fs::remove_dir_all(&transaction_dir).is_ok() { + // A stale commit marker is harmless and recoverable; cleanup failure + // must not turn an already committed apply into a reported failure. + let _ = db.delete_autogen_transaction_commit(&transaction_id); + } + apply_result } pub fn apply_cleanup_preview( @@ -251,6 +800,8 @@ pub fn apply_cleanup_preview( preview: &Value, acceptance: &AutogenAcceptance, ) -> AutogenOperationResult { + let _lock = lock_autogen_apply(data_dir)?; + recover_autogen_transactions_locked(data_dir, db)?; let (target_alias, repo_path, _target_priority) = generated_repository_config(data_dir)?; if preview.get("target_repo_id").and_then(Value::as_str) != Some(target_alias.as_str()) { return Err(AutogenOperationError::Autogen(format!( @@ -442,6 +993,18 @@ fn accepted_preview_candidates<'a>( AutogenAcceptance::AcceptAll => Ok(candidates.iter().collect()), AutogenAcceptance::Accept(ids) => { let accepted: BTreeSet = ids.iter().map(ToString::to_string).collect(); + let available: BTreeSet<&str> = candidates + .iter() + .filter_map(|candidate| candidate.get("package_id").and_then(Value::as_str)) + .collect(); + if let Some(unknown) = accepted + .iter() + .find(|package_id| !available.contains(package_id.as_str())) + { + return Err(AutogenOperationError::Autogen(format!( + "accepted package id '{unknown}' is not present in the autogen preview" + ))); + } Ok(candidates .iter() .filter(|candidate| { @@ -580,39 +1143,6 @@ fn preview_generated_files(candidate: &Value) -> AutogenOperationResult AutogenOperationResult<()> { - fs::create_dir_all(repo_path).map_err(|source| { - AutogenOperationError::Autogen(format!( - "failed to create generated repository '{}': {source}", - repo_path.display() - )) - })?; - db.upsert_repository( - &RepositoryMetadata { - id: alias.clone(), - name: generated_repository_name(alias), - priority, - api_version: REPO_API_VERSION_V1.to_owned(), - }, - Some(repo_path), - None, - )?; - Ok(()) -} - -fn generated_repository_name(alias: &RepositoryId) -> String { - if alias.as_str() == DEFAULT_AUTOGEN_REPOSITORY_ID { - DEFAULT_AUTOGEN_REPOSITORY_NAME.to_owned() - } else { - alias.to_string() - } -} - struct LoadedAutogenRecord { content_hash: String, } @@ -947,6 +1477,150 @@ mod tests { assert_eq!(repos[0].priority.value(), -1); } + #[test] + fn batch_preflight_rejects_later_invalid_spec_without_mutation() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db = MainDb::open(data_dir.join("main.db")).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + let mut invalid_preview = preview.clone(); + invalid_preview["candidates"][0]["relative_path"] = json!("../escape"); + let acceptance = AutogenAcceptance::AcceptAll; + + let error = apply_preview_batch( + data_dir, + &db, + &[ + AutogenApplySpec { + preview: &preview, + acceptance: &acceptance, + expected_operation: "installed.preview", + expected_generator: INSTALLED_AUTOGEN_GENERATOR, + }, + AutogenApplySpec { + preview: &invalid_preview, + acceptance: &acceptance, + expected_operation: "installed.preview", + expected_generator: INSTALLED_AUTOGEN_GENERATOR, + }, + ], + ) + .unwrap_err(); + + assert!(matches!(error, AutogenOperationError::Autogen(_))); + assert!(!data_dir.join("repo/autogen").exists()); + assert!(db.repositories().unwrap().is_empty()); + assert!(db.tracked_packages().unwrap().is_empty()); + } + + #[test] + fn crash_phases_recover_old_or_committed_state() { + for (crash_phase, expect_new) in [(1, false), (2, false), (3, true)] { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db_path = data_dir.join("main.db"); + let db = MainDb::open(&db_path).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll) + .unwrap(); + let package_dir = data_dir.join("repo/autogen/android/app/com.example.autogen"); + let old_lua = fs::read(package_dir.join("9999.lua")).unwrap(); + + let changed_inventory = InstalledInventory::new(vec![ + getter_core::autogen::InstalledInventoryItem::AndroidPackage { + package_name: "com.example.autogen".to_owned(), + label: Some("Changed label".to_owned()), + version_name: Some("999".to_owned()), + version_code: Some(999), + }, + ]); + let changed_plan = + build_installed_autogen_plan(data_dir, &db, &changed_inventory).unwrap(); + let changed_preview = installed_preview_json(data_dir, &changed_plan).unwrap(); + CRASH_AFTER_PHASE.set(crash_phase); + let error = apply_installed_preview( + data_dir, + &db, + &changed_preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap_err(); + CRASH_AFTER_PHASE.set(0); + assert!(error.to_string().contains("simulated process crash")); + drop(db); + + let reopened = MainDb::open(&db_path).unwrap(); + recover_autogen_transactions(data_dir, &reopened).unwrap(); + if expect_new { + let metadata = fs::read_to_string(package_dir.join("metadata.jsonc")).unwrap(); + assert!(metadata.contains("Changed label")); + } else { + assert_eq!(fs::read(package_dir.join("9999.lua")).unwrap(), old_lua); + } + assert_eq!(reopened.repositories().unwrap().len(), 1); + assert_eq!(reopened.tracked_packages().unwrap().len(), 1); + assert!(!fs::read_dir(data_dir.join("repo/autogen")) + .unwrap() + .any(|entry| entry + .unwrap() + .file_name() + .to_string_lossy() + .starts_with(".autogen-transaction-"))); + } + } + + #[test] + fn database_failure_after_swaps_restores_previous_package_bytes() { + let temp = tempfile::tempdir().unwrap(); + let data_dir = temp.path(); + let db_path = data_dir.join("main.db"); + let db = MainDb::open(&db_path).unwrap(); + let plan = build_installed_autogen_plan(data_dir, &db, &test_inventory()).unwrap(); + let preview = installed_preview_json(data_dir, &plan).unwrap(); + apply_installed_preview(data_dir, &db, &preview, &AutogenAcceptance::AcceptAll).unwrap(); + let package_dir = data_dir.join("repo/autogen/android/app/com.example.autogen"); + let old_lua = fs::read(package_dir.join("9999.lua")).unwrap(); + + let changed_inventory = InstalledInventory::new(vec![ + getter_core::autogen::InstalledInventoryItem::AndroidPackage { + package_name: "com.example.autogen".to_owned(), + label: Some("Changed label".to_owned()), + version_name: Some("999".to_owned()), + version_code: Some(999), + }, + ]); + let changed_plan = build_installed_autogen_plan(data_dir, &db, &changed_inventory).unwrap(); + let changed_preview = installed_preview_json(data_dir, &changed_plan).unwrap(); + let injector = rusqlite::Connection::open(&db_path).unwrap(); + injector + .execute_batch( + "CREATE TRIGGER fail_generated_tracking BEFORE INSERT ON tracked_packages BEGIN SELECT RAISE(ABORT, 'injected'); END;", + ) + .unwrap(); + + let error = apply_installed_preview( + data_dir, + &db, + &changed_preview, + &AutogenAcceptance::AcceptAll, + ) + .unwrap_err(); + + assert!(matches!(error, AutogenOperationError::Storage(_))); + assert_eq!(fs::read(package_dir.join("9999.lua")).unwrap(), old_lua); + assert!(!data_dir + .join("repo/autogen") + .read_dir() + .unwrap() + .any(|entry| entry + .unwrap() + .file_name() + .to_string_lossy() + .starts_with(".autogen-transaction-"))); + } + #[test] fn apply_rejects_existing_package_directory_without_ownership_record() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-operations/src/fdroid_signed_index.rs b/crates/getter-operations/src/fdroid_signed_index.rs new file mode 100644 index 0000000..b8c4b32 --- /dev/null +++ b/crates/getter-operations/src/fdroid_signed_index.rs @@ -0,0 +1,582 @@ +//! Verification of the official F-Droid v1 signed `index.jar`. + +use std::collections::{HashMap, HashSet}; +use std::io::{Cursor, Read}; + +use base64::Engine as _; +use cms::cert::CertificateChoices; +use cms::content_info::ContentInfo; +use cms::signed_data::{SignedData, SignerIdentifier}; +use der::asn1::{ObjectIdentifier, OctetString}; +use der::{Decode, Encode}; +use rsa::pkcs1v15::{Signature as RsaSignature, VerifyingKey}; +use rsa::RsaPublicKey; +use sha2::{Digest, Sha256}; +use sha2_11::Sha256 as RsaSha256; +use signature::Verifier; +use spki::DecodePublicKey; +use subtle::ConstantTimeEq; +use thiserror::Error; +use zip::ZipArchive; + +pub const OFFICIAL_INDEX_JAR_URL: &str = "https://f-droid.org/repo/index.jar"; +pub const MAX_ARCHIVE_BYTES: usize = 64 * 1024 * 1024; +const MAX_ENTRY_BYTES: u64 = 48 * 1024 * 1024; +const MAX_TOTAL_UNCOMPRESSED_BYTES: u64 = 52 * 1024 * 1024; +const MAX_MANIFEST_BYTES: u64 = 64 * 1024; +const MAX_SIGNATURE_FILE_BYTES: u64 = 64 * 1024; +const MAX_SIGNATURE_BLOCK_BYTES: u64 = 256 * 1024; +const MAX_ENTRIES: usize = 4; +const OFFICIAL_CERT_SHA256: [u8; 32] = [ + 0x43, 0x23, 0x8d, 0x51, 0x2c, 0x1e, 0x5e, 0xb2, 0xd6, 0x56, 0x9f, 0x4a, 0x3a, 0xfb, 0xf5, 0x52, + 0x34, 0x18, 0xb8, 0x2e, 0x0a, 0x3e, 0xd1, 0x55, 0x27, 0x70, 0xab, 0xb9, 0xa9, 0xc9, 0xcc, 0xab, +]; + +#[derive(Debug, Error)] +pub enum VerifyError { + #[error("F-Droid index.jar exceeds the {MAX_ARCHIVE_BYTES}-byte limit")] + ArchiveTooLarge, + #[error("invalid ZIP archive: {0}")] + InvalidZip(String), + #[error("unsafe ZIP entry name: {0}")] + UnsafeEntry(String), + #[error("duplicate ZIP entry: {0}")] + DuplicateEntry(String), + #[error("unexpected ZIP entry: {0}")] + UnexpectedEntry(String), + #[error("missing required ZIP entry: {0}")] + MissingEntry(&'static str), + #[error("ZIP entry exceeds its size limit: {0}")] + EntryTooLarge(String), + #[error("invalid JAR manifest: {0}")] + InvalidManifest(String), + #[error("invalid PKCS#7 signature block: {0}")] + InvalidSignature(String), + #[error("JAR v1 signature was not verified")] + SignatureNotVerified, + #[error("the signing certificate is not the pinned official F-Droid certificate")] + WrongCertificate, + #[error("digest mismatch for {0}")] + DigestMismatch(&'static str), + #[error("index.xml is not UTF-8")] + InvalidXmlUtf8, +} + +struct JarParts { + xml: Vec, + manifest: Vec, + sf: Vec, + rsa: Vec, +} + +/// Verifies an already bounded official F-Droid `index.jar` and returns its XML. +pub fn verify_fdroid_index_jar(jar: &[u8]) -> Result { + if jar.len() > MAX_ARCHIVE_BYTES { + return Err(VerifyError::ArchiveTooLarge); + } + let parts = read_strict_jar(jar)?; + + verify_cms_signature(&parts.rsa, &parts.sf)?; + + let sf = ParsedManifest::parse(&parts.sf)?; + sf.require_exact("Signature-Version", "1.0")?; + sf.require_sha256("SHA-256-Digest-Manifest", &parts.manifest, "MANIFEST.MF")?; + let manifest_section = find_named_section_bytes(&parts.manifest, "index.xml")?; + sf.section("index.xml")?.require_sha256( + "SHA-256-Digest", + manifest_section, + "index.xml manifest section", + )?; + + let manifest = ParsedManifest::parse(&parts.manifest)?; + manifest.require_exact("Manifest-Version", "1.0")?; + manifest + .section("index.xml")? + .require_sha256("SHA-256-Digest", &parts.xml, "index.xml")?; + + String::from_utf8(parts.xml).map_err(|_| VerifyError::InvalidXmlUtf8) +} + +fn read_strict_jar(jar: &[u8]) -> Result { + let declared_entries = eocd_entry_count(jar)?; + let mut archive = ZipArchive::new(Cursor::new(jar)) + .map_err(|error| VerifyError::InvalidZip(error.to_string()))?; + if declared_entries != archive.len() { + return Err(VerifyError::DuplicateEntry( + "central directory contains duplicate names".to_owned(), + )); + } + let mut seen = HashSet::new(); + let mut total = 0_u64; + let mut values = HashMap::new(); + let mut signer_stem: Option = None; + for index in 0..archive.len() { + let entry = archive + .by_index(index) + .map_err(|error| VerifyError::InvalidZip(error.to_string()))?; + let name = entry.name().to_owned(); + if !safe_entry_name(&name) { + return Err(VerifyError::UnsafeEntry(name)); + } + if !seen.insert(name.clone()) { + return Err(VerifyError::DuplicateEntry(name)); + } + let kind_limit = match name.as_str() { + "index.xml" => MAX_ENTRY_BYTES, + "META-INF/MANIFEST.MF" => MAX_MANIFEST_BYTES, + _ if signature_name(&name, ".SF").is_some() => MAX_SIGNATURE_FILE_BYTES, + _ if signature_name(&name, ".RSA").is_some() => MAX_SIGNATURE_BLOCK_BYTES, + _ => return Err(VerifyError::UnexpectedEntry(name)), + }; + if entry.is_dir() || entry.size() > kind_limit { + return Err(VerifyError::EntryTooLarge(name)); + } + total = total + .checked_add(entry.size()) + .ok_or_else(|| VerifyError::EntryTooLarge(name.clone()))?; + if total > MAX_TOTAL_UNCOMPRESSED_BYTES { + return Err(VerifyError::EntryTooLarge(name)); + } + if let Some(stem) = signature_name(&name, ".SF").or_else(|| signature_name(&name, ".RSA")) { + match &signer_stem { + Some(existing) if existing != stem => { + return Err(VerifyError::UnexpectedEntry(name)); + } + None => signer_stem = Some(stem.to_owned()), + _ => {} + } + } + let capacity = + usize::try_from(entry.size()).map_err(|_| VerifyError::EntryTooLarge(name.clone()))?; + let mut bytes = Vec::with_capacity(capacity); + entry + .take(kind_limit + 1) + .read_to_end(&mut bytes) + .map_err(|error| VerifyError::InvalidZip(error.to_string()))?; + if bytes.len() as u64 > kind_limit { + return Err(VerifyError::EntryTooLarge(name)); + } + values.insert(name, bytes); + } + + if seen.len() != MAX_ENTRIES { + return Err(VerifyError::UnexpectedEntry(format!( + "archive has {} entries", + seen.len() + ))); + } + let stem = signer_stem.ok_or(VerifyError::MissingEntry("META-INF/.SF/.RSA"))?; + Ok(JarParts { + xml: take_required(&mut values, "index.xml")?, + manifest: take_required(&mut values, "META-INF/MANIFEST.MF")?, + sf: take_required(&mut values, &format!("META-INF/{stem}.SF"))?, + rsa: take_required(&mut values, &format!("META-INF/{stem}.RSA"))?, + }) +} + +fn eocd_entry_count(jar: &[u8]) -> Result { + const EOCD_SIGNATURE: &[u8; 4] = b"PK\x05\x06"; + const MIN_EOCD_SIZE: usize = 22; + let search_start = jar.len().saturating_sub(65_535 + MIN_EOCD_SIZE); + let relative = jar[search_start..] + .windows(EOCD_SIGNATURE.len()) + .rposition(|window| window == EOCD_SIGNATURE) + .ok_or_else(|| { + VerifyError::InvalidZip("missing end-of-central-directory record".to_owned()) + })?; + let offset = search_start + relative; + let eocd = jar.get(offset..offset + MIN_EOCD_SIZE).ok_or_else(|| { + VerifyError::InvalidZip("truncated end-of-central-directory record".to_owned()) + })?; + let disk_entries = u16::from_le_bytes([eocd[8], eocd[9]]); + let total_entries = u16::from_le_bytes([eocd[10], eocd[11]]); + let comment_len = usize::from(u16::from_le_bytes([eocd[20], eocd[21]])); + if offset + MIN_EOCD_SIZE + comment_len != jar.len() { + return Err(VerifyError::InvalidZip( + "trailing bytes or malformed ZIP comment".to_owned(), + )); + } + if disk_entries == u16::MAX || total_entries == u16::MAX || disk_entries != total_entries { + return Err(VerifyError::InvalidZip( + "multi-disk and ZIP64 archives are not accepted".to_owned(), + )); + } + Ok(usize::from(total_entries)) +} + +fn take_required( + values: &mut HashMap>, + name: &str, +) -> Result, VerifyError> { + values + .remove(name) + .ok_or(VerifyError::MissingEntry(match name { + "index.xml" => "index.xml", + "META-INF/MANIFEST.MF" => "META-INF/MANIFEST.MF", + _ if name.ends_with(".SF") => "META-INF/.SF", + _ => "META-INF/.RSA", + })) +} + +fn safe_entry_name(name: &str) -> bool { + !name.is_empty() + && !name.starts_with('/') + && !name.starts_with('\\') + && !name.contains('\\') + && !name.contains('\0') + && !name + .split('/') + .any(|part| part.is_empty() || part == "." || part == "..") + && !name.as_bytes().get(1).is_some_and(|byte| *byte == b':') +} + +fn signature_name<'a>(name: &'a str, suffix: &str) -> Option<&'a str> { + let stem = name.strip_prefix("META-INF/")?.strip_suffix(suffix)?; + (!stem.is_empty() + && stem + .bytes() + .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-'))) + .then_some(stem) +} + +#[derive(Debug)] +struct ParsedManifest { + main: Attributes, + named: HashMap, +} + +type Attributes = HashMap; + +impl ParsedManifest { + fn parse(bytes: &[u8]) -> Result { + let sections = split_sections(bytes)?; + let mut iter = sections.into_iter(); + let main = parse_attributes(iter.next().ok_or_else(|| { + VerifyError::InvalidManifest("manifest has no main section".to_owned()) + })?)?; + let mut named = HashMap::new(); + for section in iter { + let mut attributes = parse_attributes(section)?; + let name = attributes.remove("Name").ok_or_else(|| { + VerifyError::InvalidManifest("named section has no Name attribute".to_owned()) + })?; + if named.insert(name.clone(), attributes).is_some() { + return Err(VerifyError::InvalidManifest(format!( + "duplicate section for {name}" + ))); + } + } + Ok(Self { main, named }) + } + + fn section(&self, name: &str) -> Result<&Attributes, VerifyError> { + self.named + .get(name) + .ok_or_else(|| VerifyError::InvalidManifest(format!("missing section for {name}"))) + } + + fn require_sha256( + &self, + key: &str, + bytes: &[u8], + subject: &'static str, + ) -> Result<(), VerifyError> { + self.main.require_sha256(key, bytes, subject) + } + + fn require_exact(&self, key: &str, expected: &str) -> Result<(), VerifyError> { + if self.main.get(key).is_some_and(|value| value == expected) { + Ok(()) + } else { + Err(VerifyError::InvalidManifest(format!( + "missing or unsupported {key}" + ))) + } + } +} + +trait DigestAttribute { + fn require_sha256( + &self, + key: &str, + bytes: &[u8], + subject: &'static str, + ) -> Result<(), VerifyError>; +} + +impl DigestAttribute for Attributes { + fn require_sha256( + &self, + key: &str, + bytes: &[u8], + subject: &'static str, + ) -> Result<(), VerifyError> { + let encoded = self + .get(key) + .ok_or_else(|| VerifyError::InvalidManifest(format!("missing {key}")))?; + let expected = base64::engine::general_purpose::STANDARD + .decode(encoded) + .map_err(|_| VerifyError::InvalidManifest(format!("invalid base64 in {key}")))?; + if expected.len() != 32 { + return Err(VerifyError::InvalidManifest(format!( + "{key} is not a SHA-256 digest" + ))); + } + let actual = Sha256::digest(bytes); + if !bool::from(expected.as_slice().ct_eq(actual.as_slice())) { + return Err(VerifyError::DigestMismatch(subject)); + } + Ok(()) + } +} + +fn split_sections(bytes: &[u8]) -> Result, VerifyError> { + if bytes.is_empty() { + return Err(VerifyError::InvalidManifest("empty manifest".to_owned())); + } + let mut sections = Vec::new(); + let mut start = 0; + let mut position = 0; + while position < bytes.len() { + let line_end = bytes[position..] + .iter() + .position(|byte| *byte == b'\n') + .map(|relative| position + relative + 1) + .unwrap_or(bytes.len()); + let line = &bytes[position..line_end]; + let content = line + .strip_suffix(b"\r\n") + .or_else(|| line.strip_suffix(b"\n")) + .unwrap_or(line); + if content.is_empty() { + sections.push(&bytes[start..line_end]); + start = line_end; + } + position = line_end; + } + if start < bytes.len() { + sections.push(&bytes[start..]); + } + Ok(sections) +} + +fn parse_attributes(section: &[u8]) -> Result { + let text = std::str::from_utf8(section) + .map_err(|_| VerifyError::InvalidManifest("attributes are not UTF-8".to_owned()))?; + let mut unfolded: Vec = Vec::new(); + for raw in text.split_terminator('\n') { + let line = raw.strip_suffix('\r').unwrap_or(raw); + if line.is_empty() { + continue; + } + if let Some(continuation) = line.strip_prefix(' ') { + let previous = unfolded.last_mut().ok_or_else(|| { + VerifyError::InvalidManifest("orphan continuation line".to_owned()) + })?; + previous.push_str(continuation); + } else { + unfolded.push(line.to_owned()); + } + } + let mut attributes = HashMap::new(); + for line in unfolded { + let (key, value) = line + .split_once(": ") + .ok_or_else(|| VerifyError::InvalidManifest(format!("malformed attribute: {line}")))?; + if key.is_empty() + || attributes + .insert(key.to_owned(), value.to_owned()) + .is_some() + { + return Err(VerifyError::InvalidManifest(format!( + "empty or duplicate attribute: {key}" + ))); + } + } + Ok(attributes) +} + +fn find_named_section_bytes<'a>(manifest: &'a [u8], wanted: &str) -> Result<&'a [u8], VerifyError> { + let mut found = None; + for section in split_sections(manifest)?.into_iter().skip(1) { + let attributes = parse_attributes(section)?; + if attributes.get("Name").is_some_and(|name| name == wanted) + && found.replace(section).is_some() + { + return Err(VerifyError::InvalidManifest(format!( + "duplicate section for {wanted}" + ))); + } + } + found.ok_or_else(|| VerifyError::InvalidManifest(format!("missing section for {wanted}"))) +} + +fn verify_cms_signature(signature_block: &[u8], signature_file: &[u8]) -> Result<(), VerifyError> { + const SIGNED_DATA: &str = "1.2.840.113549.1.7.2"; + const DATA: &str = "1.2.840.113549.1.7.1"; + const SHA256: &str = "2.16.840.1.101.3.4.2.1"; + const RSA_ENCRYPTION: &str = "1.2.840.113549.1.1.1"; + const SHA256_WITH_RSA: &str = "1.2.840.113549.1.1.11"; + const CONTENT_TYPE: &str = "1.2.840.113549.1.9.3"; + const MESSAGE_DIGEST: &str = "1.2.840.113549.1.9.4"; + + let content_info = ContentInfo::from_der(signature_block).map_err(invalid_der)?; + if content_info.content_type != oid(SIGNED_DATA)? { + return invalid_signature("CMS ContentInfo is not SignedData"); + } + let signed_data_der = content_info.content.to_der().map_err(invalid_der)?; + let signed_data = SignedData::from_der(&signed_data_der).map_err(invalid_der)?; + if signed_data.signer_infos.0.len() != 1 { + return invalid_signature("CMS SignedData must contain exactly one signer"); + } + let signer = signed_data + .signer_infos + .0 + .get(0) + .ok_or(VerifyError::SignatureNotVerified)?; + let signature_oid = signer.signature_algorithm.oid; + if signer.digest_alg.oid != oid(SHA256)? + || (signature_oid != oid(RSA_ENCRYPTION)? && signature_oid != oid(SHA256_WITH_RSA)?) + { + return invalid_signature("unsupported CMS signature algorithm; expected SHA256withRSA"); + } + + let certificates = signed_data.certificates.as_ref().ok_or_else(|| { + VerifyError::InvalidSignature("signature block has no certificate".into()) + })?; + let matching: Vec<_> = certificates + .0 + .iter() + .filter_map(|choice| match choice { + CertificateChoices::Certificate(cert) if signer_matches_certificate(signer, cert) => { + Some(cert) + } + _ => None, + }) + .collect(); + if matching.len() != 1 { + return invalid_signature("CMS signer must match exactly one certificate"); + } + let certificate = matching[0]; + let certificate_der = certificate.to_der().map_err(invalid_der)?; + if !bool::from( + Sha256::digest(&certificate_der) + .as_slice() + .ct_eq(&OFFICIAL_CERT_SHA256), + ) { + return Err(VerifyError::WrongCertificate); + } + + let signed_content = if let Some(attrs) = signer.signed_attrs.as_ref() { + let content_type_der = single_attribute_value(attrs, CONTENT_TYPE)? + .to_der() + .map_err(invalid_der)?; + if ObjectIdentifier::from_der(&content_type_der).map_err(invalid_der)? != oid(DATA)? { + return Err(VerifyError::SignatureNotVerified); + } + let message_digest_der = single_attribute_value(attrs, MESSAGE_DIGEST)? + .to_der() + .map_err(invalid_der)?; + let message_digest = OctetString::from_der(&message_digest_der).map_err(invalid_der)?; + if !bool::from( + Sha256::digest(signature_file) + .as_slice() + .ct_eq(message_digest.as_bytes()), + ) { + return Err(VerifyError::SignatureNotVerified); + } + + // RFC 5652 section 5.4 signs the DER SET OF SignedAttrs, not the + // context-specific [0] IMPLICIT encoding used inside SignerInfo. + attrs.to_der().map_err(invalid_der)? + } else { + // The official F-Droid JAR signature is a detached CMS signature + // without signed attributes, so its signature covers the exact .SF. + signature_file.to_vec() + }; + + let public_key_der = certificate + .tbs_certificate() + .subject_public_key_info() + .to_der() + .map_err(invalid_der)?; + let public_key = RsaPublicKey::from_public_key_der(&public_key_der) + .map_err(|error| VerifyError::InvalidSignature(error.to_string()))?; + let signature = RsaSignature::try_from(signer.signature.as_bytes()) + .map_err(|_| VerifyError::SignatureNotVerified)?; + VerifyingKey::::new(public_key) + .verify(&signed_content, &signature) + .map_err(|_| VerifyError::SignatureNotVerified) +} + +fn signer_matches_certificate( + signer: &cms::signed_data::SignerInfo, + certificate: &x509_cert::Certificate, +) -> bool { + match &signer.sid { + SignerIdentifier::IssuerAndSerialNumber(id) => { + id.issuer == *certificate.tbs_certificate().issuer() + && id.serial_number == *certificate.tbs_certificate().serial_number() + } + SignerIdentifier::SubjectKeyIdentifier(_) => false, + } +} + +fn single_attribute_value<'a>( + attrs: &'a cms::signed_data::SignedAttributes, + wanted: &str, +) -> Result<&'a der::Any, VerifyError> { + let wanted = oid(wanted)?; + let mut matches = attrs.iter().filter(|attribute| attribute.oid == wanted); + let attribute = matches.next().ok_or(VerifyError::SignatureNotVerified)?; + if matches.next().is_some() || attribute.values.len() != 1 { + return Err(VerifyError::SignatureNotVerified); + } + attribute + .values + .get(0) + .ok_or(VerifyError::SignatureNotVerified) +} + +fn oid(value: &str) -> Result { + ObjectIdentifier::new(value).map_err(|error| VerifyError::InvalidSignature(error.to_string())) +} + +fn invalid_der(error: der::Error) -> VerifyError { + VerifyError::InvalidSignature(error.to_string()) +} + +fn invalid_signature(message: &str) -> Result { + Err(VerifyError::InvalidSignature(message.to_owned())) +} + +#[cfg(test)] +mod tests { + use super::{find_named_section_bytes, ParsedManifest}; + + #[test] + fn parses_crlf_and_continuation_lines() { + let manifest = ParsedManifest::parse( + b"Manifest-Version: 1.0\r\nLong: first\r\n second\r\n\r\nName: index.xml\r\nSHA-256-Digest: abc\r\n def\r\n\r\n", + ) + .unwrap(); + assert_eq!(manifest.main.get("Long").unwrap(), "firstsecond"); + assert_eq!( + manifest + .section("index.xml") + .unwrap() + .get("SHA-256-Digest") + .unwrap(), + "abcdef" + ); + } + + #[test] + fn retains_exact_named_section_bytes_with_lf_endings() { + let bytes = b"Manifest-Version: 1.0\n\nName: index.xml\nSHA-256-Digest: abc\n def\n\n"; + assert_eq!( + find_named_section_bytes(bytes, "index.xml").unwrap(), + b"Name: index.xml\nSHA-256-Digest: abc\n def\n\n" + ); + } +} diff --git a/crates/getter-operations/src/lib.rs b/crates/getter-operations/src/lib.rs index d9502d9..63462e6 100644 --- a/crates/getter-operations/src/lib.rs +++ b/crates/getter-operations/src/lib.rs @@ -10,6 +10,7 @@ pub mod autogen; pub mod download; pub mod fdroid_autogen; pub mod fdroid_catalog; +pub mod fdroid_signed_index; pub mod github_autogen; pub mod github_latest_commit; pub mod github_releases; @@ -23,6 +24,7 @@ mod lua_provider_host; #[cfg(feature = "lua")] #[doc(hidden)] pub mod lua_runtime_hooks; +pub mod onboarding; pub mod provider_cache; pub mod read_model; pub mod runtime; diff --git a/crates/getter-operations/src/onboarding.rs b/crates/getter-operations/src/onboarding.rs new file mode 100644 index 0000000..8ba3504 --- /dev/null +++ b/crates/getter-operations/src/onboarding.rs @@ -0,0 +1,595 @@ +//! Getter-owned fresh-install setup preview and apply orchestration. + +use crate::autogen::{self, AutogenAcceptance, AutogenApplySpec, AutogenOperationError}; +use crate::fdroid_autogen; +use crate::fdroid_signed_index::{ + verify_fdroid_index_jar, MAX_ARCHIVE_BYTES, OFFICIAL_INDEX_JAR_URL, +}; +use getter_core::autogen::{ + validate_installed_inventory, InstalledInventory, InstalledInventoryItem, +}; +use getter_core::PackageId; +use getter_storage::{CacheDb, MainDb}; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use sha2::{Digest, Sha512}; +use std::collections::{BTreeSet, HashSet}; +use std::fs; +use std::io::Read; +use std::path::{Path, PathBuf}; +use std::time::{SystemTime, UNIX_EPOCH}; + +pub trait FdroidCatalogTransport { + fn fetch_official_index_xml(&self) -> Result; +} + +pub struct UreqFdroidCatalogTransport; + +impl FdroidCatalogTransport for UreqFdroidCatalogTransport { + fn fetch_official_index_xml(&self) -> Result { + let response = ureq::get(OFFICIAL_INDEX_JAR_URL) + .call() + .map_err(|error| format!("official F-Droid index.jar request failed: {error}"))?; + let mut body = response.into_body().into_reader(); + let mut jar = Vec::new(); + body.by_ref() + .take((MAX_ARCHIVE_BYTES + 1) as u64) + .read_to_end(&mut jar) + .map_err(|error| format!("official F-Droid index.jar read failed: {error}"))?; + if jar.len() > MAX_ARCHIVE_BYTES { + return Err(format!( + "official F-Droid index.jar exceeds the {MAX_ARCHIVE_BYTES}-byte limit" + )); + } + verify_fdroid_index_jar(&jar) + .map_err(|error| format!("official F-Droid index.jar verification failed: {error}")) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum SetupReadiness { + NeedsPackageSetup, + Ready, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SetupStatus { + pub state: SetupReadiness, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum SetupCandidateCategory { + Fdroid, + InstalledFallback, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SetupCandidate { + pub package_id: String, + pub display_name: Option, + pub category: SetupCandidateCategory, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SetupDiagnostic { + pub code: String, + pub message: String, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SetupPreview { + pub format: String, + pub version: u32, + pub preview_id: String, + pub candidates: Vec, + pub diagnostics: Vec, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +struct StoredSetupPreview { + format: String, + version: u32, + preview_id: String, + candidates: Vec, + diagnostics: Vec, + fdroid_preview: Value, + installed_preview: Value, + inventory: InstalledInventory, + context_fingerprint: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SetupAcceptance { + AcceptAll, + Accept(Vec), +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SetupApplyResult { + pub readiness: SetupReadiness, + pub applied_package_ids: Vec, +} + +pub fn derive_setup_status( + has_enabled_tracked_package: bool, + inventory: &InstalledInventory, +) -> SetupStatus { + SetupStatus { + state: if !has_enabled_tracked_package && !inventory.items.is_empty() { + SetupReadiness::NeedsPackageSetup + } else { + SetupReadiness::Ready + }, + } +} + +pub fn derive_setup_status_cache_only( + data_dir: &Path, + main_db: &MainDb, + cache_db: &CacheDb, + inventory: &InstalledInventory, +) -> Result { + if main_db + .tracked_packages()? + .iter() + .any(|package| package.enabled) + { + return Ok(SetupStatus { + state: SetupReadiness::Ready, + }); + } + let request = json!({ + "mode": "use_cached", + "installed_inventory": inventory, + }); + let fdroid_preview = fdroid_autogen::preview_fdroid_packages_json( + data_dir, + main_db, + cache_db, + &request.to_string(), + ) + .unwrap_or_else(|_| json!({ "candidates": [] })); + let matched = fdroid_handled_android_ids(&fdroid_preview); + let fallback = InstalledInventory::new( + inventory + .items + .iter() + .filter(|item| match item { + InstalledInventoryItem::AndroidPackage { package_name, .. } => { + !matched.contains(package_name) + } + InstalledInventoryItem::MagiskModule { .. } => true, + }) + .cloned() + .collect(), + ); + let fallback_plan = autogen::build_installed_autogen_plan(data_dir, main_db, &fallback)?; + let actionable = + !candidate_ids(&fdroid_preview).is_empty() || !fallback_plan.candidates.is_empty(); + Ok(SetupStatus { + state: if actionable { + SetupReadiness::NeedsPackageSetup + } else { + SetupReadiness::Ready + }, + }) +} + +pub fn preview_setup_with_transport( + data_dir: &Path, + main_db: &MainDb, + cache_db: &CacheDb, + inventory: InstalledInventory, + transport: &dyn FdroidCatalogTransport, +) -> Result { + validate_installed_inventory(&inventory) + .map_err(|error| AutogenOperationError::Autogen(error.to_string()))?; + let android_names: Vec<_> = inventory + .items + .iter() + .filter_map(|item| match item { + InstalledInventoryItem::AndroidPackage { package_name, .. } => { + Some(package_name.clone()) + } + InstalledInventoryItem::MagiskModule { .. } => None, + }) + .collect(); + let refresh = transport.fetch_official_index_xml(); + let refresh_failed = refresh.is_err(); + let request = match refresh { + Ok(xml) => { + json!({"index_xml": xml, "mode": "force_refresh", "package_names": android_names, "installed_inventory": inventory}) + } + Err(_) => { + json!({"mode": "force_refresh", "package_names": android_names, "installed_inventory": inventory}) + } + }; + let mut diagnostics = Vec::new(); + let fdroid_preview = match fdroid_autogen::preview_fdroid_packages_json( + data_dir, + main_db, + cache_db, + &request.to_string(), + ) { + Ok(preview) => preview, + Err(_) if refresh_failed => { + diagnostics.push(SetupDiagnostic { code: "setup.fdroid_unavailable".into(), message: "F-Droid catalog is unavailable; installed fallback candidates are still available".into() }); + let (target_alias, target_path, _) = autogen::generated_repository_config(data_dir)?; + json!({ + "operation": "fdroid.autogen.preview", + "target_repo_id": target_alias.as_str(), + "target_repo_path": target_path, + "candidates": [], + "diagnostics": [], + }) + } + Err(error) => return Err(error), + }; + for diagnostic in fdroid_preview + .get("diagnostics") + .and_then(Value::as_array) + .into_iter() + .flatten() + { + diagnostics.push(SetupDiagnostic { + code: diagnostic + .get("code") + .and_then(Value::as_str) + .unwrap_or("setup.fdroid_stale") + .to_owned(), + message: diagnostic + .get("message") + .and_then(Value::as_str) + .unwrap_or("F-Droid catalog cache is stale") + .to_owned(), + }); + } + let matched_android_ids = fdroid_handled_android_ids(&fdroid_preview); + let fallback_inventory = InstalledInventory::new( + inventory + .items + .iter() + .filter(|item| match item { + InstalledInventoryItem::AndroidPackage { package_name, .. } => { + !matched_android_ids.contains(package_name) + } + InstalledInventoryItem::MagiskModule { .. } => true, + }) + .cloned() + .collect(), + ); + let installed_plan = + autogen::build_installed_autogen_plan(data_dir, main_db, &fallback_inventory)?; + let installed_preview = autogen::installed_preview_json(data_dir, &installed_plan)?; + let mut candidates = Vec::new(); + append_candidates( + &mut candidates, + &fdroid_preview, + SetupCandidateCategory::Fdroid, + ); + append_candidates( + &mut candidates, + &installed_preview, + SetupCandidateCategory::InstalledFallback, + ); + candidates.sort_by(|a, b| a.package_id.cmp(&b.package_id)); + candidates.dedup_by(|a, b| a.package_id == b.package_id); + let context_fingerprint = current_context_fingerprint(main_db)?; + let mut random = [0_u8; 32]; + getrandom::fill(&mut random).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to create setup preview id: {error}")) + })?; + let preview_id: String = random.iter().map(|byte| format!("{byte:02x}")).collect(); + let stored = StoredSetupPreview { + format: "getter-setup-preview".into(), + version: 1, + preview_id: preview_id.clone(), + candidates: candidates.clone(), + diagnostics: diagnostics.clone(), + fdroid_preview, + installed_preview, + inventory, + context_fingerprint, + }; + persist_setup_preview(data_dir, &stored)?; + Ok(SetupPreview { + format: stored.format, + version: stored.version, + preview_id, + candidates, + diagnostics, + }) +} + +pub fn preview_setup( + data_dir: &Path, + main_db: &MainDb, + cache_db: &CacheDb, + inventory: InstalledInventory, +) -> Result { + preview_setup_with_transport( + data_dir, + main_db, + cache_db, + inventory, + &UreqFdroidCatalogTransport, + ) +} + +pub fn apply_setup_preview( + data_dir: &Path, + main_db: &MainDb, + preview: &SetupPreview, + acceptance: &SetupAcceptance, +) -> Result { + let (trusted, claim_path, ready_path) = claim_setup_preview(data_dir, &preview.preview_id)?; + let prepare = (|| -> Result<_, AutogenOperationError> { + if trusted.context_fingerprint != current_context_fingerprint(main_db)? { + return Err(AutogenOperationError::Autogen( + "setup preview is stale because repository or tracked-package state changed".into(), + )); + } + let known: BTreeSet<_> = trusted + .candidates + .iter() + .map(|candidate| candidate.package_id.clone()) + .collect(); + let accepted: BTreeSet<_> = match acceptance { + SetupAcceptance::AcceptAll => known.clone(), + SetupAcceptance::Accept(ids) => ids.iter().map(ToString::to_string).collect(), + }; + if let Some(unknown) = accepted.difference(&known).next() { + return Err(AutogenOperationError::Autogen(format!( + "unknown setup candidate '{unknown}'" + ))); + } + let accepted_for = |category| { + trusted + .candidates + .iter() + .filter(|candidate| candidate.category == category) + .filter(|candidate| accepted.contains(&candidate.package_id)) + .map(|candidate| { + candidate.package_id.parse().map_err(|error| { + AutogenOperationError::Autogen(format!( + "invalid preview package id: {error}" + )) + }) + }) + .collect::, _>>() + .map(AutogenAcceptance::Accept) + }; + let fdroid_acceptance = accepted_for(SetupCandidateCategory::Fdroid)?; + let fallback_acceptance = accepted_for(SetupCandidateCategory::InstalledFallback)?; + Ok((accepted, fdroid_acceptance, fallback_acceptance)) + })(); + let (accepted, fdroid_acceptance, fallback_acceptance) = match prepare { + Ok(prepared) => prepared, + Err(error) => { + restore_setup_preview_claim(&claim_path, &ready_path)?; + return Err(error); + } + }; + let apply_result = autogen::apply_preview_batch( + data_dir, + main_db, + &[ + AutogenApplySpec { + preview: &trusted.fdroid_preview, + acceptance: &fdroid_acceptance, + expected_operation: "fdroid.autogen.preview", + expected_generator: getter_core::autogen::FDROID_AUTOGEN_GENERATOR, + }, + AutogenApplySpec { + preview: &trusted.installed_preview, + acceptance: &fallback_acceptance, + expected_operation: "installed.preview", + expected_generator: getter_core::autogen::INSTALLED_AUTOGEN_GENERATOR, + }, + ], + ); + if let Err(error) = apply_result { + restore_setup_preview_claim(&claim_path, &ready_path)?; + return Err(error); + } + // Any accepted generated package is enabled by the committed batch. Empty + // acceptance leaves the still-actionable trusted candidate set unchanged. + let readiness = if accepted.is_empty() && !trusted.candidates.is_empty() { + SetupReadiness::NeedsPackageSetup + } else { + SetupReadiness::Ready + }; + // Changes are committed; cleanup cannot turn success into a reported failure. + let _ = fs::remove_file(&claim_path); + Ok(SetupApplyResult { + readiness, + applied_package_ids: accepted.into_iter().collect(), + }) +} + +fn append_candidates( + output: &mut Vec, + preview: &Value, + category: SetupCandidateCategory, +) { + for candidate in preview + .get("candidates") + .and_then(Value::as_array) + .into_iter() + .flatten() + { + if let Some(package_id) = candidate.get("package_id").and_then(Value::as_str) { + output.push(SetupCandidate { + package_id: package_id.to_owned(), + display_name: candidate + .get("name") + .and_then(Value::as_str) + .map(str::to_owned), + category, + }); + } + } +} + +fn candidate_ids(preview: &Value) -> Vec { + preview + .get("candidates") + .and_then(Value::as_array) + .into_iter() + .flatten() + .filter_map(|candidate| { + candidate + .get("package_id") + .and_then(Value::as_str) + .map(str::to_owned) + }) + .collect() +} + +fn fdroid_handled_android_ids(preview: &Value) -> HashSet { + let mut package_ids = candidate_ids(preview); + package_ids.extend( + preview + .get("skipped") + .and_then(Value::as_array) + .into_iter() + .flatten() + .filter(|skip| { + skip.get("reason").and_then(Value::as_str) + == Some("covered_by_higher_priority_repo") + }) + .filter_map(|skip| { + skip.get("package_id") + .and_then(Value::as_str) + .map(str::to_owned) + }), + ); + package_ids + .into_iter() + .filter_map(|id| id.strip_prefix("android/f-droid/app/").map(str::to_owned)) + .collect() +} + +fn setup_preview_dir(data_dir: &Path) -> PathBuf { + data_dir.join("cache/setup-previews") +} + +fn setup_preview_path(data_dir: &Path, preview_id: &str) -> Result { + if preview_id.len() != 64 || !preview_id.bytes().all(|byte| byte.is_ascii_hexdigit()) { + return Err(AutogenOperationError::Autogen( + "setup preview id is invalid".into(), + )); + } + Ok(setup_preview_dir(data_dir).join(format!("{preview_id}.json"))) +} + +fn persist_setup_preview( + data_dir: &Path, + preview: &StoredSetupPreview, +) -> Result<(), AutogenOperationError> { + let dir = setup_preview_dir(data_dir); + fs::create_dir_all(&dir).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to create setup preview store: {error}")) + })?; + let final_path = setup_preview_path(data_dir, &preview.preview_id)?; + let temp_path = dir.join(format!(".{}.tmp", preview.preview_id)); + let bytes = serde_json::to_vec(preview) + .map_err(|error| AutogenOperationError::Autogen(error.to_string()))?; + fs::write(&temp_path, bytes).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to write setup preview: {error}")) + })?; + fs::rename(&temp_path, &final_path).map_err(|error| { + let _ = fs::remove_file(&temp_path); + AutogenOperationError::Autogen(format!("failed to commit setup preview: {error}")) + }) +} + +fn claim_setup_preview( + data_dir: &Path, + preview_id: &str, +) -> Result<(StoredSetupPreview, PathBuf, PathBuf), AutogenOperationError> { + let path = setup_preview_path(data_dir, preview_id)?; + let claim_path = setup_preview_dir(data_dir).join(format!(".{preview_id}.applying")); + fs::rename(&path, &claim_path).map_err(|_| { + AutogenOperationError::Autogen( + "setup preview is unknown, consumed, or already applying".into(), + ) + })?; + let metadata = fs::metadata(&claim_path).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to inspect claimed setup preview: {error}")) + })?; + let modified = metadata.modified().unwrap_or(UNIX_EPOCH); + if SystemTime::now() + .duration_since(modified) + .unwrap_or_default() + .as_secs() + > 30 * 60 + { + let _ = fs::remove_file(&claim_path); + return Err(AutogenOperationError::Autogen( + "setup preview has expired".into(), + )); + } + let bytes = fs::read(&claim_path).map_err(|error| { + AutogenOperationError::Autogen(format!("failed to read setup preview: {error}")) + })?; + let preview: StoredSetupPreview = serde_json::from_slice(&bytes).map_err(|error| { + AutogenOperationError::Autogen(format!("trusted setup preview is invalid: {error}")) + })?; + if preview.preview_id != preview_id { + return Err(AutogenOperationError::Autogen( + "trusted setup preview id does not match its key".into(), + )); + } + Ok((preview, claim_path, path)) +} + +fn restore_setup_preview_claim( + claim_path: &Path, + ready_path: &Path, +) -> Result<(), AutogenOperationError> { + fs::rename(claim_path, ready_path).map_err(|error| { + AutogenOperationError::Autogen(format!( + "setup apply failed and its preview claim could not be restored: {error}" + )) + }) +} + +fn current_context_fingerprint(main_db: &MainDb) -> Result { + let repositories = main_db.repositories()?; + let tracked = main_db.tracked_packages()?; + let repository_state: Vec<_> = repositories + .iter() + .map(|repo| { + json!({ + "id": repo.id.as_str(), + "name": repo.name, + "priority": repo.priority.value(), + "api_version": repo.api_version, + "path": repo.path, + "revision": repo.revision, + }) + }) + .collect(); + let tracked_state: Vec<_> = tracked + .iter() + .map(|package| { + json!({ + "package_id": package.package_id.to_string(), + "enabled": package.enabled, + "favorite": package.favorite, + "pin_version": package.pin_version, + "repository_id": package.repository_id.as_ref().map(ToString::to_string), + "resolution": format!("{:?}", package.package_resolution), + }) + }) + .collect(); + let bytes = serde_json::to_vec(&json!({ + "repositories": repository_state, + "tracked_packages": tracked_state, + })) + .map_err(|error| AutogenOperationError::Autogen(error.to_string()))?; + Ok(format!("sha512:{:x}", Sha512::digest(bytes))) +} diff --git a/crates/getter-operations/src/startup.rs b/crates/getter-operations/src/startup.rs index c2a650a..72cdad1 100644 --- a/crates/getter-operations/src/startup.rs +++ b/crates/getter-operations/src/startup.rs @@ -2,6 +2,7 @@ #[cfg(feature = "lua")] use crate::lua_provider_host::{evaluate_provider_backed_package, ProviderBackedPackageEvalConfig}; +use crate::onboarding::{derive_setup_status_cache_only, SetupStatus}; #[cfg(feature = "lua")] use crate::provider_cache::{ProviderCacheMode, CACHE_ONLY_MISS}; use getter_core::autogen::{ @@ -55,6 +56,7 @@ pub struct StartupSnapshot { pub format: String, pub version: u32, pub bootstrap: BootstrapStatus, + pub setup: SetupStatus, pub repositories: Vec, pub apps: Vec, pub update_count: usize, @@ -206,10 +208,15 @@ pub fn startup( .map_err(|error| StartupError::Inventory(error.to_string()))?; let bootstrap = bootstrap_data_dir(data_dir)?; let db = MainDb::open(&bootstrap.main_db.path)?; + crate::autogen::recover_autogen_transactions(data_dir, &db) + .map_err(|error| StartupError::Inventory(error.to_string()))?; + let cache_db = CacheDb::open(&bootstrap.cache_db.path)?; let stored_repositories = db.repositories()?; let repositories = stored_repositories.iter().map(repository_dto).collect(); - let apps: Vec<_> = db - .tracked_packages()? + let tracked_packages = db.tracked_packages()?; + let setup = derive_setup_status_cache_only(data_dir, &db, &cache_db, &inventory) + .map_err(|error| StartupError::Inventory(error.to_string()))?; + let apps: Vec<_> = tracked_packages .into_iter() .filter(|p| p.enabled) .map(|package| app_summary(data_dir, &stored_repositories, &inventory, package)) @@ -222,6 +229,7 @@ pub fn startup( format: STARTUP_SNAPSHOT_FORMAT.to_owned(), version: STARTUP_SNAPSHOT_VERSION, bootstrap, + setup, repositories, apps, update_count, diff --git a/crates/getter-operations/tests/fdroid_signed_index.rs b/crates/getter-operations/tests/fdroid_signed_index.rs new file mode 100644 index 0000000..ec96ec5 --- /dev/null +++ b/crates/getter-operations/tests/fdroid_signed_index.rs @@ -0,0 +1,190 @@ +use std::io::{Cursor, Read, Write}; + +use getter_operations::fdroid_signed_index::{verify_fdroid_index_jar, VerifyError}; +use zip::write::SimpleFileOptions; +use zip::{ZipArchive, ZipWriter}; + +const FIXTURE: &[u8] = include_bytes!("fixtures/fdroid-official-index.jar"); +const WRONG_CERT_FIXTURE: &[u8] = include_bytes!("fixtures/wrong-cert-index.jar"); + +#[test] +fn verifies_official_fdroid_signed_index() { + let xml = verify_fdroid_index_jar(FIXTURE).expect("official fixture should verify"); + assert!(xml.starts_with(" Vec<(String, Vec)> { + let mut archive = ZipArchive::new(Cursor::new(FIXTURE)).unwrap(); + (0..archive.len()) + .map(|index| { + let mut entry = archive.by_index(index).unwrap(); + let name = entry.name().to_owned(); + let mut bytes = Vec::new(); + entry.read_to_end(&mut bytes).unwrap(); + (name, bytes) + }) + .collect() +} + +fn build(entries: impl IntoIterator)>) -> Vec { + let mut output = Cursor::new(Vec::new()); + { + let mut writer = ZipWriter::new(&mut output); + for (name, bytes) in entries { + writer + .start_file(name, SimpleFileOptions::default()) + .unwrap(); + writer.write_all(&bytes).unwrap(); + } + writer.finish().unwrap(); + } + output.into_inner() +} + +fn rewrite(mut mutation: impl FnMut(&str, &mut Vec)) -> Vec { + build(entries().into_iter().map(|(name, mut bytes)| { + mutation(&name, &mut bytes); + (name, bytes) + })) +} + +fn append_entry(name: &str, bytes: &[u8]) -> Vec { + let mut all = entries(); + all.push((name.to_owned(), bytes.to_vec())); + build(all) +} + +fn append_duplicate_index() -> Vec { + let mut jar = append_entry("dupe0.xml", b"duplicate"); + let needle = b"dupe0.xml"; + let replacement = b"index.xml"; + let mut matches = Vec::new(); + for offset in 0..=jar.len() - needle.len() { + if jar[offset..].starts_with(needle) { + matches.push(offset); + } + } + assert_eq!(matches.len(), 2); + for offset in matches { + jar[offset..offset + needle.len()].copy_from_slice(replacement); + } + jar +} + +fn replace_entry(name: &str, replacement: &[u8]) -> Vec { + build(entries().into_iter().map(|(entry_name, bytes)| { + if entry_name == name { + (entry_name, replacement.to_vec()) + } else { + (entry_name, bytes) + } + })) +} diff --git a/crates/getter-operations/tests/fixtures/fdroid-official-index.jar b/crates/getter-operations/tests/fixtures/fdroid-official-index.jar new file mode 100644 index 0000000000000000000000000000000000000000..46dd0bf621050799c614ac18c2478be2d10ab13d GIT binary patch literal 3375358 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

F>#n7+F?X)?uC=i3*5R&C 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

E(`d4_#YY-f=?`tOk(R5w>#5jC8$)me37wE9 z0lTOPmjjS{matv88=}4_8hN%a`y2BgmJ)$B9=qSZdr zH`-_U7Fn{-EMdeVj^h;8r0!C^98VWUgbIno2)(ZMS#D8bjr#)9$vMGB#=4oiW><@7 zKx^vX>=z^)8>4J(X^H+$L&)E02z3W$BR|4&Odn$iD9_M#EkwB^J3#asHZxNBg$>0U zGXbxx1Ql3e;tTPqXXECyW9y`V`7@ zRsGxAk%|xOy1jwp*V-c~jiw`&EUtQ(Z33OX~=50`X;qX~EliKGl{o#UyLqP;In z^MPb;ljUUxlOC)WI)O<3GbU7}r%D<3IiG3xAOqsRF*86Cqwh7HcGOJn!I}meFP#*c zxaD$Uxq83cPAAhC2aa0-vl=v-s0`jXZ!hRK{WX{eMFeqovBckBW5nSz1w}8!Mc-MIA9u}jkYSgZ3w%SU@Vxut&nFwRMJ!@}L#E?ZaZV>gs z^MS5y*&Z6N?QV>Kp&llyXZeniP%5>ZhHy0EUr059{}5_{fa;ky5gfhz3|VFaz67}+s^{03KL-rg_z4*jC<&@bIVk|ro)&N@|74 z=b9o<2FQ~EvZDb~%;8;3k=k9|GX!D*YC{Wa75E`=E{$Cr{&euAg@}I!Rum?PG{!*^ zz**u^4uJt+0`@@cebDhTt68zgYWI;v>wjURM3}+{UfK>~;l|*R5&}RXVNwVbs^xC* z!x<&jR@WcYSKT*6NJa~&F~x2er#5nu4TP)(QYLjUY++{wdnFF@0cd6olX!eW#I=cp z*2h)}N}s^~LHep@s60wvJ&K`HX?6WUeYJz}0+nxQW+iYg!hVTh%BM)Fft9HvU?2Oi z=RG$BUj!tyIQ={Xr;A5Y;b2LNk|0cKeFg4c&-1^q@v?JYZ8l!Q<>1<;c3jNntI3tL zSZZI^u-gX7d(msPMx|wne3O|nR0US86X*}1Qc^O2qlSqfzI4{pK!}YMTqr0RlcF%3 zEs$B5Ou{u&h7i%Dij1&Z%7V!Y60RT{Wi`XDBLUIXg0sQ|LWT8O$VOT$RX~hYgvt1r z3ox()0uH+a(kfIhbte?RslfLmB4F3v!1fUniWzda?*P$jetAHMM-vqSWLqn*Kuj{% zhs-hT~0=)OeQym|X-aGNxtml0^c4#ikI|EK@sdQy)H z-pU^bv#GeA{3?ay1wFDQET|8RfwK*ltUHg!#IBy#4EfUAfR(lB{{dIFEW{00c0W0~ zcA9`7D>Y!XK$tKL(gYrDFvr?powO)V39BrYx0E=sVC{fk%OJu45d;oSmDR{0NZ-wM zH~hx6*Vwl9t}%TG&vroKXW2*s!#UMRU|hKr*#u7zmft23!v>&M=7g^~(l_j`)Qzpw zqPpF=8|}{B*d3wgx~6A`iH#KT0KS>X4MC~ox*<;cu@wNR5Ia_cO#`TNvU0dP0fP-= z7nk~p|K+>U*)xNmOgFYB6X;gwZnQdgV|Rje3&(^Quth`n!T^7}5ivJgBxnH7W`Mjw zgGklGDAb(VU2|uR!$iErSVAr}XdeOQQTHK*(-X`mHXm$6bW zdgkD=a<_zr-CYY=tQ0$>dQH=G(6a_FGX2Tn+hO@O@>Ky0)w2hHc}*iF)8-76OdH%> z^M#a9+TAZxwDo4qn{uD*h;W*S>FgRpU;77>J?@IVa$y~SPMZp7#XFrrLGKW-wYp`K z6}DZ_=I-upFbnTzAz!sG|AaDDR)W!%r)AQaaicY4ZnK4?EY@%YjmE9!klA`f-Ndm} zoyH$Fkt@|N?T$$R(^-%J`~+1pfUqJL zS^H6xdO;i$6CQH#V&v#Sz1xUG*NmWt5=D^<=t@)tKq2W5+u-ANes!mMVCNV8{J^dD zW^^|i%%{^On%IPJCRM>Y7H{jyG#?$ zf-jYdlxit81j!S4>GW8sH>Q~^ zHnu?$qMa^C9F!(v?~&x3RK&q|}}OJKWOTA-8B)7E;sCA7DdvMH@SyQgkBWkvjo=d-+`%^zfK zjcgemfZ|y}Ey;Z?8(iC(gA~1%eQv`Y-M2ern+HHyx==NUTqgz}jjkKWkOd1fzycs) z20p_C`<3M)dLoePrvveuBAkM_S6YLNgMP<$tonAxoUg6;%U~PcS8f4i{3UYwQY%7E zFT55`1grou<-rdf!V~4#anf$ivh(82yW>lkNU!@Fj<}zb(YKSA!)iCR z{GErQzw=P+4n0TCsO?~!!E%XpUnDUSw}DFmZGvs0uLU42K>S)xV(LHv+pcFpZ^Lzo z+dqup*s2aictec!_|InyMSag8H{{b$eE3joKk^yg&O_1Lc_?;ArF($|A9#wj%r+gE z`9MdRgk+=W9`q!@E(U!=90X!L5&;eYi2K6}=YUxkSgHWXdr=Y7XsZiMS$(2}h z91f-1FqBvFH~{Ie zgA|GZfT4Pc8CeFlcn4`?U}Z27N=!X3)1n+3CGB_85F+`hM-D3cyn-Z`oI+la7^-F-J$g{ z2Iukh#j#wE8h)s)Mh??Cxf`hb9Co-gu0|+OqY`ULH4>@-U3(xw`GmiR5CG>WFIEUZ zP}=m-6!RYgWg^z&7+kC*8UTaa9N>Wxbq^GKRe_nO!v^ZI2BVKdbx^$zSvtV?{Py>N150TSI2_L1Lya5gkD4G|1spz59gK|sF0HXSDmN_Zcu zRP)(Ftl+t^jug&h9^S`jekyNHFUSOUXj;r338Gz~5JT6-sN5JddJ!BtlJa0g^neuY z`6NH|HN!=^IO+$#KKt2m=SMU?m6!tJD=&`)&EzW$w1@D8qD16{jS0O2FVJ_Z+Zk>` zF(RCilS;asES}U1$q8?7r?|o^zCqL6j`&&AnPMuO&4E8=>c0gXJo}PHf&(+SDgp&z z_43;(wwjD70>L4qanG|XC|nmZiQ)AKV;JEw0pHMav5$j4-dYn^R|~YiZ1g+qV$yR2 zBE)-;FpQ^*arjaDzyIAo{pbJZ|I#SPwOmq=>#JmrVR?`7as7-#U-IigFCX@5hz^h5 zSUkw&KF2?npMWnY(#4aoRQ`Vz-p>9xKHc)~zfzMF^Ob*~3(61Whj3sIq!Ek79*|K> z47^(mGeBYH_dE*kz)whBal0BXM|-y*qzns3SE)+F@K$?6v6jhI*ifUzc)Gd~lV73e zMKUHhOk{VlF0dmDbb19-X( z-`QdWH0&K8+8`*6*LEjkb5g)+VWevF5Q7zBe44+fXXj4n1w{(9_*C*_f6%%n# zWQiF-i0RnvuEgKXzUm7=*~U*y}6?W z-_%X!4qe*adWWis3zq@u2}LgA+kh5DCN#ic7~2uj9FSn911bhL2U5eJZ;QAis6{{| zVy0k;h^wBD00Q$n%JOaNQkb`<{o{A(7N)W3;1-cMSb+lqw$9aVq`1bKe_X?LS20#Q zu#HinRmSZa8i_Ap^8=YtcpZ5)nUNgxgC~KEY-Ei^ND`xcNb-@vhP+`|v|yg2^(=ZS z7dcK9$P(<#C8dCgG1{6br(hUkW7U+MJs#aiqnj9?2)Gn3ZgK?ENYOC!<2h(2^V7)K z9>tUqmw6&Gm^r@?SxxoI!e)PeO|jBwjvy_k%Ap)0k!P$Numn3tR3tMpc zjPBO{_=kT`zK=;^(^4DENBoH|B}f)owSsL(dV>W4$Q9Q(MTpf$tuUap{Ysr-Zc_gg zT~l;O2EvDRmq=nVNnpi{YaE6}kd+TMini=N%kt~>IL!}fg5MDr8JW=R?qLG-5|uZZ zhoN>o7wO;1gG**Qg{q(Bw@fXUeM1&y$mCn}P&}ozkTOonSkCWdshQnY6kQ7QmP#+8 z!@Gx4U#K?hAJO@0I=h8$xW7jDUA1Pjdp20I+1t?EuD#7x$5q2UuseldM0_W|RZ|IV z&Z?JUo`6{l9d!Cn|Ap3}x)qyd7yK$)s7BLPYAtgshTgKexV6kyY`oQG=(Mb)AFMWO zr~QWR#w`*Vf(Rr8F(6-{L4q>8?>eBX!AD4ZxUg`;NLa9GQ{dyH2r1cNY&zgCfbj!P z?^=qr=R3}tf9o@|l-4-5B1oXbp=at8K1*B=G_7#Tg%B@_($F-mh8=PXH_fAwAMGYk zW3~eCTXh**yWHG$jC8l5U^c-z3Js9CgKZ6*?v9B(i4@X|Cah&W26!w;9RU)f7(Y=W zB9!2O>Gr{SghPJObiv^57FnI3nScuo;|tF9U}!npH9I4*aO#cO=s*2j1hyP-L=Eh&vT4yHr27` z4Dct!AHYKg~_TOt*Mh0e<7TzWi9izqbp1hzZJlFU?AVpmz_v_SFcVbQkwY+9Q z*RU;pGg>}p;~8d5NM=jFuoB5{zNBvsPWakI9p~8oc1ZWu{YEljP8z6HXP8%~oL8W7 zEt_7ioN_r+W%bEqzTaC;_i%RTlZ_9%TU1^+GxIaE|Brw84=8{pHF}ikak)XrcY6(A z_ulVUAv?M=7&mG36YpehiCv;38M!O3*82YVgy zh<*k4s919Gf|)hXkqxwJDX@|wCdt)~*a}h7!qBG^(J8gUY&u&Zf~6j;7D&7cog8us z4n);&ki{p#CgwSg&S6#x1c}(ek->w75N^>-UXIBdJI93WyFBc0OhJL z1eTpe&NmGKEYN+^w^SyVLs>eLt6wbm*nK5spE6MMD-0sZH}_gZb95x�jGf?m;m_ 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%0AIn1={fTJ)^g8vsu$}bZ6@Z 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%0DtdK79OP0JdhlBs{;G8=<0R|BP?vN&zkAXK^? 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

rI#kJkP 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 z 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 ^&lDb8dhhoy;=0+7s4EIsy7 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 z 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:=xI`l{O>Tafzr^ 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 z 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ê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%0D7D&=`9U@L~AyI4q z!UlmaT}Q+vjBH4Y`d~@NqU|F6B5)G&Lsniq>8RYf#lcSx3F-#u&F1j zI%jVI!jdPP;ViDQ~$T)h6yfu|P%aA@2Uqkqxm!qWszGrbwureTeCJ{=)splu7 z#m|vCiiD3}NX0BNM`4ZLWaYzca5Z7GRE(jlxT_>KcZtz^zN_>z`_N_oA`AG*)#B&z zBwA7wM8s77Zb09$UK&;XR_bZ>W%lAEfqomMmDJXxDj5Hl7rSo{U!V4lk4_HAwQ)S) zKlyF*$LC+=PnS5i4w5-162&*4BnuYZ+xFN}?omvJo$d1fR_@;#L?p+pv5SG*lOwm6 z4c#_}mB!mw&neZgiMQe0;J1mFG^tSgYn+P>bM?|+<6CUJr#;t$;TRxxz@iPRHaMUI z`0Jp#0q_Q3`}i{i)2R>2N(U}%V5cE#8JiH^Fd)`p(Y91JmkbT?>g`x6H^HRU+EdtI zkXitd9~Tlmy6-{Z5(x*OsdJJb(8#auH%IGV9i4Ebowtgemv7%4USOAalmB(OcYvu? z_`H95a&owTQTS%CzjsoAL9gkBucUfmYHI{4>Rz;k=*8MHvv+^Af@CvOace#GZj+Y# zJ!X-2k6F}S$0i1g9T1=%G}--F_cSkvBP#?SisgWe%mSA_RHhL1aX?xD`f8AFgy1K$ zBXEoX;qR1nY`~9hS?*ShDI5IrCLcQdAMGr9ngI0^py}vh-(v#U_m}|f^S^Ac7E|hU zhye~TmmeTnr8Avl=*2>}9c(EvqV8s*31s3p0;nnjM6^OA9+=z9=M?BYtz;v*Yev7m zb`M!GZ|^z!n+N(s0bKaA52yTsgsSg?K>n$6y6b+bnD1_7c2~@I3KI3GiE7Mu6nUnK zESv3nQYe^UKr;Z2g!~K0Bl3ka$xQLy3c+1no%^eDKNp4U49Ud7j4l+pfTlUoq!0#P zfuzQT2TDELDQ82(Ln6OGsDagVhl_q|37rRk;4|e|R4IK`T(Qxt&0nGLXR6)Wn%rVz zDO)<@H<#CfDDP-C!KrE3RXy|!igV95dEhr&()Fd*JKCEP4C_YYWh0$h{nhOgvtcnK zzV0ElFbHi8`LB@x2o?erZb&?&dgvyAm^ui;1B(M>gereZMiKREf1GXY~*{}YP9KFjkfl<5TNHFW`ey7k|)Tgh#edF zH4RQaWdHjiFeXSN1xK0k(PnE#l(SU|M3K z<^Az+9K*W>ctJ8EFhCCkWj)Hg>)|bW$fW!v@_TpWj;~lyc=rtmM=_289v+LG4(E#o zqa9F;fenRF5NdKL7nsZ^#Wt_@;g~WSan=5Q@8!qd$v&Zm`;rGu?tfiILa*Kc zg`}-X?k^7bOG8_e<6pV5eZGmOTaJMI82*KGN`TKBSWM)cz{uC3VL+PDh;5UWY)B-v zksg6;%7LmKv}SQGFYme7hrtg}Sp_)(;C--OAYR0>pp$Fp+C&~Pzsk@ z*=*e(TZl;t(CUYfpwSRH2fv%62@kku@V12T5jz19ZiKcC8w4UWj=-#fMH1TxV*+I2 z#nt|>T|;*tu|Mj}+^zd#GqQ7hce5ZyN&NeHI?jQdoCVB?m3wZff9TzK-eggn2z}^Rax*YFeL@Q zT9?S)mQ%@55mPePzx?B0|JPl&cd+-d`|AAgkgC_cIEM_kihrHG-8&iJr_wF zl_=JK`N#kMpSow~NALIcKXzY!>>eBrAlcpNFv~3}HD^-R?^G*%ha4{O%wmw!Tuhj)SIC0c&Pemepi(?4F!pt2|9wXcv!Ea#t^^uRH^C=L z?h^rJl>vN8Wl*S-E5k&2*m6Rmua$In4Skw2QUKaX`E*2WF+*ZnN|Tosx`601AxLk? zDMK0v*OTd-NrUnFO4wQ6kAH{=`FbMx08!JulLLulr7s}Y+&u*S0=*SA4~PmC#f5wz z2wq_cN#_%PK~AsF>4@QX!!Orl1QYoNNN_0vjjuitS;MeMUjooD_v0V`{-3%yyYM(V zK1DC?bWcwXyL)G6`0e-qjI=;pOeBOax_dWc@xw6q@Vfgf8`IBwVL&FZ0p;kan!kJt zIDAELljih=Gk|iLt&p^?%d4IDNX-^mC!3$!Fr(0w62qL% zWw1UHvBrd1K`!7`?w+sdP6Xk>pgWEwL|4{G#OX-j#VNg&)@4Ep$g>FHG!!@*>nLG; zsd}8-$ebwIgNQ(y?Aq)bWH6496FHY`xx5bLqmh`AiM*5hDknjKI7)y4a0X{64HTN+ z=?vyIL{=nif*azuAsBO)&rt)cFTsfC^WXfh-7~BzdnczSACFJp4c>n2emJ^#)7?M+ zcy@8xJw5O4?+vh$sFj{LN~Q}15|`#?J%GVpqN(|vY$@dlrdF_34I?z6u67EZtQ&m9vc}LS5#FIOB!md)&d!|pVBV8%hQ1Q8+uDbR9?_e zc{hx(^DMbLvoB=x{dM=ln~zijy6hS#Vxd53ltoU>#6^yLX8Kcqi)3Y<3~1Mq1r znZUD~Zpzq0M#1eoUC(&e=I7=+twiYaIx$sv8*m8)%*!OFk0P)xZS{)A8`%aj)jDnR z;#SN}Ba^&|jg>MB6vL9N!>i>;E*4|uE_lJne)TqmB}G~k)ZIALav%YPauKffGno@Y zC;u1O&cp9{OKnI~{0bSl)H$VPR`LdMA@8R2D!Qgfe3DEo*1V#0elkz_y7XgnI6DO?_w z0bu476Z0%LNT|esXz%H~hA(98slBm>;B7fYNP*@k1xot7vfX{&4J2Iyb>D~>ov2U# z+y9LJ|DU^q5AbrNb8M$*C96GD*%Wi-L6qwz);8tTtY7=o@~BA4@+GiO_b+}Bvk|93 z4T7sY3)YWeiFp~2Kff?Gf!y%T1lkYC@U%4QBK$hBstb}0xiKhT}f!ycPPjp~UFP0{SJc!A(Jf07{Qjx8) zC|^j2G@O6HmM!=I$w`P=FH_C__J8XlO43EVggkWNV^s zo{`Ohlsro`P1|xN;*Kq6BJOC9b zbb@{eybVwf;Qxse%Qd5lFu4T`1Rn1^?o33?L4TTwPcyOgOf>s;<19pO^)^$GFK<8p z`11DncFVVY9h%gJ0bzEX@c2p!Ub1V6n_aW!Z&B84`97^ok|B+sauh|MCj7He^3 z@s{B#`ZW&B2Ypw-u5e%*-qWjzK zg)Fe5|H&Q&4n5Gxk#r@03q}>RcNNF=l0!oi3gApWEvM=x1knyiDE9t6wAOtpEy#|(4LY~#DN-IkvFP9 zI-DvpnGVZqA_|hQGaJ{T7o{>Lya{k!f4av>@dVDat99vzH5atbI~1SsU^s&iwHWPY zz1=3|ut%0vMq<0#-iObP{NcarzRHST$r9^IIytHyC2}Q-NAE4g(0Y4Iia^1Ej6!<*2pO+Pf0hbo zq+PWnKQF^{f$`+MgH z@;7FOll*l=8HaixBBdf>fo)qEDl{jNOnPP2SMY#Hd$L!J#**R*UXuM!>?CcNCuBsNPUspwi?diC+0$ z*8P!EGlh5M7r6Q&ce2XQUY`xJ`22G!k2*u5B{EBl zyDQDr%UK*TXy^t+4N1z0Vh^)e$+{Qn!izxn*$o0_b1AO_Y|3IJQWOeanncjWPQTyh z3IOp#EbdA08MYx}DD)kfQCX)}+bHc9#`#EOLHd64jwFFkBvE7I5RFATJb5}$PfW#x zkZJ@)BNIA6pd^sE31(8nOQz$3jQ1X__c?K5)(l@7dKfQ^jVcV6{{0x(a|PK=rgW2j zY-#`(R-G2wxMl~wmc^BpnPJJ7q`iMw6b52JGRMrRDW@Mziku}6EW<3gik^} zFo5I_;}FciW)xddU|Gn*2q6svW@9*o5N~lwciZE8LEz>ZK$nNaIxs&Ox6zN3{c_?kB3 zcKm8!)Akz&pzd!m0Cj(h0jRfUxQW6rv5^H5#zc;dJfRo|QNaE^NFl?iX~dwlL~@X) z<3M46*Ai(Aj)o{5sLB8(kIT}uaCc&GSo$LlK(k_;{Rju(w-`lE^C)t*8AZ-Eqo}>& zHgd*dM|ctBt8^2J+c8L5Y~PIo5br^?)ke}0P8o1>BpOZ?E|SYcLVDZ6N4l9uu9(s& zfYx9K7{v!IZt>`(k*&D2D&RE@D`3w1cBR;_%zZAN>Nn=?#z*a zDn*1rhwvwp4A3i?>_EW9`E`n%q;uaPX(pkBGeOop^gM5dF{_5^5W+wX#7bMSW0+Ms z`B?;$W?T^=inHo7P;;)ph-9#iIURw>ykW*?Up((h&hbU@eLETF$dqtgV?{i8D}i)r z8i}mW9XB?EFuTM9((m*L{Ws-g4}#JL>X1W}>vSny@NO;-2`u3WSNTGo>B5=BM*%0y zSq9XLd=1^RFJV|S!aDJFQj1j`Kj?~A=|M)_NxjLiADnw`Ddh;wI962ZIKGfKzrUi+ z^lq&FY5-u-%f2RP*SQGj5Y|Ncw}p07R_4}@`0Gh{MbmT~?5f$?E6&yy=Y+K-Yze|z zU)DOZ^8C)6t6ZKm@&bOvi;`TRvU5oJ5TUin_ud>T(AFh!H*B)&7tYHj@YSZ0&^62rQrSZViVNOKxP34;%YcncpjZBAU7&v%|*JY9#$lFKu<+@ zt!+|GH+%i$W>Abg!#V9Z7T7IN6W4;Op)D-D1Vm61NS~N(BhNJ^*#jAGDF9sj{Ql_W zIX$U~!s@Y2JsN@Aa;j_?O8Wgq;1*72cxr6`8NlTO=6hrcL+FW^ODvEJAoLIkn}80s z7Foa=ML`H2A6%+O8qN>C=^K?1=wQqk&ZCb&{b>Z=Po9Uy#%FS-(cYftaF@QW*oOo) zDS|Fn1OCz$ueT3Bv%jru6)MVH&WT_LA=?>91h`_Ycgi=+^1@<@r7u~Z73Y~a%|2R@ z&P<;rt*R8j-b;`}NQGo#=|v4q)_WoKNVzti6FCJJ zoT_J1L(VAM1_Mo9oj#2lq#){!X)hBgNa9Gof@%N~B$66C4A&ri(+Mk)lA+n@?8)#! zR&Au6!sW}kbrENRkny_cKBNAC2#gtYr?QwPe9=Sn>yd<)R z{_IGm#8S0cOh^%7bB$lmf+=24oDR^uDJ-AfPGr?QrSvS7?}g_+qwFM@@Nu4Q@;u9w zqae!_(g@+;6e_udlT%3hlv|9NgRlnTZMvH-Msh7$ zWBqQveYe-r_-$2Gi#!BU+}nh6A!W@rDx^(N7t2}$lq{{}mzK2#Bw4+*J&m^GLnKFw zEuq=}KYMT28%dI7i9HX0MHB|uX4XUm{re+lcDk4(Gm~A(Om33dH9dnx!avEV3`TIn zBQlfq)GICYBxwN6K*FvBj5L6x1tbX2i!c(P7Y+1B3IYT@?fe9tbIsht!y`OCSgh{q zZqD{r1|!_f&CRcw+r9VPa~L0@x_#h7xf%jFfHZiy>6sy`0|1M3+#GO9C`1C};Kp8J zo6#bC!F3#LQg?$#+M9J68quMETNEqE^AP)y82NH1cfg~B%?w}|p|M7wZ^fpdWe2bp zeb{)>S9y*SRfy82RQT&>Mcj`xs{m%wJR!0{=dlg);@I0;i@m+IY|owp5JT#upsNB& zbe>@c307P?g8WPvSa=u+QlrQubxj5r0k;kP0Nj3g>I1Mqqj7$GYcZP=>A(Nh(#Evp zSle5RwY{}$?*SW$!x)ISXF0^Me8}dic z{xlrqIgDj%F`b9D7W4PmS~eqxKcHkX`-~@CJR3rkdk!3K{LpYCgqEE!_|yc<=xUmlZG5*%MNy@TV^i=$VErx^67ojMaUw^RegD?+XY7N(v*7 z-AOtY3h+6dlAMK2-~hY-nS{T!F)cpaFZTA1DlN|W6VyHioCt>3rJndev0UiNd^{G$ z$k~^Q9$c{+6xft!`0a9h#jw5bxhURna!K(=@PTL0584+R9vI38wwpw^!e5n56QG53 z(<*BJaXbY14^LM_TpH@c0Um3!EhN!!Lplnaal;0FNq`mS%cUCJM4V{4%Z%BVPGrIR z%d*nkuz(<|k`&d&w9VcsUr!8Y|M@wnqK?krVO-HdxAOs%t_Owgd2Jeq=1&2#iegpE zRoX#aLa_gppsbXe^WJHc#BJ3(AT{2EB#r1VlH-b!t8_w(FKW3I+UbpT+4tz=Oj(uQs53fLM&uc#M|^ z{x}_JZCKqs0l^_x#sl}yqPrMt#DxqwTq(G`Qn_7YY}wm8C5xFXLvXr3GPH6vMjSwf zp>*5T7-LoK`W(E)k zsMiRb%(pRP(xirQPRKTfiwYdF+imef# zJP<{JR&1ayFaxxV@A)W0ac~yzSRYuSwN?!2ZwarpSwRr9euVc^zbKMhJb}!dYX>g< z)Tnw3MEfyl+r3&+4(+TCf)}heJBn_eN?7k))5YKmyn~i#Ob3c@vZC(9N!+8{s_)B0 zbUd8XyAevDaobX!rkYQr`Hqs=SlA1n3aXndF??x$9FUR?awOSUy}t0m%v;Ru9{g_?v@eWq?i0W(TcA-#(!yQN;!nk|iNR_fg>fMIOkG|f-#dgZ%Y z?pR2B2F2BkBt2AW5oou1?>GB_mq;CIK4*f|CVpiA>Oy?ot!^HN8shWcz1enBE7gcdG-7$WC^*|P(vj~atn^hp%CRF z+Kd-pXCJSG1T%R0gqKEqdBjM!rkA{eJtN?Wwh!N3zBxk%?5Okn@CfWb@Jf%E4_mm$ zio#8}p3;mKP|P2ckS{IWPq)5BD>34=ZT5U9FlJ!ik&IB(VfmqI0lhnW_v-Q#u5$G( zgn9HSuq0*tJQ87#aWF~s|k$onOB~vFi zvcu#ij8u6!{lviymMZl3;^pDn09-($zY|4|ck=32Pf2v7x+IkerG>INNfc1JQ_)T7 z(Ho4C;Y^ALSdUOuC&3X>LYe^%mGOrPG-&d_P`KF@FdfK_b3O1G7VmU3gY}d_F5_tytCIlM9Y0<3>m8#Dborprs8AT;~ zryM+~yAaeF=ViZc*Xr4*A{udBCC67?ozvqR6y5Z`Iem>n@D~en;*(7FZw9QJDg9pR zUv$165wFH_=YnpT!lYq6`SQzG@F_;J#m67A!hqE3HAur=o}MqXExAs5)M#6%@M6$y zW;!odk$7dFS%_Q_%L$<+5o=(T`j)Od2LTL!uko_)<6TISTTHp05Zp~`88smbRPPMT zI?aiNtt+O_P}i|Y$b-jbWQNmstKK!_5uU-*stDiWnO<3*y?J*Dy49!4J(K&BR1Din z$%eOw8&%6V6G-#6o!Zyn)EEMqUdy1a%;L}GlVlI8`IE0z*mo$K{CC(kaRhw`*j z?w!ky=zjG1tgD{tp#pvJZ*-XSWwG)v_7~kPH!pa2yd&^;nl`v7z*{Yk@>+|e+=sR&e*gT5$fEM?SQ8FvT1HiM zB>^CLx^E`%hZGf?dUW$!s`)pKJ35&6j;{s`8rUYpzz$(4pI|K9a+bu6W!+TiQ~td3 zoPMt&iW`k(5r*ga^Uk>>|2lk(c-miJ&Bn7xaeE&#)oDbbO0Hi}CuWpwD=JF4r)BWx zt442?aqWrjoO)x*mW!{Iu6kwR)z@7xiaJf@c!IYoNCU?_T5YoMr3^q;eu%n_ZDF_i zS1p(DR;7VC-(Yuv6;r)+;J$a2QR{z7u&D$YKzthwlJ0!xx_7^NEwwMMxzXLFv~m-7 zsW#H9i?&vcHa&&nK#vVZVx!mHZeUya-|eTjdg9Hca0yVk#+WKtC6}2a2E-+V_~(sP z?avMxzc<`{koDkUy*=t}!eY9HWp4KN>&>m!`7~PVu2*_!?|H%+ozK-LY!Af*V-^Z& zK!4{!%GL$3LmDJL_#EKC1J!1LW((hjhMA+Z*|QwNGQA)+T{|~3Gg_>HfK-U!KeKvhgWYV_eg)nI{V)Q2)dk^kiHc`~oX{{6#Ub)$(CyC&#CU)mKXp zw(Z2kHYmxO!5Sr5eMu_F{un#}$};hOiPqSrxvOoOyV|C?y=`VLs#*dR8zz2aCX}a% zeG|k0K@M(4BhG@{M8wInqaaEGNag_)X_=6vNx(XR99P3OUC%WgYbV<@HVM)oUgAF5 z<`1_`ht*<`sDqWX8mF_XaXPyir?ZQ3rcgr)tQ5qfPC#5x9J?SZM0Hl^_)(ljh>BVe zada$TL0sZ?y%_8kh@WDKG_$A?9R-UhY898{lDu_tN&Z@)#%3t*(K>gP4qYEi^25eES5v7b{Bts6}A8T94lz{Erex`Flk~W zRiCsU+7tQ9*58qG2A=plB8i+n7Fcc2(Y2m&`rUVTZ$|4|RfFj6w}_Xp$F-(Sw6PuYMLrbCtrDQ z31U`NH2vM%S1Ry{iVVbNB(}tpq94F&6ck3X`rWMD!=Za(9 zbH&-dnj%a>D+gbF;AU281z;5jF>w>yP7*McdE_IGeMGVoEI1aK;u8?&fQT3r1gN|) zntTGb?INV}xZ*snIFBpNZYxe1U3;)K$Gqp7W8QPk*(EvQ01KPu2%KUqF;g%~BvF>5 zFbc#BX$lf!a9+pw24p6g6=Me!L@vK|@-TvSMH3zC*!b~r&3RmN9@m^5)|?&lOlIG1 ztsutqH}C0GnfG+6%B0n-sOojwXL!Tf@v%N%laC_7IB&;bUXmxDCjOLN;! zT?Z+sD6>JG9-_X~ZKj(na0c4;gG(;hkIT&t%guwXEI-+WOlB7=Pk^UQa}&@pXjy{O8*)deQB5;&*gIYV z_8E=CWx_Qr;zC+wU!CpI0gV2b9SEzAQJVke4fMP7r$R7=JtBFu^t_7x1;0# zVp-u5AZ*~St~rUoAzVJBr$U~z3NEL#O<1rBwEVkM#griGokIRS zo03qCbSm@maxt;0R#?i*vm8BuAR{VdIxi}kulBJ(UArL4yB!4`@0MWhZl{LV?sh6n zt$aL}`d#6u8?&hTHBgbjBTs~y1sHG^?4r__qBDt*WXWd%_MyA|&OiO#zx(yxUIpm< z{%`*^{@3~bzxnqaXw_r3cb=ad3Q0+4Uy~qK9Up3MfRRLAtG z$_?g+cE11h-*$u*n=Zq?qgQ{>}jTN4#sL1EMa0_7urVT}si{H}e-@X9QfldPE?a0Y!wqL{w{< zGCcjon-uhYPoeKmRkI{SR8`b=eui7d=uKwBtQSWlYS{;U2aoG^6z~+q>T1~qp9ss` z71CBQm`ER=fP{qXA>?-~gu#y%0zFjH4=wiY&8T;bQfl5Z+1_tTEO_m~<9T$z&kq8b zUP<>SqgSl~Lg}Y8XGsL{VIW+OH=J-+!*PuJDS(15{#4Bp^&4@>s(wfGnn3^8=-5%6 zPrneM0>Gt(Aga+pioo4ci-01wSw1}w1_9v@pxI+t?SKd4sm3OEODpYm&Ied`PrwV% zg{hDtr?wb1Is@fn+^ldTQcr8irZjUWhnWW`Dh{C9aYnadOqc*HE)^LaEvpPrbmKzr zM&#ejN#K!6mzr1NK=c%+3(UQz4if!iybjzRs6xj0O`0i2u#yL6%bCro? zot*YyI%LS8217Lbpv%avC)EE^D=FVs+DVYn(8T~#u0?uELQXmREDV=mROCgYE8%^u?w~H=++C=%lZ6Ewl(Xin zxDbj0`lKryHf6sGU4TtrT372y@EWL?|{K~t2Tq2QOS77SF>=_>Ng3OXigAT z^{g`J|NP%`i!PNjs(R)xHkxw^vSW5$#(Ub8`6NR>Z)J!}qh5dE;NTOm3w zXOe6j$nnPZ``FZ_Wu26~&J>3>zq-Ok|I@e!x^GrXjn#NYynCKLzeSl*V00T?bxP*6 zQCf`(H_anxii_tk?R<+zs7J18Oe-Cz+Q>zq<-;Pa2-wuf=_M8Vjd%>h8QsRwK(VFa zn{4ie=}EL?ze<#c+acjO#=L4S6;gVIYAx(%4pO`d4i3-Wyu#`y=ink$ugOQHw;b1a z>1kUGG5SMtN@1tV(yi(B4XFsPl^i5k$08DqlWYl&%2nB2MazU4Ig*_dmIYEJs3!n( z#kdcf3jhR-dbA0-uJjHUsND<}mO;feS?(gVc|10(k3;pywscW29M$7#*u6^o>6v^as~rQH9PoS>|w?CdKC>Ohlb zfB&ES7n(o&iPR-(zocYB6~9o%)8RdPq#|H4r4A<4B=C7yN~ZpX^6)ol8xieGuN z|Mh?OAO7L5aepT#wS|rJY#l|yiW|EnZ33*4an><3}6!@OFk3kB%k%89c6@ zmiE$nMjdDu!tctJqTYLk#8~$;i6j0Nh}`6$)R+)^V4ECNcqKq^~_k8%deq8X;)#ZXGK;|AOb8fl*EJg!HN z>(S$S^b-ScY+8}b-Bu)Xw-sr7xCS7TfMD8CUIg7;=3}#zd8Qd){zs;V?NFWrd>MwO z;{k0QnpSQ_vF8QYMS%x3@f$m&06O8ulF;|#iuAZ5J+4T@3VmNccFb+~bg0vE7m{Xv^b6~cNASE>?)_@>%QPF8F zxoeL7(2LOm)aG$9dR&Yi7o+>RYwOk{Yq#~t+HF1B-d&3`%YrsBOl zw-w3UZAIGNU5g#?_h%WH0a5zyfGr2*F@UL?xraTH7bZ?_Bu46+D6}!aDwu;|FvGG0 z2)>ybLG1np+_lFQ>2XDRT#+8EE+9$z<0QGkf^w@00%~6@R{|^uzOJeSSglUV2A9hK z&Tt%vwnwFmv)~RZl$Stza}CpE1CsJ(!gUezU+Zrlk)>s*|IyKz!ug}qymREiS6^9# z;6G$Fk+s@oiC`#h#?+RKm_t{HCM_wi6}zLVYJewK7vZmG5Vjhmgjwu;|6l&Mov$Gr zSSoN+Xj`LOkI#_(AEgl8N`XN>ctK6NCrJ8FNhQrk2Cc~%$7P_uD(es$oh1TPwW!$r zOTqNc{-A1IiA}02=&tlB6Ih9>v)_WpDq#W@B$8J{6-25q<Qta@Y6OWm=ylHVl@v}83qY9g7Wc_}#LYp&4QNOW#k|$++~{^RdRE_4SIHpg z;tEl4R8-JPrd`m+vN#CjiXeY2UpXSsNJQMI2s$3|{TwOGyF9`pCF!kU0~)a}qUrj% zKka-)3}=#kCH9#l_)QfBJncYxk-*JS=ajkojsSOit3>Bsv9Sq;u6KNNaHEZAcf`ZR zSmbtJL%0zZ+Jb6mIjA9Zrf+5=&Y`8}TfiJvp4qBlRzG?UO~4uG{i@cBTiD2WQXIUR zf!+JX3+lR^VMNslOPB_X>z{B|%i6+wPj5D09B|+^2#1vouiPL?gRAAq=WE}4Hq)X< zzIfEun?dlQAS4!MHj;!88^cb|b5h?%A~AvV4z`407MeEE4@m%VbL_n=-->-BcWfjM z&BdZ>*9{#nSYmax*X;$}&7u2-HW~7psgKQI99ckKSm3*k<0!JtATi@K%cB*0K`k}D z-6l^-?VP_l{M9pTfYxs1R`1QW-x}3kYqkBrkwv{9(VCRq)75xY#f^w}4;L0IBejE>HHyTDSm2iCx(7m=qoJac&g1l8< zao0&D+gpOQHGZ~~y25Xlf^Vnl`jC3TGAzEOLd_UDSk^;vrp5Ux!gE67E_)fPZ z&!yD+#sL>)%^jBoT)uNH@+^YPr%xj|QeIXL(!&NkIMieb${ytNqBk6VjD;>91Iy1d zq)t^t>IKXU_d}G>*(wTyrV3^(2bH6XpMF`ML?!3iH*`kSS=AuRHeHNJKNB@5@RhKY z;sgxEwOOrEs#Yy4Dvc4I4gJS`Arc?8y)wA*(7&qp!6*ft&b|o_D6b z$Lb(0sb2nFwo={_^w>r2oYuiQfR8L@D=NV{*bV%17O~U9%0j#OVMXWs`1zt$)o)Vk z9kZwUr@O@&d8_JVZ*JvI1wyMu*k-B3Tem^aOAQdCJ@hDuGws@IKKQH;0dMz#&XDrmKoz1la z(C%rje->MS>DM=~4oH92IfFXhv?I)oT zdWM%{`y4vRN;no!uLfM&D8^1Z0rWM6Agc*xZ!2-Z>FcIcH(tzUn4TGUzP*&q2-a=C z{mt17b5SEU^qn-0z1$5AWO(5j1VE@jzaWN+O5$&y&8YU(2+Y5KcKYV* zB}^x8&r!*A*=^qiZ5wBAY>d~EPPLnlc@Oh3?_oaX4(8)p5MGCD7V=tPk#hm7j&maq z0Q^Dz3#8Csj&cm&^Ww}$s>@D+8cL!t$w*4&#C)y`=Ho+B$1!#?pDh?-E$8>teAYWF zRFFnsa1wo(-HdtZ;drY2lvTehx+v6UwtA+$p3=x^aZ#kXMStYgAR(2H{0CbGu&KZi zP^pEc*RiU1eQ(L#kgS4a6m*87OoZr5l~+MU;FQPVIpDP+HIcHBEz94nG8s{xpyKo> zd>_uztXSCDBhor855Fk8h?2!A-tcPMDUyiTGjTK4Wn_O^R#75mPP(G&tU>M*VE| zX9w!1#=G6D>NGUqLnHVdBV=I@}?LuX5PlyB1!78k}_Xw8N1mX|h9iP6b ze{p&?2N9@metCL)@}|3Tjk@Q0zA2w>-6u}AW{+X?-4=^?cd>YP7mME^9Gqotf)omX zG!UmUKq(KBN(XV^G>tt7RDs9R0+PjxpeqwW$J0QRIJQktTPD~)go^-V+j1QUeC}lN z##)Ovg>v5fQ7pbP-IY&u`%R?LDmUQl@_?OP9E;}6S9|o_GEsZI)}I?OX_KU^mfN=BbH(mg$E1(mHg|jYs-sxp(8)`H zC7?_X9DrNf>d6-9k2^SWZ|`tag^GD5!^JDk7A{B%RWk&6H6oW4`7(r)N`?9majIiY zNSP(=>|bCvmvufODD#9zGjV>~tX$?GQp7CS0CLla=&IU5MzzrRCvY=fv%{-~4EOZMfBeUXifm0s=Su7i zG~5S5Sfh5ECH+=9&NK(i^qRkd4^g;3N&-PeCc(iXcZl#@Ov_=zF@NA zl+G3;%=TsHB`9Vx68I$uut&+AV$tEVD5jeGsgJ`>K2MdwkWEF%TuSr!5|EVJJEe*) zWJ-fuiKp|I3r)F*_~k7KvdV2fkLAxGbVN85isoe!p^VZCQxM@$_)e~~|5j7D)z#Ax z04HNm@q}$XE1xsR5naEZJz_tR z*OS4x>c;%i^XpdQvv^IrbBWi1_#0ryZkzgYJNwEI5BW0^ND ze?3gYwAOjU^4ENoP%fshr{?1y*D7H8fOZj7wfK0z)pT8&1Ya=+ux0hGsmRXuUxTCy zD-_p=yKMTTK09>EMM3HaPAvKQDo)9TH3<1BO|N=Q{u{v4W>`G-c3nBD33on!fwrw8 z=xpgWKdsUo<$^k`FQ_>3Fp;||Pf$d7Pdk_6IV0iHX&erW5Dt4ymb#KdP+2>|2@F+` z&uDZfWR$X4{>|U)ddsg=v|!C$&fg(iCFg(9Af7(hEASPZu-6wS^Bx{z+kzoM9DLER zst!2%Rli*ygiI|ryK-?mKsa!6jJ(qLYP-IirO%@Y-YajTZ=(Z6lE|#7ZfX69{MD*{?CqU_{j!4V zc0l7xoBqKMXT_eV`f0x8A@l{-Uearw<`u~<=Lfvvn$bFXZ=}7Kt{lp}AP*B{@38t& zy}8wXX8)ODJC&_pGw!BFqh~SVzAF0mLeXXyU2ICb-yowuU)BPk)IiP2-I3;f(Iw}flN)k+=HTDn%P#cvs2qTlJ^aattxQs z)=JB|DC+3$ScmVnQi7Py?ndS8ZdBU`N>U5jsg9WhnH^>@7ZjCQmJL-VgufiXA$>rK z49c`cPGqDkH_HH|L>;W{gOVtA8b;*=mT5PGzh-cMqjGvi*drI)qfxczZ0+5R%HG|m zw&!<%m`|KXV35fXyh^Z0*`WnWy$pE;1L}VGIkzm!3vvs&TGw|?{AjwS7bA6mHO>5u z7?tV$;ThHDT<#Pwx8!;>xRRb$+wplyx8;CFpwH-;)*A4syecfCCD7N|ul8h5mV$n_ zqBg;(2Y)9m@7#~$h zi%;fp!x-=|x}lokR|x|M-)rrvk}f7njil3b$o2PH%tq1Qs=e*{m%gWWpF(Y(}v>kyhgzE)mDgJ0OB&Yu+TIg!29db`|oW{2lUqBznat)ng zCrrEtI*8S=qLW#~Aa&B2BF z)S2C?xB#QP5-#o3r$i+SREPs0h9DS7jm%O9=a8_0pP zLLG8;McWaQq{J_TJ6+$2JUsv45~-Kr{14Mn20L6n11hQn0X z<^~V+>I}XMuHV&xVBm?bt_bp!@M9Tg(8D##7?B{muLK>kRK!pSiH1r^lFjQ5#dP(8 z*QsR;e~1(G(9X|IHG2-E^(}m!#Zy|*1!u&7Kt$rQJi3W8xKRguRhz$)s}`Jr)ps{y z2u)*~g0?jq1!GNWu4Q2dQfp0$uKlF#tJ~Zh&;k<)S<|#)E6qa325yQKY#?0;un!Hu z;_DkW1c7rqGcxE0+K!w1NX+K0+1!C*hiaPsPHv66*{!h_YjXf_2&FK`OOP{0ZqNm3 zRcyyj2vC#nBr7%x>(g@GHG}yhrTp(#8uMf9Y^~N*a&QlO4envC+t*@x@UQ{rOG4jd zuGo)rGAHIK7|pPG4MJ>mb0_4M7H&F3?D03)FswNC{WyflE?TRDB(!7N4`{7U&orA` z+ed4CSZlRgtku7Vwfgt4R)71Dk!O=jFNqQBv7^KfJv+lk%c2@HMVB3{q+kKXx2Ek! zK^BLpV?$g$1N=Jn^F?d5q3vv2OOBX#f1ry6Tvr_c`lDFuZ^MlO3KGRF*!y9h0{N@b zW^wkY=1Z+RssWY7Fd6l?(6U3N26B}{b!EwFjFeRscRcanXc7Lp zGH|c!ChB+98!5!*JWl_HY8kB5nN4+#(cVQVO;L5Fp1|0npvxze7eRNCTn&P;l`@~| zDzbSW>YBe2N16qKr$iCkNiL;)|R6F6yAh1n4R$n$heAnAL8j=$O|13soGY7Yv+paeB6=MLR&W zF`m?W*zNq|-+%uf{m=i`|Ik@{V)AU{p_F%BWCANL515Yk7SDhsTqqtDt8eZt|ERGR zf_WY(X%j6VQvQN}KZ;DtQziiGgWIVMZrS9%hB8D`-pmu6F;cV^)T2tqq9L?@dX7p> z^_R4B@%rqXhgR)D)o?hzBe4j+r6)Maqom>@p^J_D>7`P?qX?U>r{f8o%DR+jo?=bm zeFJ)Op~Op8)mB*>YOC}Owt}Xczt>RHYvxHdI@svYpzl@lnAv5zP*3nJ+?#a`EIT_* zH^tvn=;UwZrTA_2Oa3}1W!;MT9;dE(n{GOFhiaxEvJn0@z)fr9F@oTs>4Du1(R1+F zT1IBWVM!rLl4cI$=Wd=Rndci(hRvfJWQ~_9=sFB{7izW**g|*3I~DruATa<463{{) zI7crAtlI|Tt`!FmPRX|oZt5?;0o+-C^X%2TlS}ZMz3gi8fg`9AzBs8L^5W#Oq$zuU zfq2yhgrfTP{NkhHq|FSSdt0G%ZzFVeU{JQ*-1hNM;ab?X zXC;}5kb369n}GG%VI1Yi(&T~ZgqaD3WklDr&~vTWiQum+<^tU?G~8w;FkEASEMIE3 zK+~fSVtJ1ixQzw2!Q?Ib-Uevj+W_qylF`Tk#&!UZGJw@cl(?yt`k|kZnaB)t)Z*EZ z=cO^SH)#Z8gai}lAOkm#T_ma-geoEUA)8+Cy8Mp0ww`lov;1g)+xRWfl=O_VY#1YK z2^<}41-sUQO5Qh`dt~h)jTDf@hhSK39*e~ck-0hr#74p7h6IZ=+vF=uhLlN{!1);Z zogE=S(0K~!kluJ}T8$-#MmHcaawXq9KeN_iO_&$E>JH8+Y3JTF50whR@_=5Yk1M}v zZV$N{2s#EL%!$m%ooF!<*vF${?20L*iM=sn`egSInhtf5n?X9F@+EQ#)My@+L?=`m zw;b?&%qB%S0wAZD@f>xN>grv6#Q{;1*u9rom!L!BB?#EP7Ef2-ZP|d5?N%uJOy9(S$+H57N|uT_mXYccMiWU4HMSGV84?Vc+$GfC z6^0A&gNTn{boLiV@(E=zYnrZf9h~x@5h?p`(C*H49N7o zSL4#Z)al5Q_e01<#FqP=rGsRW>Y*Jg6;CDVStUOu9jJ~WPliIF?w!L8CP-c}EI#dg zLmm4$ac4OvnvKot2=KH=lCzW?W!Z8?_Q^XVY`4$?ns^DNWn&nv8xEeZ4)XjfqG#1_ zTBKR?f)4Z)7t!UxRD+E_U2EY^_Ji?9*aa{15U810 z8F=4;oJxRN0)z=^DBB8iaD#e3zLj8u7yiCkiP@UpaqeX$&b_Q;`%plTSd?jVEgMfg z1W7_}g#ln}p6eyRarnrZpup9GC3%oJfflX>>YWS;AC$SGnmJmu5~!E>=5ExM+gOS9 zKvr_}=J4%>uuV#}{f})Pn~55QjS$iBalc@Y8yiRay^w&6aMYZzaqVHNh#L+29`x?; zOUA*HzodxI=`IOL9JgkP0w$p}M0ExQb2rf~a7fo#IvZxiDvrjrw*aojWva#%7iN}5 z$|kByDVW^6BTt3&Pq{G9qUm&~_~}O6)!mifboLRs$G+--*(7o#{zdKBi)eU*mLPA= zKP2^lI`+ZNf&ice-Yu4)fauC~W3tF_zv?6BVS}a1 zh-{K*m?>0j7Q=rwRFScJ=Td5gtZKYOjL zttD6f{jJo$zm;we(z4AoGh*cOd@vA!5YzHdI+K9DCIrY3vdw1V1EB?CdIQQ2;L6M$ zGlUYV8M)ZsBMrT{fe(>>v7HCEQsdD|9}XcVZ1>krH1`kqR|D~7N#)xb#21h7+uVR- zruCYxz_~zon14`|0|=QaAspG)lD)gEH@GV!pjn-!d{ipZG!iPST5B{?v5%`J5SV>F zrXCPaq2kXNVOiPbDi)jZt>&Occvm&rpVNxVFF%tqnU4s`b9-LQiJ6Nf_Gq=wELrIF z*dTWO{N(WR-P@B3a06j8NYx_sjP%}c@SXex#L*l6ZgsF#UMLXB(SIqhuaP%K3oS{_ z4{WPo?a+MGKV-}zy~yAnL_@{opjf^oQm*OZzf@H6C69dVd&JyTSx?CtNdYb1d6(F@ zn*knUHcfxVnRZOcd3Au~nu^`@x10nAp#54X;9wC>+5FV7KQ~sbH z>)zfw+*0`+O0$r6t0CtKA=nT@q0(2D@(X2pxSd?t;H=id?JULvK2TRpo8usdsI%B5 z^$Cxn{0S*`EIr@p+{!+v^|{vT+Mmgj@-V;%V_J9pfcmLVz*NAyE1!nGRrDPCXn26g z!C=%fvWQ~QxeN;LCyU5hh$O- zE!;y<){sS4YF2Qji7`mhjVUoQj7+r3nHWZAAFS8CYd&PX4x-5HYH#mC zXlJT48U)F3gf=<0f`6(3Ofi7?M@HgH{b0&TT2*P)h5=K!-(R4_$FjzGh9WpTdL^Yd z8bpa{*q!}VrB3CqPbzHhP4q3%r#A$83WO5tN<6zlr#?#vhtnDcfzK)xuGUi(7U2Of z&WkCfA7DT;+RdEizL4*4)YrT|Af#CSK(5*O^2@z*apGl6CW<3VuiVJQ>M>Y#3m_LG zK?*oM4)_|?VTC805t4Q4+^C8*^*z^pEDqTOslX4COGk3{1tG)uJ|XgByc3jS<-@P_udzw++rT&t4gjS&XYVpj%#G=vWnP z@q-78wv^XfUAhshaT0h(@BLtnFQAXuzKY26O%p{!(059V%yZ2+20}CeGrkM*a)^h6 zc^4&b30M~ZTS`Ju@H&>ASpmFK1N4;(TYg~sEX(vBFlJxvEo)nT6pUEDZvkJ3t^npG zB2-{k2VHO+$8P)(TmBu`bk{2DE-C14C4kF^_)~Ih#bDbi<9vUk!TK8wc6%Sy1pNo( z1rs;Tyu`HNjykEEBg5pO3=W+931WRd2&7S|6XU?Z0gcRq~n>fsB*=ZGIvrDz@Lfj9?C8QNw)k&rd< z|CW-ntgIVI9UiYQb$0S63b_giu9Q0{hE9G~GK#1qZKOMZNZXx)Et1@W(t1kajAeQA z>a3?Ccr_ae{a*+F$3KU1G40Mta7HyrKEMI;w{U2ocvehR^dK6>vzwwXy;Ns58amQq zf9#R4m6TMURWG_F4Do{&#G_kqokc0D#1LqJBeM1ab3HQgr+6XqwyZlx9-?fU3x!OT zy2Y5+Rwzp)h=#{nimH#)aDx%%L#Fvkis_mw94kRLeU15<5dR>=RMVQM0!XNI(|TY{ zTa07vi4;+uQEL2}PBx1j>Dq~xUHVA~ijCK_d6`MBAKtX+da^KYAOhxSGZN2~gSn;w ztnBP$O&n!Bu52sw$eJ&v$+J_ZbcHHAaC0t;=+XJw7MI(7uh4k3@8 z!nn@G_PY|AyWux2Uf%LKI}?`xClMDJ@V&!1WujU%ws7a#PTtY4Yi(wI#(fZr4&{ZM zxum@!-}a~asIRX0tx}h3VMf^|E6>$#o4UhwNq5!8%3VrxeSNRj*VQ|gmZP`cy*M`1 z2t3GCL6C*Y->9C6Qj$1YN$N&^oVoY{R4An30~Jb^LW3es0Q|})ri8UprWi_ZHWL5HsKyVI653&_vS~-MTbY)z#%K(8 z7o#!UU5v)q9xm#pj_W|G2b)^3<6tvun>m3M2J+cx2O`&~i~{fvvOS0@h%X1CNAQ)v zO^Zy&`tgm%`GYc=c7+sZC&Dw?-e`FA_C~{_w|DIAI0vB(R;M>-v;*tQZCvNE?AV zHe3rTPXJGVmdVe6HHHRP;Mh5QB=JtHd=Whg_a*TCU?-yqHW`iC1oHfdM)RAomGyoI z%Y?8!?W+nH<%KMM*GLqHqK{>*!HYVgxw6U!87#RVr{~WvkSM|Sl5jb-<0JdqWJGyt z?7+&TW1+v-e*XLAm#SPYm0j>e7j@lc*`zle_xSHZlC|8? zfBEIJ!Sr%0xK-o|N)`$9BKg2%@$A*qVwqBbDi5=qLVstmH++Fp&7qboap9>d0|_&lZD3Md%S)cnUN{YWM0qRih6 zvn#nuH(T||%!kSq(Hg36&^1PJ~wl^n1EM-*)Gf*C#5^Y!HAsm}45aP>#PMFA)%k7_Y zlE5y(J;Jou6Q!}!X?%?eIt?ArU(g9>Nh)uEU6xwXJ9Sz5=S&sOMiqyQFh+hKjjmivL2X#X+2OS62{a(BtH}4I z%ZKwBh+fO$Tz30ip(=9AvQQ*p`F>Egi!ecw-A(wrah6tfwx#zs%j8G`MxP%9<>JKbD}=0cS^sruSTu*i(V>qYxBN5Q48#= z!3YSJy}v5!X3xhM#Jhi4&uasJA-?zU*1A0ya5P zT8JmanT={J5b&iYHrMW=f;M%xL5|+s<%d5AV(Qdo0l9yNTLQ`W4D2DGQ1T)W*<^7P zB>=~wY|P8kohu0!hwrD9H?xs9YqJ~M+tO}u;C6O{19!kF;C@Q&1Nk6IK3;ftPebMQOq&v^>3HBeGGREHeHop zS*t@1Y_u74D1nzC0J$E+dBQG_wky6X9gDH(uc~uZF0y2~xdo~fCwxmu0ZD?vRrz97 z`D*=cfKu?5GNUJ>Z^!V6fnl6)21QC>Oq$YUg7eV7kBBj3s^AlpSrc)2lHp7|rdy!a z6J#{7J;$y#8IUW#3sFLl!(1t7Hoaw{u&HO()mg>aoL1=gR~IKIof4J~e$C_4i=#I% zmd?=|nAPjc&gIL)%b#6z4v#M1A^rZV&Wks1j^Q_5Amey79;fkLR(iLIno%OF*J?ssbSx z0isP~@qbZ;LI3v)^s0QSBZ2&?5#{BOlKg~PS@>?rB>L{ItF#@cO`wQuCbts_RiM3U zcmzJb{0!#1PWAMEc66O(MfNidu>9FOZAd?(cd085BI)u}DI-XHiyf@KRnXS?0L?~7 z7f(bRAr?&rhB02A;E|l2y?cFn`K!+9Mdt+~hnIX1g7sBYH6RBfWs|yuDCLr!NU@M! zm*xQf2XejTtq+k?{9>`EWw=dzl(J6@4rH{~esxX$uBVD5gn`{vSLP3&24u+5p@S*U zy0|=j^{VswmPEH|TJatWg8g+h1mbeg7Dg}$58$X; zTJZ3&Qee1*rHKOtw_RzIC{xi(S2A8?I|9%>(N*W!=Yh6bBx6U}@MlG(c~}!65#n2Q zOO=8%3*k!>)8N-UlCzY@N5uL4W{+BScl#L+(ge2T{-p=E1*8Dr<32?(d)LIy@^|w+#_Kxm)Grj9T3~w z78c#T`up`g->>ahzUHCZyRl9Znt|)0U@|lU9|Wh^Q6@&1 zznG;4zY3#|wsRG1QwA9mp9z8%9YM|R(!4o+$|ts9fV zG9KEx4d>Cif11|queWYzd+T<#x9;s}s=NdQ46trHH$CuDTi~H`tq3tC5ZomQJyOv& z_JCc8f*?XLDK-t1%O+q~hZqUCt(w-2{EO{9taW=m%YU@)-@0{gR#&;g4zIW#+_H#} zoi4Jwxv0vD9~*!P(i!wjca4?`f*WC5^aAbCJwQIc+;A?&GS2E4=`rLgZv@~Vbj7z> zsV&0n`5=KW_H+0grw}%T(g1cqiNDCQ7G);RIL=LGMWr}iljtA~aEVY}1ZS7pgsVER zFU6VSUJW@R^qIh)n0)Zn$x{R2$n)Lj11?;>;ytCg~ zu~#l17TB<|1O2o*PdZR<%m{xQk3VozUaQo9 z31?Zu&f$V%>8YA%@ZpNGs8nSmVnSidvuLU`WE7?X?$(XeJOfw!LARJF&^GJ*74-fU zMy8+N5a>RR5rdgOzWLY-=K4wy*{X1pl?9w}1-vt02ZqJZG0V6bL}`To6vOhIo0~CVsKw0#249sNOpMLCCy;2bwnH|pv-2U!~res^N+h}l8 zXo*kJhLNYu88ewbH!)vcT3K&pw@CLg0Sg8!!!Qu20Az!S;jvICaK7=S#4COxc3zMW zngD>_Y&GJTdvt)WX7KEXimtopNDVzdz@Cc%0Uin&rPOUQRp`zKr=C>5(^++Q!e4-U zE2gE=D{}d`E}e7n{u?u~50M%XXl1NO*c7ff?22ExHk6%n%*hEkEsn+tnq#pG-=rtk z{!POEx9s!*nXoqoDpsAjRg7ZgC3hjTWbvpIinZBF0oc2=kFtu<;KD|Q_39czsU$LbTdmsbZ&*Ycpy zhdNu&x8Wgz^9e}rB#J^i&KxkR8dictt!+c>#YjLF92*h!VayC#tBb|Bfd0D6>M!dh zD4k8&HMM`L?WCaxl7?pJ!E<$kJOW*@m*=tJK(@wk;P(Dhcharm9IBPWcgLr1YP;x` zhx;RGV%pYyG%?pwso8z2Z3dXT&H!`Q8DMVDjO`m9d_}+sz>#1%2>C@>YGeW8B2;5d zU|W^}aEsxY2$P_W0n`dn3f?pi(w0HPnRI;<^xEbF&HzUuCEg#-45)o^de*21{ymrz zW-F*`*EwPAIwy9}(Wa6xP!!+-PmKWV-X`LL5DfskC88D~g5-uD=3wh{k`%O<5U(>` z)KS=(n|h(^c+ELs;oFCu6Ns;vk8|R8Z%(Wa$bf_@n}8e}K2v{;EgKw!6yo-TxA zDv8qhkyVPiet|n_y{AwBe9E69@ihek0ROWD9P>z4-Qy>Y%j64%t6sPngdSLVpoOS- z#zgS+C5XR3|E9Os1QwLV3kB2U2+Ryr6lf&Zm3;9ETE|+SuF2pu;wQvhmIk9wWgkIf zlNUe&G4RL*-rQ9w;W316$0T_CgsnP`>s0|GdT`QP0=|)ijHHS|adawrc;1*)Z13%T zo!wOtpQn4@|N8GiDkm~Z)w9!e7sVcF+NBm^a2u)OjpiYeFUQ!x{bs2HBQ)&L7(f95 zUudbLuEw$4Fj&G0^;DO^XvTNy3SBeHt(^80fSv)X&xPjwMk>w=+^^6l(2L~VPF&rK-q4w_gWqd=V9&2NZ$keo|L?6GBWxE8D3$8khc0LF;aPW`mh4TnAcuN z`GsIA79Ojn;DiYQ9&`=3kD^&nz?$lr_w}%Ab7!~(jCq>0VRnmW$L7@6U5$7We9oMY zQ~6qY_$u6qJ_qGm15pdU$&~43pZVZan~2MhOpoCXrS3Zu;L7bJLw!aS{$Z&Aa2lvT zQ=L84cpm;PV+J{ciq5YwUxgC1c0832l*K6!*FX#-uh2kU%?XKER06>Ob%W7@y_7On z`k|;2k4odYR5}JHVM<*uL<|en?GsECz{rxj`or71;6znlRN4fmuMfdyS00UfpOhRK zid1yom-$gjUaY9FD$|9_s2-?*MfHRbKT?$(YNXg{8?iKHb-=mBs`W0*h zxrSCo{xgx;6ZB*q@U{GEQ7?;hzPjGc*G*L+hU?$Wm+F3}Eaw1fNP*MH4l@y)BgtVn z*XHk+>SA9kRh68)LjcaCtGQZRTa9e@LJv->#RMbN73;rvrC;s#Q1!E&bDHF+P87|52H zY%)Ktv!9N~vYF?F@sLKdoPTgxqAz7pZ!`*Uy&vNhvkPuIUNi0XX}=`ae`3>YJ)sV=8UPd#N==xH|?> zzl3HA>R)+Lf*Tebx!yolwXz}(sy#Q&pn$`J*=*XEUuxrn;hk?YFx^gi7=j(Y;U(Ld!3fUwYxOdYtu*J)~Cuj2YQ`qZWlIX&{i~ZFo`~?yqWq_WzQ~FCC*5{ z2KT;mQFVBpK1+clm}cwl>_8pTzn_?xY2rLX(3%>-PO_d`0O1;xJ6RN&RGy8)L_nwzn{=YUgAgf1C&P7mFN~(@P-}Bb zv(-*#eT`iU)J@COLKR%wCxea zfo~%rWs7M9*tR_o7RP1|p{m#cd_B*gz6J`q2wH*E zphYz;G+seK7eP(71_m{ej>#S@W5V%?%DY)lb^l;YewP2Z)ye#%^t&>?9aCOS*Wq>WFRmcEz-N%C-;Agz~P3IBBsJpswxYRz{j%@&tlTaYE|`zOJxB^eZ*8>Q|cY z;1{>sA1zQr`0+;4Tnm^0F0>D{53erU!g^U=uC)+ld3AYTp0%bXWzAXJZ7k0%ks;7x zc6F5rxqpXZVTdP{qt++49EcEZ^!w@)m8 z5+5LRh^1<%U}WoBcnv0t&uRXui}((o>RM{a#Fwwy zh58cKNu(qrk=Ug&l5(^lUWUOeI)8$k{|HoykSG(&0qaA`1fdYpjrbBBjBaOBsRqHA zTp@?b#*hWgppn1{N4&ykaz0M>Hn_y-3jc<)90% zE4Ci2Qt4=uVn&qryB`CS^eGdt2{WprD=6D_7Y;oe$4rN#{N`?a74;MkM3&0o6i-BG zxjb~gn8j1-2xP^FUP+=LCRebu#Bw3U0A<4-cL~)zD)~N$@(^9m9$5pGMM_=6gfULV z?O9h*z$@`*iVTL*owp@>2+XC&c9>Bn!T<*Sv=b}RP|EIQ64SiVIs+m|h(pS)49`H} zRR5}9-V+wAGZ5il&N4kL<#YeYrL4=4b_9!6dDqD zR))zSvIs~_W|9Rdmx6K*RCa?j{LSrDDQSX;XCicsB^_#ci)7Xn^4FTUX}1*N_=J@Q zIu&ZwV3&LP2P$=xp9SU6vZgd=2jn$w2}Ft~`a+U7z(mB<9xceLva9~U=}$@FBG zJPjy3$9QP*T~B@L$V$S(Dy%USltd0+tsU}1w~Dw6Ezbvaa}h1RHwAd*f_9|EOhoSV zdXQ=5Mv#QyDd;=}NlG#qtwgr^`wHLT))YRUs1h^1kykv$u*NYF0?HalOnh4?#Y@5} zsE0|w52;{sQFk5HEJcB$BxytVSy7S6DqjLu`^CkZ*BxrNfbay*Rw+N0*cxHuyioEb z9SzTTKEacq)rvx?uGz9B9V1J#06z|Z4Ek`OZ~jJU5SB<`q6R9N$6o_5bXX41peS{IO4*;q!BUk`e2k{XJ%TZdQlV&#

fHy&gw9v)gFRu+s?i>t@beFnv=ETj7M)zPF}x16+*i1FHfJnW%zQAZC~fg{G=!|f_|G;p=sX7uFd<{_4YMS zK?2;8X$A(eFn;7DuwKh>!JiR;)EBa`h+zAW3o<>JOooaI-xqRU+X^ypxcUuFP0um> zz|SaDE$2gw2uVqlC1su&`vLOcj^+Bf z5hM|$WRVbp77fyr9;A%&7&T34l$mC_Xw*OfTFcPBA8OQ%LcV(#^$#dodbITI^Hwp0 zT0luK1?_HM#lsR5e84G~r>|NMS<>}{0(Lt^j7`X=-Im3YRLv1nb&&g_Dks9eO!jOnec>k-^vByu8}UK z&0o+5@9ZPgp6P)eeU5<|`z>6BHRtc>F|2E8onYcKL<vu`&8hMCv*VMq)227E_fP9xH92U2NZw^iK0-;;ws{7;9f9)uK^*nfRF*BH5IrcHm*Z z7G<6l2RVQeLGC0*T7nSP)+e_^-@JQ0C+}X*X?y%z22aa_D!d;9cj2XYY$irO4Kh3; z#}91-V%tc=8LetF3cIv|-0vZ{JYziVoIVWwyN zN$tRur0i?%4s0x~(E9cc*TCwM7c28&LZL=2r1FgFTJoZl?AWJzH5ejkjXCL~yyuubWkTohlPA78;#8qcup_`@<6w>VxxQQdaVTamld z<0WP5!0MAJuI+>>*n6x9y6m|ySBucIVx`g*$nwM_KRB1q>+By23)Pe5&CTIW5mp~gnKrU!$h=&P-!?^s8Px@Kj1zm_0%?TOoq`Np6|g*$ATS%mG0?SXa_ z@~jR%v5CR2uWZD*iBK4P~W#OIbJ}^prDU4~k24tjA zGLX8c%FBE&253D1VYdmsktB5$~`^UwILU)HRNL7*T(Go+L*n4MA7hU zAF&h@TbSH;QCdKWCKOQSR%`@-=pxEUSsn<4L4zm2XOOP@X@JmT61#T#8?Z4@#yr|s z$?)8ohPkhenfJA^?W5MN6Z^SE?qm~z=onmdDYTJD< zkQh2n67K6=b1@rZViRteX7JF+@P98#fL1M!`xi5UeHxDh2iAy`OX=6cI!L6#G@*aT>bV{k#5Iu|1k}rr#jQLg z*Pr+PDW!}-EDZ!eG~!2Klm-bue3wn)Xz(q5z+WKR1zd(70L-6&%~uXaFc9M0^82{Z z4Lq9-uRsVapO0V?L-pyJ?8IOH2d`1zkX@-GtIrtW4>$k#-wzkQdwn+o%$|32&bNh} zL6u$^35q|@u4bxlZ(*JK;%{e#KJ)@lSbu{2Q}jW9$n=31x8>Mhd_w7oe7b}}9Q$yY z4be6~Tu1D)rgy=ocOOXetXgGIXNV$+91&A&1tT-aEZa4}?+Ch< zlr%18j1X@EMcD+Tt`=24`~=3>PyhvGit8>(4%_#qqg*A`lp&++#85YOvMjV9RSE~l zL+_xileu|ruh{n;9iE-MJ?uVzb$Ide)#;0ums_TFHQnUfH@`SJlA6E`so!efcy{<2 z{EW-=lRNNKZm$mV_VjYarB?5jeoEAp8ydBOHI1&mX|}pck;{(r*4%O4nmf+h?fo^( zLo)-*Qs_dB*K+b4NwUbJ$`r5;z|a{%0Bvvs|FfWN1r$aCYM#(1M}e8A!D39n1|Mw@ zhTh}6eVn&Hm3dntPIj2Nek&=u(PgEg7WGyz@meJ}GGZ(NB5x(_{bjmMe$ICFnfZ`^hBcE>la zv*R0g2Q35O&m(ZNr$C`Zxdr1l@W%B4RF4cG9|LTQsY-8KcItw!1-sxd_i|_&`CY{fBPXDCDQY=H1VrUdC7WN@NwQg&fDLWH}3k(?_k{5p>b50CegP=nTJMLU&)rL zH1Ou*R#Ai1`2JSrgxz&H;cv1S2^Q7#Voq3n+fj0wAR<8*2BMjPg54{v*YNh@0zwZY z(_KbFs-&!9rybrc@w05cSFC+2SXvsrZeUUe6RJx{!Mdi}n2yIom2E}$6rF`uie>+l z4lP@$;@OZSV3fGou=MJFJ~(76BafBm=1BY>3hvpdBxEafAr3Y`=$SJy+4 zRhT7AIH!`7rV8j>Tw$?LU1&d=cV1PQkPCQDq$2Qfsm@PzFZKN*3(6L7JJGb?5j9|< zWSMHGKEcnp4 zJynoRn#xlq-odIfCfHSVoFgcAw7?dF(v4h{9s1>fXtZms?7tw=BU@4Te_S8CBXUo4 z=i><$?BN_N&|NO)(PTdbkOAOWAms%>2OkvhxnNdU8xtSaaXA1_k(-q~Hy^#~aH*hy zVPgtC0fdC3?Lp|W(m7cIZ0D8Bh8Qp=O;Y`zZ%aj zXHYg~5^)3&2K(@Qn0*%Z@mCRgH$^AZm|!z8CtoVCbZdk?$UZ201Cj_)yw=8aqGpR? zcLB${SStSWfh0V*0VM8+BoF@x(!gPpwuYH123^X8-K;QX&VTjr%TL#o@0j(D=*?V%aKrtWIJXS0HPa)Cfxw?LWfZZ zUh&*fCD3ime_L#2enRE!<{B1c@BWtM-QTjdFLVoIFAjkiHM1dhr zYG(-|x+&s7J~-YH?{!eJXq!x!qB<_L9m-!WS(a%&xMkUYz?RiU0OH=?vfTSymb-(5 zK!E5XRwgg@(l|vdBTaKJO%f;z#G#vkh1-U#0Ii5?8d8A3`FlAul)wm*Sq)UR1A%1Q zeR#_<{xB?Sy*K^&?z{f&pcsGKA7Aycb)bi*l~Gsz)bN=hn`_l;AR0b%_45j!`2}jR zl&s99YPdmUDoEhTs1l#y<>T>ZMa9S+7xuqHwb88rzd9%(u0y}cJiyAkTh`r`I&no+ zZ&yeiA9icw(G`yvl+J}z>&?IK5^_5j&kDF51NwZQ=-n)ytBA}j!}$8y7md>)QhN9E z4l%S!Kc^(qV|RhU9;MZ76+D6U5L7JD6HdE-^sBtF+ng_R4 zvEgl5gE{Yq1`F$I*k&htit|meX?IGp#l;p5^+030+B{vn*;qJbC)G8w^oz~idWn06 zyGdHOaUq&VKVR+}N#?-&fgW->kgF}W0?a5p-b}gRQw^0~>Lj{WUPN?(KB!GR9Gb<} zFsP+pm@Kt&3*2pT*Y8wyoC@qsZaiu+WniJalhYHNFSWKY2_(09LSrSA%XmQN3QDLg zBf*Ob|E5rk)}{eIHwWhBcsOphLR!L1g8;^p&KqKTLNvW@J052v4`uC`Ng*~*t!YeiYh(cAMxrP#SR4hqG< zz69wIAJk?Jkiww@j4?2YxnpAi$o)KkV`jK^o{|H^NP_^xQdy8S%E(OkaE#5W2)?LTz&f(k6FLkSnTaFWviqwjzHZ!1RXQV>Mwc10^*X_bp=J0tTGDVtNYxAIyP z3H5;%a$I<(n!Q)OrmLHP?~Twf?w<^~F-OSle7Hf+a!M6Tf)LR~d9w|sCC~gw!iwXG z+8>aan7c!v%o>Q=eKFSJCZpP=6eTY@{jpL{*1`Kzlplg3iw~_qD&E6orr`cT52M5`_bUQe?I!XF? zjiNhFQP}O#b;di@d*3pNqBdaiLk@xdxW61fT3lR(DJrHMMBFyq5XvIX~5vR2Y~}@R^VDjY-A46 zl`iQte8Y@UedHT@MsjA(!<9=kBJ^h`qE| z2!P7YGT+HFV7FZ#p`8p2Qy{<0!TgcfKTs`N13{|oMav-HH-u6dO^H}2J?1{%E08Se zU)@2-Awd}6&kp7Wwjna-w<33UH8p2fQ`?@T1@SB}@j{&1_YLUqFg*}Mf5(K*9V{+M zVGf2l~HYi$rFAq}(Y==lCfR`n)Kw%CVJkN&7!8fv^0F=5G`1EpolR+k` zX=)*0;yam|v&q!Vo@G3m+Lk`rcyfhKqA>;O(au@(+eNR;^vS7V`0cS^(}3T$(knxw z$h6BQ?OEA&gH$8_7CWfk8+smed|phVThPN6z%nr2${nf(8T52ZMbi&^^WyEv|IgmL zZO3seX`1(Zid3yNBWf1_#yKO{Q?oZjLJ~?O!6rayR5x641X2VtvGYI@`)Y2c*P0iY zHS+-d3iATWq@;mjY2{0u6fPW>@C@Vhd%hQYXZfV^?4KC@*;U(4aj~_qkSo_5h z!~SHemnxn6jNQc(xu}(Mx^TsgL94}U*9NSXgel~G7^h(gPEp0pxzKGAr$F3xI|-cI zhNU*x!0+ZZ@zgGVXYbr5ZrU|xwl9G%g?&E^;h=@IoUjiyJ8nwtPF$n~RXwiPt;pz7 zDBxsa8W#mKJo30s%2~*PcNeDY$d~kQvRnf1M*H)ePUnlcIlFKuLh?C=FmCEriH|lp zEq7p|i%Y1$?3_K92qQPvKIOV`wBpvI6}KL(xP7ud$tZ7zd%lXDJghw6)fJi`DYUd@ zHxbQ8Auosmk8()}tEio17m?(l-(@jlJzg9wT)80W#$ViM?GEa9ec0_9b*Q_o$E@3W z%-TbD$Xu5$oV;Lm65*Q&sF?F|1_SOc)=Mb^&NnH5%{lYemR@8d5qSm9;9uC8#2BIp5+6^NQA2o++Ehs#f zjW}~-?xtRV9)wp==@@`);H&ILLCzw5VjPt7>`7peYTU zdmJ-IqA5=NdwyXXM=NMOT0!g4Y9EiF;J;uzypo-llO<(Dn1Mk_iAE%Tj&D)&ow06J zu=Bv;;e&b1A?=KcH1bhgS=uDR0GhZ_c;BP7MBecmd|}(U*WZu9DP(x12W zKKSeC>y8u)P2UfdV)K}fq)j@q3m*!nN`34BcSxUP0xE{EwjOB6HD`Wdg_p}u14(K*q_?2y$`B7E@#P}4mZar&X&D_M zXX6ODJC2aM;|R4!2;qUoF~gDoviDJnL75HC1vc$zHwr1^NZEMM2?;T1PlC~SM)w zZ8&f5S=n#oFIoMi&UT>m&wye3g1RZCZqk62q=fZooz_qDOC90*`;N1!_j6CJ5eQCi zWa_N-cRgXO*@h!2%2Xs&=hulym{aM9m!>}Jdx3K4iY(wQ^<4!vh6h5PK|N$3H)Hdt zb9t2hfdeIHf(PqJNo<{&c-pDoMm+QWLfv^<92zVawC;}(8?Rwo$h1r5;%AY!zZOk* zQ@{!~NS$B<{Gpc7_JQK2Oa2013 z-_^5W^E$l!;`YGml)zn)cHxLb)Fl!D-VmuFboZRd;r|mxk72H&mW+a%lJy!X3{M2p zBdQilZ*fs3Y=fwD80=I@7Vcb0CMxY=g?31Z{~ZP*UxQpt;gSl-eK%bPED-J6Z%@x& zo?N^+eP)z!PhWk1^5*p$@B;^1mZ#4zE!(*?ahqv*pJQ6y=a^ReJ(HgTJ%O7jsBp1C z;-QyJ^s%JgpY4xdlKX^y6dx`ojgzYSvrrvoxbArDmH%BitTub6gQwJ;83NP5Z5jc7iC9|P#QK!c}S5X zn6|Xv6XmKTUfE7OSl2%EjbdMZvweA=voLy}voLzQER3%3s-3x+0=%1nXo{*iS zuDXEmF^a{En620!h9Mm1P)1}hJeG*3q0m>9D+?oWqxinM5s~0` zSs4HDrnTR~xY@eg&#^A|bF9nVA!Q%?DSmrK0v9k$7MDSUP_-y($6f>w7rKRz6md>& zpFvH73~7}V0rWWXA|UL`3!~>H4-?nDEsS2XeYu~rFuI?!FuJ=ejB${Wt;d{@@+ra) z%m>;Zl|yLP@v0IUeo_CC>hI;?wEsUE@ z%ljPD@;=A3+AoZGg?pYw1qA~%)eUlo}^uNN0aCkh`Xu6-89E%d`<7@Ygye59fpfJWXs z8vJULW9yHt;Ikwt=53%%3Yy3ZejRk)T%5n|oW4e8^M=$y=fhee136IvR+LkNkeQu6 znjnx0#75)#A3?~hkQMB3=_3y2dJu4uwu@TolI*MII`r*Svs9o`li{^Vd0h!xs)ZEg zyrMj0EJ~~-O{(7I#H341QGI`M1ENHk2}B@Q+f{3>D#;EQDXN_oE6ryY)pz8WX(mZ7 z!JHu`TH0TL8gbuBNI#9(su2Augh(sN?PWDHfxv`-i5XT`t`&+M8Gxr9?~`}X@jv+o zo#0boUKBE+{*k7TpwRB!Om5~|X79@jiDE34+BXcO@*q=~FLf|GKEzo90%i?x#*)L?fPE}UBZ+YNt$0DrnQV9Q(Z#?#s2(l_a8)Q6j|}pzegWJ6GgzNw@PvoJ zu)IEA{}~^hy*_#M&Xo0iqqG!yJ4plzz|9(za(bkOibEP@Oh7oPQa+1I6;tjsJiy%h z9aG$i0m7}mjea)fNz=dO+c%k4^M1lFekGrjHRahg`!we5%WqHKD7&tqSQ>X+Gr%$| z6Z!B`LxXluvqmY(?*_Wt-tG158=5v(@p#f^!Eu(QK}#CW6H@^wY$7K6ZBT?dDrQEg zu2ZC(533FggXjx*UhvIAW289GwxS*c^UdlP+qVQ2w*Rz`>fY^5na)s0?(lzz8+8s1 z`_(UJZ{NIoej>^Oid`LD3F{l*q4rAWFzsx&n9h8oJBNo#-?7ySrca;yQAw=6h!z^t zSZSmFyq0QfduG}!HU|rwTQ9|wNSgQ3gBr%r#IvDS?@qi0nfs*IkE18LTDKwHn!jRK z!mWjCH%s?w-y_~@I)6DGZO7)Q9-OTRNr7W+OE4#GM1I}I3{6u_<9bti*HqZpbb9;h z#+)p09(h9HDWL#@-v#w?L;%>oA>>Q-9C9jdhHMIWNwmuHjDopG);fk}JstJzKJWf+8&hx=C?Lftp=XQf7&EWf6o)&VC0xLXc5c z6bM~GgihXy)r70TqtCP&cUgMp&u=x}uGH=8&7ixR)$Bmg-W8c}IJC)%blI^JXpF#G zBS@Thah^g6hcXnSdg=f)Y;SkU~QUlHKLG1}+Wqb;~<pLkjsu?ZT9$FzAbiG4&6Rm|gyt-eXyW2R5hx8M!o!GByET8DtWO zOwgjMmTC`z&`IJr*lB*+#!tHVh3Wi+=O)o~urP5zdmNPB35fb72|@>SLo+)MGt|## zZ+=96435&8o9Ac5-Rv*ZX-qZHUjJ|gYmYgywx55?X>IQ*O{#E88obZB zsz09b^sGC&G_g5L`U*=En{(*8O@YYw`LVg-;=J|}=(`f5B2LA2hM3NjxJDv93a-AO zlO~_c4s{ua-_-D0ebTAL*?oID98QBYl;PLNjt*l3!uyAiBs(vasZ<(@$?oy?K z&d-0Ca%^&64rA4QnKKA1J+TC&%<1}HYDGsQ`eXFy(WbYba%P*jCGt&%sEAG&52zuv zK{o;(rdoA0sN-Od&?>kg(5meAO#GKh&?$g>iQU-X31U*xbBed0jBR+*@NkbtMhA`= zy**D+y`?`e1V~MVk;@zZw(HRI=Yw-=PETfL+hQ$Rcj1mZ{k6Af!LBWJZT^<^D{8Y% z_Zl}`o2WL{eSz;bF>b8CpuPK?r&SIgX4dUOnvLRyz=|Nb2_Uy&PetKkA__?0j`&aE zv9pJ*x~iZw%!EmMk=h2h5j=CzPI9Nt7FGZ|56-Pl=$gA_23)v;y#S6Kc8W;>d?15s z2X*wM?dr5f@w8r^ewV#fv*+UPY9Riu2GX8fP`VYrgl%_LM5P-S5kk1WP#ifzlf%Z# zBY5MXVjT(P2c(uo$iRd-0UZF{@sdzcz=-XnhbvWgn`t9fbc*&ckoLB)%f@H$o(Glv zUOM~NyU}1Y+x|41u&Rw3ZP0m{9$x1w*t>Sl&(2q5QB~J(1m@n>sUdDS*4q3OJ;1Te z8dOrsZ!{WH8x+>OnJ$`Zi}@N}>IgDa*!Ncp`R3cWSO8~KP-j(qbzALLjD%V-5J5*_ z3^!F-B1n2T?2X>Tk?^61tc77ampU{Ezp3{!rDD0!$>d+(4+fHvJTHeOH=HeJ%p0L$XAo zB{QXY$9uro`?(gqjp=f2e9vwL=NuL-Ow{tZM`k3ylaYE}4^8K>`So1<9Rwx6v1vUb z475a=Eaj4ezU2H2?T0+C*Pj@H9U%mvFNH@Q(bB#MtsIkv#sK@`S41GB2d;CSn)z2mSmtPcH`Y zzS8=|K_ab`-i}9_wHGSUo_U5y%w7!3_u7L`IzRN}A5TfWDW;;sw zQKX%0I;O%!PdYCzzJ01U7RC~5I9JRvSE6I1O(=&$!#*O?GXo?3MEW+fXaw&0Qa)U1 zqNduPDzS?s^H+>cZ>aK3aPo`ngSf|nO(^rLzSvBsF8Vy|yiSMdB?=9unLnm?>3jbB zv#^Pn3FYa`*vu?Ao%XCr%GRprJk~x`-dFMCjk@l2kAudMAHsLS(3lEi9U$}N$&J4G zFZ0u?{Tg*5!_P-OSB-91YKC4NB<}%$& zak-Q^W6h_tR=l*Wc>G+bcBnrE$8fHB+;1djEP6t@=m8wcPKMOrP`@GN4bsm`kso}v z*3{2_tTh#w5h;=8;~Ujqx6Fw4lUn?+k2)tK`BmzMbx2{~Q3Z>>A(Hv1Tr{Z}mK*I^ zl~<_eu~9DUJF18@>FW+y$cf}+xtqimiuJDtCEE$H~p zK__x#7>OzKdhl&Ei0XW^Y6a%Jh)z^~^Y$301%t(qsDJ*N71ItTWOcYXS1- z$c(VG^SO#{f1i$S?TTY~iA*K5ndwX(Km4DB+o)0*Drxgg!#_zmX}oRnRI-f7TjxZe5gzW`Qrv>p(%I+`Hge z&dv`yFMIiL@clO;;5~wvN;YdZQT^ zfEAavE3NiY4Fa-L>}V!1VqFfIab65+!e-1R>(KFJqK08%yU&B8snQB9ZnkuuPV)UtC;-BA-=2EremNzAd9F@f3g7D8OUms%h_3JrhxkKJ@K}9Ch?>469X{9!gP5VA>U#e z$4Fc9G3z%@ow^!_C*PJ)XEA~_lUvbYEEOcC6Y5c|YWMZXG__WLRNIx1p52w&=}lR~ zMzSC%Hv+4qI6WMm?Eu&h9857RfYRxv0gG==r}NEn6as) zcTmznplB@WPnML8sdkdHIHuP!56UV$V#e&{PvoYRH;Wa@0o9U zqbsE3Z^=2zKS<0VKy%Rfo)=D;4_@ObO)iPcvHYE)m(`OWpHIee&iO}hLvqop!SEx? z0Y?A$+rj&r|L?zFeBqHP^LMwYVzDgQ$CJNik}JS6{>>cw+gz9BItqli#kB9w7fjFdO15?`)i`;clRtb9j(NUk$=iXY&t7qnB1#4 zaPtzFP!)NoLgsFB?D*T`XFu8q;j;EfD@l^$XBVeGonHJRsZ4a-09-($zfSeXHDSlq zpWZz`zBqn&{>!Up>kdvIu;$pyljo<$?_R$+{zY1U@Wbi%KfF6Xe);+ZMIY}(MUpdL zou9oqpXFs=pPgSwl9iOLx$pUb&Aq<+_Ke)?vokI~A^FPB|Ml$&C!c=*iYL=Q7u@2P zcW19JPG6q>hrO2j)0~&*rq<-`ix=%HboGB_tLqo z->0|l_v!8XeYUTd6PqqUL&aT)^$^&JGit@-kdpF*8uBP}eJ2&ly(p#jH$#?yV(}ss zMKm8G{O$@bWy15j58vm*_xT%spS#x|Zv8~veLqom-%qqXILNEuAVrX(s-Ou;V3O=+ z1+rYgf`I;XQ_mI1Yn%mO16`ntaI{xGEa<4oQDe$_Q0pE_2CnJ_(Xr(PxSMO zIEBe_aGg#>$kakX7k*krN+t8s8|!#Xg)(AI_H%Z}C8(6yqIc@!vl6d@t+2#BK5CWa zZEC=i3n|WpwjS|?u4FX7J2}31`{v{vMA0h~lKe~OyR-9i3HHfuoIjthK1Y5XOf*)V z8ZvsKU^eu{v1dX;c|dqzWOzz7pYXAi|9$l6hqIqM7iS$TM93_aPu&pOi$HW8oTK3; zNlGkM?u=+}elwI9UXeF#&bIfbM%^Fe1z4Ui`*UX4FODN*&o4NZEj=A_Kn#RMr#!Y!)HLub);S~wd zP%144j~>REmu+LP5xF5MOj^_J$EAXoVB27ul^xA>K?h<(8x}Uva9+|4Mf^al~)Us#XFfS z*83DI=e4i3f_*@!4v_wgP?8OBf+gC6lzo@u%e_3Tc`)fm;w&!#F{;bA5Nb?XXShS=Ipyu zbQ%xV;<{MPj%CT@kGJAwWJ1VvQ$iS|o|olO=sIx^zgbZS0ULxc2dXY&gvl_ zpZ~a8g?D#O>wdJY>#Sb*!nU^~4PIf&SvmbWy_00TK5psWbr)C-1?5#SI8*RQXX^;d?LSPb3to&HKVYW%gFHvLI4;d1p919Lm!+mevt6aZ$xl}5%$XyIlWKu_moEYaxFdQ@4^ai%=coC#P=pqXV)f2#=Z9gYWw z@?QfB1^Eni?P#U>79*-C*#&@D-2{W)XXe(Cm_rU4ZnuNk&pMSKb>8;1GSZ3}8AZ-n z2OiY76#$q)#A&UA#8(wEI4#M*t~FkmQTi|KKI>aZn|9j#<#{R4PfA4wD*sf@EJWUY z`fqgvg!-)izAs@{vk=Oye>U+*(fIsiLaPD>?3Y!~x(`zluhCRg3ahGdVOn$t7!F65IU~F|XP*Ez2aHK1q(QVvFK%=*#IplPO&Z$I@D?BJu z&Z7>c2*MH>9snfMo>ZZ$^u~gad3$hy8WWapPZ8O~`@uvDl3ytgr(#@UITjlzZ)|&Z zH0>i+HwLflt2cSsH}ry5;42erHOM#ZCmW3y=;S2(wam2)+0>wn#o{fZw*KhN&(_Xm zIx%anQvF!z@LyN``8dsRxI3jL?&2+sU#Q<{A#fNH>>d{hL^zn0g~Nn1vvo-XdV zc%=0trAa%8iHq#6QU4`*Fh!t);QKJ+p1aiQcQ^7&t!uX2 zkt<&G_GtUddIgxWo*n!&=uO38f6`g}8C~1pUr(>!IUcX8*?6E0K0*UG(YEW_ zhczMfNvWBRV}#cuk(mjF@yrQ@45SPsb{XbHf?lix$_DC>P$-5I4`m1BF1w3yAlHQ{ zjkr>1Y|@J^CfDtJj|0bzEp_$?Ftt;j^aeRFpF{Mj)Po@X!4 zxas*X=NBh0-;uC?arXWBfuTe^c=p3F6vx|Zzkfs2K=T=I&i?)6S>s(!pS?L#j+Avr z-<-TWyO0~56C4ALu|{NjQ;p09^8ED^Uf2H&2Yy?JhK*b?aQ?1z)*Z(p3S%K_*D7yr_y2I2xS@Sad|*jTsIYbr>Q=UQ|iub&)tNmf)9=IU+z3b{}T4 zhnehQCi?NgR4{61t>D zm^DH;y!?c!5b$DbIl~xv8_&(XMR_;$Q4xXLjjZWmI(wMT9;UP3ZaUjpRxmj7w@ql? zo^dm8&$yYlL)?sVv=Zq_e6u`-7T!Za(4)u&wLrwLx_OD}E{Wnihe;O@V1&4{6v1S1 zm?D;1B*~J4=5@Uw@a{QoMuIqan9&|)w1*k(w~w2(n9Te=Co_M~$*g_c%t4`<*jd(8a49^e*!y?+jQqf`kuq*~47+ zFqi$|!e%WdGH=g`%-eG!+ksf1gdYGY`W(sdFwEIQdQzu>#1k`B7cWf^>?o=5SH$Gz zrFoX4ZV-p1Ps*Tj7AG>Kp}p8m9wxGfiR@t_`-4W!T1;o|p3|AT=XBOSYKCGI;r*!e z66$ZLa*3JWxB;qTROJviBRmF8UL{pAKJ!IZ2H=q+pQ)}yDr!kt98WHywlaK}&K{<- zhw1DO8#RMdX7iNh;kWD)H`D9(n9}sR_IR_9l7x~eB4BEO1d51Q0=$d~!biTI0$MVf zX_Qis=n_S9!raST{IDvMR6y8`mf~joKern?_dBO8)C@e#X%BPS!<_b8ve<2(&ip;6 zGk?$NtbOFHOo5g85MD(U8by%Qbs?E_1LmYClALA|q!b}Uex4Nw=usXP7u_QAi%7Y& zy5-`AGeELtO&#^aZ1ymlJvl~Zgfn(Lp zJ=E4jg)Pf+vH@ur7VI_ov2u_vPP;BNoq-bu&LSyi68gAiLHID0JxpZ}Q`sLhcGhY_ zbN8Ik+&w3>_R+H}5V@$#s|b;Ku!5$UFn~in21{ueWUiY?ML<=?0aHercU_T{tU_pU zVj;v?#GJ7`4THpYA11Vi3GHD*`(s4Uw(}R2{a*(nVO0%AqocvijG}1iz#^}N8%x~V zjT(3*W{21CO5Ch4zM@1jR#pj$;HCchPRrC$Ga8E41SRf9kd0bpDLcNt7RubbTqke2 z)Myx5mKi3HicuYC3jXz}(iaqI-<88T`6MAOxSaGvf5)~-v~gE9(ZzO0yEyU_oKL0Q6Ilg3%>vIx4V?;!`h7zk!h5=ZfJ99YP4n&o>pvg@pT zrCAh|*=iJ-hG(IbNFD)p;idwc!Y|9mt&_#=e1s!F-K#Txn~M zukze<=ksuDiKx5l=Oga<+TL?K~KNO5K@G;}mKJfJ6&QCd%3h7`@v+##bREDM=P>Tt5^ zhO&fIBDN|t8|EDhBZO5Ag>eI#h>~)Q#ei?Apty!x@I&V(oR70k?GQccASFo0@qHL_=xeck!wU4&s0;=_>cie%^Q_Jf z&rM&=WcRMq%Omb`T#I#nI}vskl|B6rbbAl6Ewz~1K;-1_p#hPuduBLAc#`4tzAh`n zh?fn8C%msZc=}KM^~2yAxk^J_$2{8mhfJW?mNr~({OwFePmJSf=f|?Vg?fC1(x_@y zT813^4#KEr^zG(U`+7qBQPGQ?*En{kZ(nxQbL!xSoV+N{PMMrF4>FYX9u01kAGr-$NAAW()#b<)GCLE z-|7PYD*e#AGy-#-=XZRBnBVbrHi40EIDSfhtMKleZeW%gyKU2Nrg(Gjp<2eVCl5I(GeOR3M0_4 zfkk~FB7!e6h`T)Sw!+oYJ(bV++ZDZulI3pjUSXo(31lnG@dK`F58B172B z^CK_yp)jEKB%+p%Ky92rQXQ0`Qzh<_5KH7oP!=vnF*f;0ezygUdUuh9utYVch+=?B ziGzzIg~@r=g;|);MpE{<`Z#dojYVK5T zpQk_QLb~|F`CGHYe?u!?5KUn7y?NKX!ROg?FU)i~I-FPcOh3&A=->Xu_x`0LMun=0 zb&ck@f7kZGzdTtVF+7;tY9EjiIoL7DyWcLP;lpuv|JK^?NkRkxsm_WMXgo1nqJbX5 zV^KGA^2~<~L^2=|yOS)6q97x)8|0L$LJ1F%h~r54*6;DH1rOia!?)H-&PY5&UjK3= zZlf~0T2Z6b14}zf$k*KsJljgo?mKIDlrO(OpS$bgo^s)!+0H5D;H(`p5o4vVRH{-V z!=8#RHct4M@Riv$EGQtU&-DYYv=yv3n~9s!sNRo^;P!a#km}uwE4-@U%$rl zmCbK!ze8Y1Qp07fqC;kv4rOZO@KoEUhy|d3h>AE!2f?$bCzI{kW5qI`~? z`p*cop{o8J5s!1jWBbbT*xHx_a%%mfZ*1pE3)FU1m*9;x+5=jHwVDfF&1=5d2imKp zT_YJD%ZB?wH1Er~uRvIlWh*$cZz%Jg1RrQmaY*26tkEl@ltvg}sP8C|19`B@+1W1- z_H6<{vixYw%&$F&t-PY zvi?*@nAqj8tp3gLZBOOY=a=jwUB;i7Gi2a5o+ajys(ExCT|jL-Qg-5=Oi`U5umT2` z=gW^9o`0n$9(^U!l(V1wRn%+htCqrFp1(CK==s~jU*>or@3cU}{|di}}mk%%$%ZZ4pg5d0z)ASHpXGzrU~>xCKVL8s_Kj^`pSm{nAX z@OSD86CQ=U2|QCv+$uL^q)36GBlJI(gE9aRELq#j z8QbtFUZiO^y%q~H!(sgV=JeuZ^*ZzO6n3&^%c386tsHoF{qe7t{4Ux}XY}VbyX5IES8^w}8K)fh+!pwzIk8-*^EHO^z(-ax^ z(u)8&Agxygfm0$C=wZh&zT|#EiOXVnvFm#ggfPKQHXCfT+2tVAo;JJQ1b=r6+XWT! zA-$XQk2E(wgM!TcxT1>ZI)~l{RXlrm9sZ-=>wIT!xg>)}>;{2t*$&AgMUamCvQ;G4 zU83y21dL^N+eJ@e7LpDXBU4EY+tkGn-TmEnpDgQV_~Bc9Z5^LO5e=CPpGe0Z+Kz4E zX*XQkm{0vl&X+tm=g<7$F9t$3XQ~@BHVfyq2X;Gr?oKz&5|-P7Bn)ZMEMmS29S@fr z$?1?oUM`xv-3Z7S{Cghdc@V*nDvcL`-F_H_33K1lyyd%}wbm|tjxr;8OsXji&2c3} zfId=kUerZX!)KX~wM{GY)|zG2=3CZhRGuBbIC=FP-BR<{*{cg6y;~(nPhYW!&r6mr zFT&H?+CS_2E{)$mapB#fCpSK-N+$6-H8bGrQfc-h0WDfdxsB&6p?^uid53Nz+RhhG zOJMyCv+XM*3%@wqzS4NCiC+%DVjY?}LnlQWyS9OM2^%SH?LARG0l(S6=vT`Vt%Ozq zsEO7RanF$iv2IoKlgN|_9*=6_sgYzj=K5`<5l{Xp!CZY9cqJogX5v=zr~UYKMxICC zk#ZYZaNGxtnh{bT%lsunN5FCL-o)25>Z>ieAvUA}TukytO3L7muNA~+W5G63R1k;19^4{>SqJFl)>G-F>Cfn3iDZ%ll=Qa$+5zpD5?xSvCIiWn zSz0FP!Qklf`bqg+^!LAi_vq0N<@K$+ou(hgHjbv){@x^N%K4j3$Bkw@8xB5_caZm-hQR;x|NcK_7$}2)nnOE*Dg&Q|aUKY^ zsaK&=r3j@Dw1kH+oyxQ6UWsU{m3LB2ER6KV=MvxL(o#a58nZP)P(4%6I3+nG0c!=I z$r&KhOTmQcg3e$Q3)8-~4eg6ot`35E+GM0jy7bZv#%vwd^DhR6#XymKra3H6`PHE& zECi~hKk7F`B$|k8KG6{7m81;}u20W6`6S`kCpPC~o-6q-%{1v5)}UTtI(5QH!EBoA z5TLykKK!Ur?Rvo!R|rJ0{nx0*w3WrvPVH3W)?UP`ux9i`rn5sceviJJ z3{_@s;ZxC!fvEr}#k~Go-NSeJ)o{@7{X_dk4-h1tN7ji-nvQR69{+6`o3DBbo}DF6 z>Ih#QPq!(a>1;i+g2O5?{1w8I)hxxTX`w6m_;j#8*3;qgJ-FcO>udAuC8!gA&R- zD*P%a@q$ZyUId!DNSjoCUUa=8A;w!dz?K#@<+_m{cHP}{ll_hE`P4z14=Y4Y1&+&y zf+O(CGDWN)Cvc3%o&l_KqUKQ8{>7Ek7}>o5r%qq0_r3;7wY^kuZ57~ZL0?12#0W?m zt5KZ6Jo<@9ioM_bY1b)O23Dc@GwY4W`48+Ep1r-;dcoUQYcH`ChV_+~z#ir<;{8s4 zI=%Qswkc=dzdF_7S#z27UdhW|j;a+n*Y|cqW_Cd$Z=3;RD#u4n_^)ZjsCM5g*#~K* zYm4j8*?rmTdEVyF^f$t{dC3NR+j^@IfZK-9s7!87T`D_HA=iW43+!h3y42T|%i{ABt=tXmpu1V!!ZGGA?BWQ8uvn z#0`S5;)Fblxjan7ZUcN`-ld z7DmjM_s-Q?-w+H0dnb1{#q*>fq~^C2}O(OpB=xJJKn`z6A(c z9)hRyHNtOC&F$*q>St0Zeb6yvD|6WvgE+Pzd;r2j!A3tiG1jPKU0VdNNPJ%nw3ujb zXsprZB6sBM%qF^n=Wq5OxG7QXQjJG-nJlG zyd{QKN(j3wz?N?fquJ-JcgErkIqc@yA+!zo7FAc7^q9u zEh-U;OI%_2M|BR$|85$%WfD78n1S8NfxyH7cH#w3%mbT)a?`RW7JU{3vOAvx6(1`~ zQBcgt_DI$2xRk}nBSb`&-5EW$mFIo#8j z!`8-3k!%R1o$mqF4b#Z;gzYCQ-2_TII7cfV$eD|n7^vThB35jwD*V{VvM{c42;*0c z*^lqrn4K@#nE&WD+ikMhU{9M3_O#jdLIrVx+Ikja1sTRzMO_jZ?2J?D!BNS}N+)Iu z4Cc3+1<+QNT{kQMr&mx}K{@G^#cyx32o`rAZ1%SY5G^+Qwqh%EWeWkby1ew>{b4fI=E}-k3YrEq-pdw2!}&8uxlL_s!EsKlf~_0N&)nt;o%nUh|+vHq5qA@y4$6JbBzM0x6X=8$t_#`ya!yTHa^2Fz~j%^LlbU!SqgBYH@bN` z{e~?Z$!xPFB4c~cfW&Q#Xw^jf%1Qtcf})0Ghd!oE6sXvBAvMTU4uZ0_(@Uy7Eqyt6 zA1O$d%k2HY?!nZ@0j5baf6h8sxf8S(4KVEFt`s;Py+i4?#foXjW}!EaYX*nfaP)=L zuJ!Fo(``c8mVWBpzymi=l2=lFuT9o~gNB>vSN$Ml{=<~(?_c>BOc=*NlLU)JHwcG> z;xmO5Z>(K4(jbh`An-2#b<*SOo`RPy_;RGtf7hRba}OA01Y@%gHJLi4<;b14#(*&D8rGRQVtC%p0xSx>C9pl2lq-R z&FO?>t#;`Xn0+c{nl;~*QpuTV?Uvu6v6EV9!%z1K=c89DtpnCu63`oWj%}dqZC_!2 zJeg0$o+*zLDVFotcAIr&$>OCJN|WJJrfVe!kN1kU{~6261c|AuEaN3ERVBG_QH}v|-5k zm3nCK*mnj_2=J_@lot(MFmJ4>k{cdXee*E<)xhY`PVzf2B7&>hVU!umiVd%8lwl2KXyU=O1XI;K%hiX1o_{#D;g(jJdY~hum zh#$ZXMs5~`y}a-!IVYx4fK8$Hv?6F2L8a_c83TRt!iH7!_X2krm?FTO``oamsG3r{ zM~NYMVpx^E((4vp7ZNl6&n-9PZqMJc9~4fojXTqN3%Y4v^Myr9nh>8svWLk9$sQ&b zB<+LLah_%<&lVX#rl^cFU;z~3i?|s~-A-DDQI(ZGQIa@@JI^CUAED=nc_vc&l>YW6 zNByYxU~-#GE^Nf%1>Jp2uDg%Pb=!xM6OUP|>pOnrQbXqzdFo}NRplgUH}-OD4V6%& zs~|yylbw0Z&oURC_c%%1IA5e_^aIxoyTLt{{;A11$%D!56I(0D{fe^=!}MA#!@}`Z z9K%A#uSV8fg+(?;)?TFI-(L(?!)mYR0Rq>0+EA(tq{zh5&o84G)NeF*JpOK)nLk>t z(L1}QPJGmk8mT*R` zkutUf=Zb`QIZ$HAV~M9+mfvWf(FpO}En31n(!9{mLe^CbAhv&A8l=aR>&dn2P|H~d^b$k_W#a=Rb-LAI)Fme?;C>NmwvC}yF+2|d)%)7w+JS&CVnY80{V zP99W8Yh{xlZJpsfZ1-y)kB+`Pw85Np@4g|JUiuk30p}>#wi9So0e=)UZ3Nn^kPFk+ zyH?rOrAyi;ttfMc(1UDK(G8NW5GJ9_n8C~8_%1(Wd5|V?6!~T5<3{WFi`OoA3tjeQxmZr4 zSMTj^Ki=;4)1GX;%E(bARS`fY9h34z3c2#qjB&`)(DPFt_(|p$-9$*YJOq58y$;JL z>pF!4{YtuOKf%4*&ysibKZ5=2!NAdA3hwUKO0%(_PrZYd!08z2Blc{f1sr0#)&+u4v<(kbAEZSP0$bt*~v*2ye{|=csNa zbC(vS@r^{SPP3erGD5v%5o8)@mHQDS4~C4`fHj3tT*!@ahNXIVtSWXAoK^?X<+C4Wxm=MpSC*Wde)Qax7flDU$|n&68*(%v+F_jxiGPj6`aiRTFW=Z4_Nd@!kWmz{4f}k6RQ3iKZP^GDZ7)g#K;UY}Z4Fg<$cLnsD zY+69QZRJ-BD}l<7gm42Qjso>o;jo*C@B_ODHpX5zWLXV%ltjF}K1xS?KhVEP7s|Jw z^-X4_YC5{KVnK8nA9{_kpy}bYE7k=!JvKL80$0h8c9k(Yd_9t!mPSWt+MzN3AQ|UH zh(RSV4{8bc=8rn-IGSWZ%7v3(ViNO;4XXPfQH$kg8ZK;ZF0>aPgj7gm2F#qS0T!zd zP!qimjR2fsc^b{#j>+j! zR5Lt$!b;Lt3;}?hQ(33471$%B^0oYh;m$v0O)8`#l+6F;&M;Y#zy*9rsm(>*=c9oR zppHR3DmVl>g68!FU7@iUqi$i|kK{Oi7K3s$eTGriu!m>wuPj_NA8EPkS%=mi00AH= zTI9-g4D9zEBS z1*wqF$%UBaE8)K;;JMj#nwvPC+{kDOtXc+#>e2hE^XOFCn=TPJs>RIS`77}+dLtdu zbT;&ZmD}fvtPodXUx7XI{VEYt!{WQ{jJ9DfzWCyL@?>!WX|dL>xo*c5cikN}@!^>B zmw9k}C;gi_vLL^=M3_E11NMH)kUnEdUtYAgmie?II?aQXS zgiKP(jf2X|vy4vl$gH2ZFsz#qb4=$k=Pfrc5LMwLfyx@P11{Pg_zTPl`+Jo)AOx5sau zHvzvJHRYH8Zlxl>NXYvcoVxv94)>Le@W;U{xitUlD$iqh=s6p~+D=nx?1kKJC5>c& zyvhZr7-j=K(=DdmLW%heN9%yu^DR9J+;wPd9x5o|I{$XzJm3D*^(KN2+JRIe+8DxfKspN#`J<&+j;#W`3N#y@J!VNe2N*L7i zb)E{-;Yi9R@2)NScA~3{3&!nE;d$o0<%#_{yf`$93R%7y3{-P!U{!XsWs3(>NtqQi z192CdloPkY=)TdGN8eIRYBAhKQQ}gxcSa_$S4%-J++$E#X&LDsk?}#q#DbI`)ezm@ z&}!X|bYCfnDb}*ox`Z{|T)xzaIJ5v`u>D;4^lnqa1jEWj>TvJGXrG~P2 zA7RA19vG1KBh}|IT%u(vPT6EQeZi>^mLN0l?6^#+L3I?qVu=+!AZ?!P&Se!U?3Uk% z;R>*f*4YZ%X|%x_4s?~e&OM3RJvVvM%(qcUtE`Wlm|Sizr_}7@zOP` z>+q5q2b;-l`K6%6rqteNJqBY_jCX!Uco%F6?Jk|!dSP>87OrmBPh+Z^g!mn*crr_s zo5I;vLJg8g{izq{)JTOH5Dd=`y^K9!$)>P$7xPIdX!>Ci?!*TYZpV}@74A7jUO5rV zZwUQJnZ-GUStKt4$BDd%{qmNG?!qkl*-YTDv5?RzNb&932j<_zq_p$c=U}N3r zy(VqP+E3b!wcj4NS4QwLhF$QYNt^*paERhmns4#s@7S$84@yQSPJ*gTh&v&PTSbLv zLZoB>liz-C6Cuue42j$1OX4k&EB z26Z=7Yb9dZ#b%7w{DKoj#H*_EkTWa0QOS-qB45KHV#g0%HTk| z6|hnh+#*?5OlCrAiWo>|G&59R+l%6r9#W-k4_ zmX{gucTYH+N+-qVE(6vBPA~j0&vM6K zmS7Di4Rh|*vWZ|V$yq*F_V43ZZ!zjU9qVvU$2x59SW~pE*rInAVK%RsQ>tm^%jfX)3FZsbgaW2 zYD_2(s3L@be0GLmlp>W;bbW9KQqwExB}}{u(bDj%#0{e&2uLrX=vkC8qQmi0l|Upw z{U~{`=s$~Ny~VEgbgaWY9qVuh$2zS*`1me38fxl@*yRc3ctwEz0`?qu_#RO}ok>QK zl>_=Fand+R{WR!yDOZn|Lm)0T=RM5SecEO({OStqd+sU{a-DA2iR1E|7 z!Lt7ej`bF!-qW!T_H?X+_KtNChI#0KWrzdMO`;NZ^(uutH!s=uc_CI4<60B0u898O zXuDN`fOzD^PFTc6wd`1jF&s+W2c!PuJJwq)dQZnX*we8N+B?=Uyf{uSlJccz6`Mo6fVSkQ$S3)Fe^c;6yz~V0f5qo>_y!~NbjhjLtCJ` zG*dgSn{?d=yZ%Eu)|-J>f*^4w_*k(? zQ_>?N6J=pED|%KdZ9HK_u_#RXAZYy?bDjeA0ru;Q3EvfH^zqSt6<+oGn!e$FrbUVhYr>efyQVGb zTt`spOYOgQf?=(_cE@V4rn{`I2wN%GItsT{S$nM*lk`UGtE{~y&ZLzu;GKK{>lc@` z=eAx@oT!_ZDV{#usAWKa9XT2(Y~ZV`STBkMh4L(LQCkAX-pykQvMGoX0gq6w%NF;u z0Z|^vP2Bq>5i}Fde7#ngq(w&N77jx~o?a3aeV8xxWHt z{o~wn^6vH7i_>SntXZ;5?Z5fu<=Yn*T)?8U%W^ht%$j3H5BT+q<6q=W_faZ2|K{D;Neqy_|zIc zHNWvw^LO~v{2e~E_5m%3Li{3k*>psLS1Ym9E_vbro;FH3z!IlqBan4QRnQf5W>vb~ z+>0UT0`$YTxAduXK*->z8_@ohh-%d`m#+i`?_^U zRt2}*4OB3pcv@b(ertE3w_C2^E`>wNv`(vK4V5fp8}EftMYzPIaM^IOuF(DJJr^jx zgf>I&a3g9(T5hA8MI$OZcPkMnnLT=Bq-uF=DHXi6DzVjx!qXp0RXHlRhtk(YEz?_< zf=`<(I<`XjqI-)ao?k89mTjC+b%}(DwCtc(_JM#Ft>eoB%Y0VUKSj-O1t&b zuY2!hyq(Vq&cbS*!P3&tWdXvVzDH()P#7FGm`pwwn4L*)chpUv9($EN<%8it38+D=& zyjm@V12r!ZG>WAy6&hwI#3oXnO=#^S?@*VMkM?FM(Y7`0RTq!7-ILBfktelHx7dxY zT-8X!J3F(9zRNp`wrnetHdWG5!{XaiO1W-&c-K@-x&Gw#er{P%sslAD+PqT21JyH7 z6{*^Z9Yi-GKrSv&fVe~)GKho@q=Ni1?@Ey^HJ3|h@fb+0v#f+5-u!2TD9t=KAENYq zFb44ECsgysMNVp~h$C1!i#UVfHD0s!uIr@Vq;R$=dJyk2)bTDu-Cmj5g$b9s_>wLI zmxr!$Hw^*RxkcWk{|E+z)J~Ta({|xQOA}%WWl&WxtCZ|rg3wzYYJ{2ZeW;gV9$)ZK zH|<`#y9{-Am!WPiHj#xwx0fULN=Jk+Ziup5Uu*pBE~)`0y|hI+G8H+e6`tw)HbL5wE8$DseA2St(yHyY<| z(;2Jr`)ITFgE_p$$$RzF>33EyawUv!9JDtV=daNoC_h?P@AP%YG-8FMXH$!hT!;Th zCB;fe-#(^haJ?C1y*al~=xfC7(N>6{S_;pbvhB>;G4HlPjPnz1U30!r$;_^qm*o55 z$cw^%J>pVjAC=aKBRQzg6$erL)xOkfk8<(V_feLsudr+-2M#gL#k1E>-aemehY?U5+}84mJ!P$xWXMMq z5EVO66~nq`YPWG(0dCVPa#v9k6<#~2^rrfB9d-51?<#Qj5&srv@${v9S;wy~I(%m* zGm2Uv927AQBS5PRwA8{4P!p- z*@D&({16u$+ldLopRdv)_S+~p{?m% z)-K1JCcSslNpJn?-0Ir)Bz&mY!NW(;m|C80m4~8&=L7{je4ZNx8HsqO>_+H8L3@{z z;(_L-^h40-8Fq``vMd+s!jf_BYQk+21_F_B%wVA<+{EV-JQ%c$TO+3&RS!C1@cXb`3#= zL)oodSe8QH?NZ-^N13q=gg-J8(_Q3N%+H4kA2B`go?QxOGaoa8ZO?ZhfLMA=yyh8}EgZcs(lqHQKF^yG>2 zV4J^%ZSIc!Yhj_?&#=($XISVCvX5~{ei&i}AGn{xKd4<#$}%h|5vv$|N)b>VP2(H_ zLQr;Yn1e86x`jJG;-n=DB`lXvYxZEFzmbK09*VCT(xSUhN7r|QzH5R33qP&s(?A$7 z7u#H=M{^aOxh}!kFpasw3%*S&NBxAt8pLqlD6RWn}Io*`B>;(+-On)gE@0Aty=EiuI( z8rlW%U=g)An3Fu{3`A(1^wYFG7lWG#+s(^iDw!yKH9GUaoGER1y#41ncZ zg#;wHV#smje^q3&5AbmMB0@`iH3aSY4+d?nmq+B=iV0FRou8FZSJ{e7+GdFUZ>`L{ z(Gcp$He7bYSE&lsWP{?a^S&=xC*6P_K$JW}kmdzRC+T@jqSXpUX74xChOeJI;}pKp zd>ax&2O@zWml^JVQ9wZGNVMpAh`!UYw#{}-uBLlnIQUc@V9i)BFz$&kr}F0+$sZZT zYSPz8jHKSG>Ke5mebDJYu8BsxP7T#w=fA!&(wqmH#+RN^9(W48biF)G$ ztEB*C-J#j*f7LMqAgS5@{HJE*rHouCtk{}~&F-du9POFrI{Kv;7Yza$UxTY};sNMI%%+HI`0_7bmOAo$a449c?T>kA+eqYcT zSdMNWndon03UtC%EGm}Uv&y#QQcIbh%HmZismb{x>%1iM$o16``lz)o(!$F0PEKs_ zhgMH)FSDK@8UVPbWM`OHFa@Ro4tNy>Y!2B`qP++YFLFR+mnt$%NnZM~;OhX=_-q3f z3n1{IAo}s{)A^Y$v2)Ik6p0ogARaVG;l{!ZDp4EF-3*c;;6_!m^RqvE=h^YglQ+k^ z`%A~q@o?mXW8?@f&fXki=~8RzrA{B-*K2Dd|J%R# z-oJFjL@Bedh@m-ww6&f1FHg4K)QdOVbp5-_UAJ$Drk{F!->I6lb#(EE{+B1S31Q;~ zimAbCo*II-Q$x^pYG{wI74o745_XAg`XJE>TY_76O7QA%A7!pD1yay`1z>Fvn-3w{ zKyM_>$pmI)nz&0-LqL)<@a}bLaFXSH(Zkg6Fg5&{rUtipYVg}m4Sw6Hp*KGm} zDR(^p`Rvtc=e4=zVvOn~SO>!fNAo}b_y32`A5aFYg3bMnMAVGLwnd~!h>Y&g0GvQ|?2Fj3 zQOO4ru(=T>YMR#P?v7EosQ?DygQiR@0>ViI0t#1`;0S=$rjN68k#!1ZPFu zSHIAyG@I?!>44qrdZm15!%1Ij3#2Q)R?wHqg4ik#8#JxIy63(0vQMa#3Wf{%tMmBP z^NTsrb?v#j6$VCUV0c=*FxL*F(8rCR-!Z#$x8nl6b_rjfPtVsb|KS3p+~eoR=RbU- zBV_wXX9h7KGlI1o%Bb1UvNG4Ah(UcNjlI?ih^roQc((}a^-R2($M&i_GegV7EX5B8 z8a)Ty)e-SvZXxkcjf3ikX>?>pf|Asvz*Lo`^LN69rXWPyEb_Ict#y~zZ5f&B6iFLM zh<~JH2`JO0@&(^Dd;qKS(sU?LHoTU3M4DERR=6IZN<4d@ga)5>rm8EfeMh~)6FKz6 z^ze7{6R9fVt$ZtwXOK9tDNI41CWEzPMkAS!Ln) zc}iDY=J&nv50h;68uNi$d85uVu!1Q2$f!yRrmqv&%6o{x{FO9>oFH@WYQOX)a61~) zH5U?}Hz_3Ju#9FwcDeDP?08s1tAQ2@O|+dCnEP0YZtDAv%@5jAF^jo1ZY8At?fLUV z@9^2RSnv99`V@Xyf2GIdCGP%#G|v`Ap8CQ@F&=q+e5EJbW9yvxM7^g&yCj*sUArLr z^x|FV9vYsyQ31HEQ?VS#7c4D6&cChrjUrRDUNCstf9CfmO=7_jT4~NUxnMuIzj(iW z;CgX3eI@1`y&isE-jLGn28v6LjaAgD{x8cgD< zOTvrP7E*s*^s!)G@kC-f%>uB!WtVC@52N;|&Pf-I+V>&^gxm+C{&J1_o-Eqi)1tjS zEqVv_ASdn?NnCc5Do)%?1T;f0$>P*cd~!2gXwD-l2D97+RM}0kG!V@d;;+<=22=_z zT66@;DRGhqi~d3_dUsIfX1jLxv}<=yyKc|LDDcY^v8yblw6KdJEc89GC>f$4%RHTD zp;I|!B9tP&ivWug2_<6*H)D$X;gt(^9jTUU_+ZyxuwC!WbRLlsYg9dU_H?YBJsoRj z2c{|?8X6pF$lj2-PLZ2nYIZ2o$k9MA3Sel+3*~@~6S3qHU~!W8B9ZNuUX%oj>Om25 z77m{E{aQAf$nJw>|8pGcO-8+^W9{teSUWq&a+bmc<)yeH@DzAxO+?tO_#S=+2p&TZ zyf}sL5Xm=4pwrwD4XrfprhY|T+#);?{@Yp)`Mm{2SL*`1rh z3qTPZ+RBhrz><%T-9=fp+jSrfprW93+-0V2h!5i+cD)CS{!=;D&35hXY1i(acHQ2w zh6OK!y+VQ?LUWN6zK1GL24y1?Hn4aR;1q6p@-OTkbClowAR`DU1$9)`EJdsxiS^w# zx5a~9|DKNZR`LPmhARd+jT;^1aCeryK8+kvfqWwXtn!m!b2vKXCTM-d5;#0o>Vj?q zINVLtG#bF+=bi8D-IW5*-hL4%ZVz;Lr0fY5xW2v!2^<>UK!<7H)UlYJ{HP# zgyX}Yiv|&a9%qR-)`3oGWy|YqSaW1fX#^f`?dx5 z%dmybZW`;>k<^0$u|Eiw!tl{7)Yjn`;e9aE8alQmU?K;kGMKP*Ln@6(} zB&i#^9`r8{^@%l0Hg7u~z`b;PbvH+utR)$xikn!*=ZeV};rjOn9jXfT3d$7*17XX$ z9QF(&75~<@YIT#a{HQZ%%FHZ%^MPr5e^OjdrBBRg(lg`3*% zfu-(<+KGvjMGpFhMk7<1oTa8#ieC&_ReF8E0Q3)!Pgkr)*_$kpQ-5H&mFEsi3;SvW zR5{Sj*#e+)cHtplW3bwZ1IvA4rgRMN+HJSa6yNDl4__)Sb zhtIACho?|~Q8%M?IcAz`v5VMrgGSP*H5WCB$hao{w3!mBjW+3qFjj*{(eMepYYNIX zp5GqjkaUqlt5Uyw@TYcLn5-?5{DhymYFc;FTGgHmQ@BcoHGF{S3BP+?SXnABc=Keu8CQXy~rrU6Aaz zVieFC$-&X!y*x&vU++{A>zz@Ruv%uDPg?R_LUGiZSp_z6UdrloEO%C+h;k)n^j#^+ z2wF4Q8;Sgc<^Q)TUdwLE^QDty!Lg-eR`yFHKUPCvT58KIeIwOcLPPk9vgrZ;JMz9!mcGqSv2%()-$}ll5zMb>{=0Gok5!t#3n#H}i3DWG=C{^x$ve94)|{ zNipcE`7I=z)ZR`c5Hz-<_FqgbEUVLW;n)5stCZQdlP;XBtB7fEC*raE)jj%jw8O-1 znGiqVsT@2v1OQStdN3_&+)~3he}kqvMl*p|6l5N;@`$~To+$mH4$`49vW6l@u}Fv+ zFSU-3s>159&o56_Z1hR78@z;b1x9jCu7pS8Xyaj7%5lqR~wrh;t? zkMFlxdi+MUBd@VKYsH^~E0vN3j_stxL=)o*` z5Zt@uSt$Hs!4QS@Nte*`Pv8IHHv&fUH3+}+#G z+vmh9uZq1S$bm3bkVobz;pm+C9SZ%7NMsp#1(SS5#Q=Bpk!X)Y)R;q$ta%OqWHBVo ztWV6^cOUHhi?{Qg70>;~=)33c?Xb9eJ1p)F?1xOyIsAbLFvKYBP{viE$%q~lB2iT4 zlAx;qcpw%*34kw~YXH~22Wcu4iKQEP%MMFOwZU#vdDw#eFnBQWFT!DIw(`9l7I$xl z#cl7fFiE4^Vt+dC`}*F3Z--@vQe6l_sEJAa5<&P} zRN>=*S-gsh4Dxmg3j8WW#1hE|k6k6fb&si@Q((lYd|?s!9UPWm85{6m;{Pm%rP<2& zc37Of9hM!+cabu|IYB_s0}JQ7p;V+NNf#y%mnvQW*kbz>95^7dqY4REH^D{Ea~1$t z7~K4Ka9AF!{0nken(cgVhsD|3VcDUaw}9h`tz;K`r`L^LC!usE>xP6B5#J!R=%y(} zx>z&}eX#aH%@0fvPn~M?5(50_cW_u9?EH6kST^U0rDf8jtS8LoCM~@9+fo)d5U7 zlVoPHE6L2xU{-F;U_ki6oQ!51*x?amv9J*OLU$6d7rR(M3qb?vLXRNmc?Qsht~8IJ z@BhvG815b(B&)irJNHnX40ks-H#0Z?58wZNl;i|%A6amM$PhMZGMD%xDS;O+Z~{6B zBJ!hGM-0Lt8q$~bbn%6vbl6okDlGS>-6n|VpQ7&ceM!?F5oH$Ud z!z%NgWwv=lWLFfTYaFRp;8HshUV9Si|a*ZEa^*Gup7j)z|IPXoClDoYy~M<41-o z9L)wTAWVEvJ|tMa_^(BhZ9w$+@LYqCWa%S7Z9#7-92iMn0?asY;&x4y1wfl!1)I#y z4@16bls*hd;L;ha2CmF3mDPbU}H-`^Jhx;#8SJ1reS zL+9rwM;E1E#z%+e#iye>A%6}nljbMxp>t$v+gq&+*i+lg$MD%2lG9bwZP^WC6=t8E z%cbaMSgWIfIc0-8I((lZ#0Jdf_AbiZ>rr zTt*zDpaXjXuzX+%HL6n8;Ih@IF8j+H{^bq-CBET*uGib>`Fa~YU+?}Aj0A`e4Me`6 z)q{jQ^4?%$56wjPP^#q`o&oA6)9`hl%6L2jL1-X!5$X^T96;c6tr|2J)#Q%#<@NsZ zdjIlX@12F$p8F-&d%om)&zHP^_y)uaI^JU+(t`=U&?3Yu9e{(u6F`RHl?PE~g0Or- z<-3UnKKdA6d#ElTY9G43Tc!6gY|X$Y-v08Ee|gD&i7$C`VD{&G&3nG(Jzw(vU{5Cq z69a@QU@eD=3W{lwZ17wwaRYj95U8~^WR-mbLMk9Nr@g?^!T5~mkA*7Os+|soMzGd_ z#Ot|V@>){)%S-;{C4a(~{E2l?k9za9);GWB_r2%meV4C0AgzY*P{1} z0&MWX@ez-0(@Nl)ix5FM1H4NjNo=ItUCjiV+^8mJbj{Ea&U2pq^RAg+-uy3b{;%fE z@6K(ZjBTXKu22Fdb8oV$Qs%kZkJl)(ETXQ_j7~jeDvWH&d1ckn)ts07b#QolTc^Ql z@7KuRWW72ZsF4{gCn%yUSQN6c7!Gp5NUpf2l+?cBDPUzzItjnCmfvczMk7=ai(i9SkY$~TEfcz=S_&HIhx>rw`^~;z&twuiAEG*`j=<3!tEp?IQb%Awq za9g^PD<0buXPuWKLy62*w*=&7ADP(#Yxwp#l>1WvHGEQRDAG0MGVx7XpsZV@zy7q!QFn3daR)FhCiw z3H&Pf$s&4IW5eGtpQxQwDk>*?0veKzm4KoM{FA0`**<6oKm+Rs0X!m0^PkJ=XtOO8 zML;v75Dbg=ACE5&KOLXGZ=n|&c5L8(2K%J(IeJ(CosBj>IknpUJui0L-gdlgx4miR zBi=Og5pP=m99$TqPEFTB#N7>(z_KV_^DO)s(*O73P}c(sWZb?+g>cBcBR=nXS^_LB zbV6Le{-$}G)yfo|d;XC4#p#PhaJQ@ zLmTzm03W;15Hg4s+6KZBf-5dkbylL0s3Gd9Y^pdB0^eaHGNC%_qjZWt&6V8&034iX zJn!yc)r0$Ac84#!!>?m^=#B#b5(&Rzxm@4EF(q#$ojQyC1{a#9TitI@|) zZ$-hH97VU=BLdVe{>u$l!vco`zTe;qze=_si4?_)PMm>vB5fpXEVanojYJ(ckwXB7 zT{d}7LiLP#C06TOczlc{y`6|iQ7m%%L%7*1KEi)8fsyRPXmALILj(wh+zLqkh#nE3 z)BDMM5I#sQY@l61IXF0-gIGF<<(cZZm`-MsxkU3aPIdvWZ4TO@-=wk^sbo~-XHV91 z4k!>aBhhrN1OT)&Ab_F*?fQVIhNLRW0K6RPpsp8aVvK}B(K1+i*YY7^9V8GL+ zKuh_`c963_^0!1l>|T^o+)guBcAHzaWqZA}+kCU_7xu3Mi*=Ng1YpPsV+(B8sC%@0 zD0O%+aY8pTQ17UNZwfqzNXkHWG$QG+5LLgX2UcX)t|ea~{^-;}R8&OMZh~o@CRqN_ zD4AfakK?0QN7v%OPogLWc+)~^*iN9CO7y~QCRkrv>!;z+rZwWVtr6p~Ys7f$8ri{2 zcafcg>w2a2>}99EXFiMKy8_xo(Ro0 zEHJ^iHXJ(3Oyb%aF&e5Vp0Y-C5_qYFRDUIFWIO*l&6>a*-QV9Ayz8YOYj(PUwB5eZ z!C8vKdOK(7Nw7gxiu)9mXhd2~*3fL7lDf^j(KbgTicX#SIgmERpt~JnKTla&!fEdLCN}|n`FTgD1*J650Ci9GFs6l{A^xf1%(bJ6Bd-_nYpp%WAEU3zk zZXqe-;R58nL>^MdBgVvNnkY;DE|~5ar5}kRoGuE`amv>bB{!0HduHhzq$FYa$v;cBaNHIBhYiq(~qx!Q2(GLV`hH427xCPVK{t9nMGf|5ZpOSq0V_dO8 zk}8jrD^Cq>&+?V}lEOJmA%s!uRC|ig-&(|Hx&7?hHuR9?cF=W~wxLVQ?cnLIp4i`h zjljtr3N0X&W(bfQ$lOIh5XN?b^g(1qI-D;9qRxKogjx)t^4Nm;VYm_an4%=ALx>z; zjr9!(k@0v4QNEp)W#!xeeFf`Th}dQlftxZiLR6!pwp@=R-);Ev!g{o?ok%^#59B+!#4+7(uh=oVT(HZl2gbjp%`s$AW?{ z6njL7*E#{Iy$4eh^*%Iz$lRNV%mb4bx&aDxp(W{Qu=*@c%pZ>%}7GFxmMUmIwv$j*X+BujogBQUaKId#`QWWy*!HevR zp2X1zCfI15#=+=nLEPu}^XVdp_vyx@d&3x>mwY!?!ofgHR?>RA=ZG!L(Oa=V5SSy^ zh=NUEZAz&V=(bGAyjrrkI$zxLL*$oDRx(t&Tt}2OkdGv1gy{HZ7z3EuGQg{%%Ef%q zy`-QHZimw+i?s}E64GlQql`IWW{M!aAwi-!DGAB3!uTpF13p2G@{sxyOktxJfbAy=ijNo^NaV&pd#Hkdw1gwukvPp0U^Agc` zz6fz<1pMWo;j$wH)!{$j%E}Xm6kV6a={@O!HwErA5_Tr)G9$yTE%ZW|6_hHSZz4$SyXn zYPHEPu0c7H4!*@f+L+pU<3nQ^ zrZV96rb8iJ5SJp_=Y+oRX)a(nu>}Gp7|w|CX(qt=LFH=-$}kss_xi`QD^^2_H=VJX z>KBMUlaP*1SeMbFP-GH$nlNDOgU~4eswfodW7_u%IYassIXb)^5-5MQm?rWI*>q$x ze&Td|A@jp6U&uGyVm-l$J##Xko5@69l*bR`-p4yJ?# zU_JcwjjOha=IYP%w(t0^H?6VOcfI-K-tW4rQP5SGdIap9jtV;6^n42%xS*~~i1^P7 zYzxXD&^HHzu@$(M83|Bv$1!+|u{+e?b)a6%x_e9lw(XX)vCVRJ>MuK(@Dm^bZEP#1 z3->hijUs^Agl9nR6V1mql`Xq=3TMks z@s=Cj(1n7T$d=i)h_A%;0viJ?0MstPlql(1Mr7&H`yVdB>xJkf>nzqy=H|y}&=LRJ z>FV!q|MqJ2_obdmP(_mUCsWjG=$DLk29PDB{I}|%k_D8JPAZrf(M=BcE_?a?GKs{R z4Jf{@oxGmr|C~2-qm+Wc603V6_z;eHaD5;3EY{hbz!;RSM$sdvi7P_DV8qNF(QilCZ#55ia zPUuAuaS7X_JPwvveaOHs3WrmYGHvQ)?LDS7neq2=W3&$pE0sf1d@Wr=JCxt5e7g8& zF-`W>gYwV~@UpX;MT4f4QS&vF7aV0{f+xV{Jdev=eI#2SMiIcTHfFy1(Y_YRu^=A} z)ntGk9muV6UqSME*(M#=?+A$LYN-njQ@*aAkMF)A(c?lWYM+9)CZ+I61pKe0TEk z;_~z9`SJVD!xo#QZ@8P|ygru;dM~Ry8Bd9)?z9m%=VcY=E~Wsy_i8^Ie1u{)PR@G4$8AL zSSi4rWaiA*vyiPwP8%Z?o9^xX&EE`Os~320Z-4N;`m9AThnUY5!K)i4guPXw=J_4i zTA(iog%@ShBed`-74`edYSf0i@1urTeM0f^p;#`5YmbWK0Em(i_o0|<{P?>y1p>1U z;W%P$^j+mYsJ;EtvN)#ZMjQ`geRbuK2% zLmMOs>WgXa=t<_JO%A(qN%&C!RL@8R{B$0*LqdTzZT{xv3Z)--v}BNOd^T?R2xh16 z{1q%M=L5BzgI=G%rVBgr=X`d1l+NDr5%PL{gpS4SaEI>^-?s5<?QK z9vAn|lbcw4HWaBtJw(bJa*D`>;{#y$jvkn?qXD{QBA*t3`xe1JuyI(BS~Tz{QtQA* zRoU`%Bw97?8NXEd{#N=@f%pd^k4W%B$r_Yi1}brFC_Ki{;;_TS(Hg!~eFp6($(yzb z5V}uU8tzk;Mt=xJf(pzC{7O~=8xZzM3?T^Lj3B;g1_A_%;7J8NGQfODvV(a=16861 zO9woP79={OS~wU{BiDZBrQz&TJo#6;G}^TjoTn@f=P8S02MZU*trug;Aw^&s7+@a; zE)e5DRJbN|E1_L!x)An*@{JREhUXx$fbGJI0|8{N@M{EoE(-7cdic8zTMwhZ0Um&Q3jC&do zB#|jl`1h9u+y4V%4YNVKK#T!*K{V(_c4+>vy3XQq)Dwxk9 z-($ukGsOim=0XW@1q;Z-U7ry~9Lgb?KTuj4-J%7fV&iDT4m2tdDl`25TMv$ zJ_Ry+e@nqYbcKNqceoeeyIgfe!7AJiT>=F6Uf>9w3pncxo8Da3^sK^>z9CV$Z#fKL_%h;o zUdo8ReDChcVNQyl{_P*vi`Cx)mq7#cmhc^8xQSvef6wWfX!K%vn;fbkR~0*V)!%7w zGj)+`YBJ>}33YL1mU`&fz_A|su0i?cNPk|E7rmOr? zQE{V8=2Gn@pRO)d4Ju$#%3DUtc$N&;UhKZ`V{R!28rOukHlbgN1Rsjg-)seDV(_#t$X(oQp_x1468d-3G`D{m#`vV#1RA^)T$8B+Y3l2MGy+H3Gwi|_eCdE-35 z;)>)gXcCG`36VMBa5=*0OKyVxOb@_HXz2CfdfLpw zOTMxT;M09&|LQ&6azUXTK-w<`Rysl)8r7b@X@lg7sH-%vT_MpACVdZ?bH^7p3ZOu_ z31WRqg9dlif+uoaP2UMEt#5Ph{Cf3Xs3#~JbbvsJyubm@7fip7FtivNvj+OzR{*UD zLmwV}iyAA$rs={f#t}gfrqh<=hBqe{ zmlucQZ^yeVhHV5dT_iO54T;mGZ{&d(9T(}086_lS@40jLvXXC0JEuYQnd)PbL=B^c0yZDGTS(L-24J>e)IjZ`r`Oh|i!!$zVItbLwTUmS{R>>1 z4ak#6E!UrG;kmm~09BWi*=ynLVBz`T?eTog4x=E@W0Y>hCUU*V!nld)gLDzlCC>pn zM+_PaDy+mpg&l7g0UeEN$|~UPur=R$#)W6H@8^G6c+Un|Ck{uHBQEYni`jgXluXB_ zBQ?0XN&A4Vb()Z--eE#6Z>WrsVwY6>`2OttW!f=S(c zl2Gh9Pi2A1B&CQck`*B55k&dG0qI$GBfb`|=73?agtwg}`6Epz5WWHeNJXqe3RY(| zuYjG;I3AxPaOsH_ONqPXQwQS=;Tudj@=UoQd6t}}@!{)i9*{5)0@Z+sq$5|xR4g^E zas^OEUp@#r04Uno7~yoH>nqh*s`j$jte8^=cy!C`d3k)l9`L2HC{~GFKRge#gz~-~ zF|Z%c9Ym^BB8$DNWFk@lr<|X{>V=GWIg3-|dnlkz0r!_9buw38(o(8+JZDIJfyE$y zMf(V&1Rz>e7c->^8oi^=sOkU^tjG!jN+aYcMM^DDS|J#G`uJ`TE*3Ym8uBFz^xR&S z!jgTEi$tafIU_>Vrrb?>W0n(Qz(MLr0qOIbW+_rC*x1J(F=Q=>sZdfTX1NopcfgY6 zc4hhW2E8I@O&)k`3KYG{{_?xUNfG)Bi|_&6q_kb0#JHN$4U)}PXYeYiy@zlS=dI@h zg)y88UgP8~&4EIfZc%g-h^*Lc+jujuSLz#QM4 zZ{*j9QxeVaa&2T2+MIQQg2)DxU z4RAz)vn6mK2;m}e05>>9Won3&RD^6qV8szwC0%0WiA=~l>o)i%othE^a;mnq(<0Zm zc_Ky~fBI#S|6&%o(Xq&l9T&N=<03b9SmZ{i$AJMhDgpK(J5B&VghC+n4H3ox_&U*I z?BjkUa7=RzGf9HPLLx45EH{ZA$NL!;x#oOXzMY$YHvREOxH_u+`BhLb@^2if zx$%spH@o;frI+8}#(y|DADq8Atb6V79+&YxsBu87De-e`GKXP0!tF=VyzY;aH+gL` zN(%mNRwKkdru@0NWczSU4mlC#!w+dLLq2~qxS9ZJCT5F2Oqx&mKu#+kmrh<7(zeii z?rQ>4-O>fPGoEQZYd-IcPGQJl$Bhr;KTvVe`j&{C$O)WDIAu(D$~oVrr8JvZgrFy| z5*>%{7Hx+=3ibujy=B@~W!+R$WOUXPy{6F^=3t|baQWJO7(RJ-Cx%zDB-G?}+{clr zUej7cB+uJW*yCTn`QslebemOw7O_DgiCcz z2lI35HN8J~J~VhE%L#nwbGsl@j)iY~KJnfqNMInX=6S9IDrGA)$c#goK1@L9=RwCi zPGZlkQ8DVMe%)|gI+=vFeDLgAK$AXYE+XBwYa+xA#U3!1`%oDcgmpkcE`)q~LkONJ zk=7q@a!1E7#dDUSWA=dzcM$Zq(yEV-ugvxsyp!O^l zSV93H9gY{HYy=BQHxuY9*H;LvCfj(%6~gGVTK&kcd4*`5D@1$53eg_1LbP30NMt+6 zED0E&kk&$-nWv!~O#>5|28K@?`wQ4Ub>Nhs4`!kg$uWfk(oX^Qc<{i+HEK`I@en3) zpLd10b>{eA%L?gH*tCcjq2gubWJg3{(`BiwsriJ>q9(Y{R-4JGadl_A^b2+_&sR769DVaYS|(k=PO@m}6>3fe2eO_dpu6;gSJ_!7e6Q|cvW1(nq}Y*7FH zBkERS&YA701O`Cww6qo!#s6^p8r2U|Q53$&Yq`#-M4nTx0NPWf48tr5kwu${8%8NM zf#(3c^s>zCezCk6HcXjJ3g85;{E_6I14XApXCHlsl-(R%TJBX|iH!OO!^`Qm;YC&9}oJ&!(tvIsBMHz1|QU4%|Au7VqcO(jZAP0=0e(938#zj;4QZ7gQK^H=WkBl zy?-;B#g!~cjnY5cnW$AhM0TzroIc=OS=KD-hBz2lzz z_-g*{KfHbQdbUDbG&{F}%+v%%S!MdWb1HT}R?|{@&A~Xk3-R2%8DE-q7udP=qW zD}e72BGTqTxzi4GA65cbZz5DI`yL7%Y#Wj_CXmGmCn2fACeL5UsJ zwe?*0~F=MKqK>XZ*Ja7=W$Yco+svw^m=@Gmb+s9(@4Tw?1D7Z2#0PhlK zW679?_Pk5Rt!Ht6olB;l9v07HRSk=Os>RA}hGpxW46UZt>A0lLWX`jIDCdV{yTWgf z+=)qr)m?foRI}g2#w9Ul%&?kMzr%z_2zpp)VIN>RC6|>CL|M_`Mu=NkA}y=S8CWJk zLvhZvMQot3J11$bLQBm2Y_}?gMrL{q=z$^_<@18HbwCrNnu2kQDiAznrY67@;>1YG zd;n9~^gKt?DZ)ze0~lRXU>zl8D4!&mB^7_=AS*!5A#x-UOWf6YF|soAr9v&>N)uwl zUksF4C!IvV-5%iIP-n7~1Vp#aWodMd;#(3A<}#BNSQ6g>sbrNNX;|nGpnf@MyvmSbYL(`+7b}+My~(^hpt`QfjS45YC(%%n!+A}rVRLT4mvR%I5D!o3m} zCd%9b1Bw`o_DG%~e+!8t0Ccr-5{QeJra*}RGlvEbnY=?L+bosbrju|;2JO2q(~<9&}S>%vt69c+D<@k_3_NCb`E-L zi)<4Q#ied-5pBv&>tA4OYCbrZJmL95_oF01&3P<v|LmMD`6h zTTo^*V$`+(=wD?OgF*!fdhHa}b+%KZ*221|eYYSf0fy8FKA=g`a%6_tsEYP{kIR{R z*+UJ*1I;|BR4jG*3WA}8ZTRr+1l3YQ(5GI$I(>h3a`Ex>i2p(X!<&LR7?-z`C4hC^yCY67KA0@^l!U_qM0hcp-{^^ih~@vCcA ztN%E8u;mBlO=8R|U9W3WR2D(YU+%@cYDth_8 ztfWw>YeE7nz9%PB-U!Jqs#2NEb9V;b4H%#}A9kn|>L?d8%qu}kuJ4=~%{aS;%jtiI zdsGr7YrO$fgPmkZL%ejjQReI5c zt6Y&>wvxV!6ID{3LKXk4)RbhipBE#+)yS4sroAwttQws(&Q7`r!V*JYo&%M%and== zsJ&TBYEiz&2GzsKCpaF=<=^CUP;c%F#cJdA;c!eL6P6)pbU`++=)3@Qm`n}+eo%em>ORUQ((E<%A7~(gzI!!q*OmjiYvgV-_MKjgRPEn5rxo7O{zhNw zPAkBp^Q!&{m;mMy5I)2&6Z#IKp&?1j8IWT3Nc6zc60ifBdW;gHZ(}Fi zX^Hi4AvNo-c8T?%(n4P$fl2bc2`Pk(Dqy#+Giqozm({+l4gpZJD{1rrCKD@3zovWC zZTfd8?M4X!Lb;jJ@cfEcR3+w&07_T_bNq9jYfvY$of>&JS!{VDu=Rk-k)#4JZzix% z1GX@jzB#0PiIlu%AXNE=Y#QG|;Dt=q+C59uu*&jR*0H2bCN^Llx@?-p9m)F;OsRhS zAxqTAa(Nb5LzsIn=WB>FY3+tlld*d?niXa6?Ie==fNYYpdiP?vmr)~U_of~%a#H0S z5>{(HQwK-tMAo~L=-+Cu6KqvkH&{%~sH=^$a`=@h;8(D|l-UFVg%e}L`vo~H%UGt= zBvMd48?2_tVnR*($h*%y<17?Mm6ymdHlw&mu%x?VC!|GXs83&9kBZWKzT5MQ09%g zRFKhvYuJfH7eXR-0uv?DY+KiTH*`pcI7&cG3#C0>_iB_>w(V|O>QCY6%Rk;_N-EET zUjc>&J%rx?VnCh00Lw|Bqapey5|jWK{*E}A-MOV&;`K#D{--6M$`@f)?V82bM)>3) z**PMh06qeg*e7GDShO7;fC-V|wltAtnvYq&mT5j_`C2=myfjc4n6a%n5#SoO3DB35 zAR7b|8@wH%9T{;Dzza1kh-;ySB{325cfttaV8~Znb`|BN8# zsP<*~{uImCXkEUh;XP*g;h$cLX z1K61p=2bghkY)~yZ$fH?1eeTEh2$Q~8vyOe$)-?5!CJz}a79^bNBX!jvosp)?KM6X z@z{j~GlPc;p9|tIB37i@^mIUQ4U~@|Gf3ocuegFpBE(qMi2ex=NP5WmFVrqQ%>_Lw zM7Ai*5Xg$NNELI*VaSFu1@Z|HOCF01R>@tOWaEENFu=@pUr+#mI!gR>4F>)NuS2m? z$re2LzFdMWAhk}7?{17*{bhyspZ;MB7={7KGIA42G_kB z`FfAk`~3}8C(UdJy8%^^Zv~xcdLNE8)l^HlvEHeR(zPvJ^2HrEjO`cq*F_C2*Ff|( z1T}#N&Rb9vBmh0&TLu!fsP;zH3<7ja+eP6#d^%eHF3AY!c5o{=Fp z-*Zb}M3kn4_$d0GgK(|@M;58Ef_TUvhAnVqHGC1jJAL&LAHm(qFtn~p^2blD_LQmJ zMffHRV8tX_7z(hygYsR(u-8M|g*zE(p#k|y4B~oU51&wU7rWUG&6SaeztigV{7-^F=*y<9r980d*9H&H(QDmT&8J85xPvhZf-EPQjjT-T9OEZ<$kg8!(lyN@>guPK7`%Kldzgyg(Pd^+;bq;#Q(wO)7ZgpJ0`R&^H=4|8BF|6m) zr;xX6R^{0Shpw&lOJf@_dh>$*v9JILKg&j}3(`xjg||nG9MeH5R^qs^P5dWe0&KJ+ zfQ0bD<&G*uAKMuM79QBzs-RTBC$O7s2=I7MBL1}M%3L6{v*U=A!UGSfbs!iW%R;Cw zvJu|UO|iQMQxhh5Xg8D!)}(aMwkp@4BdV zkc-o;Pz!;Hx53yX0uMTYjtix4aNxum0y1E-BpW4!C_FIDu&jW-v0MVupaKN46F>c; zGRk1vzph2K9r`d^prTOTd>t=t)&=CDeq>Ws%qfeoHPy#re&Si*a zEX_1^KqXtN$mFITkEkDD8QY}v>3^ScLFm8x*FXIqEPP9m9>jO#4@RdHKnep@St@fy;qglH(#(VsgOC@D6@&j|e#=V51ebn+m=|E%`w1fm5S*H?XSV_B zmPzvXc1VcqaDj*@u_BGsgB>r{ELs2_8F0vxC7A93XTVV@(kM_u=4T7mn=gbTUX)64 z7mUu9BE(qwB2YMMHcnqwb$=VDgNcg1=IE8eYp5lr)*M#f-s1vRa{2Hqx1pLF&ZrC8 zHFT&wRs{@3o>`Yy9h6`54vW9@%7gu$nf-08tb(idG`@N{fq%y_Y-!(hU1b?1VA_t0S+L$PGDp@P#va~g$%RaHB**u>^xJJZtOf$)((Y=E&@M}0T6^m z015D?8oF>1r~;HZu_-QPz;tnKREwD)pYao4i(@yAp%?*C2*~YoK3~9r_B_6 zLi5>Y%IXB(hHvWO4XAd#HB(y~BrLs^LXgSX~Rdz$@c;~6=hpJ82=!FrF_ie_?S`E!5eYuxlmRPV!H^}Z%PeN{-B5f!bK@fZb)^J6(1tNVY zv$~xqYYzODAX$UqI0tP7U79vprY%o8f~20UZCIzdHrH8^d_*#HmJ&$>Vu;JN_^PDJ zRXpb4MD|;nv}HyJri=JlLU3hfJT18w@D;NGr8f_p{=OD+IKEOD^@cYjn;D|Y;hth{m-B3{G8=Qo z?v|3sn=#8zJb@WYJzB10A2|BY3!Pt#CQ_Dkk*pXAfb|#f5}?_*4<6vW5O>Pg7#vI? z_8XRSF%a=*j=_gL4c*mJwwqn5K8U8KBAJt##< zJ7HB^*ON@Nc2ysjsMa<5vJ&>Z@PJ073)$omsoS;E&(vGaks6{6t4w0GG&B9kLFx0g z-7%`S*$j=7ZFPd<8a| z?urcqC^SEI4Fj>w*cFEB#I6RLs>&2<0VQhN#%_AKTWh?e6;w9qJj1dOh&3%oP=zDO znS;N<&_FjK>{#>_@YzjP$21-ztFsNl*&DYhbwH<7fC6G}(G#=Dy7eP{>k4gU$=|Zl zTAA{T2Dxn|R z)fH+%oC8!g&nt89*O^Jbtk9iSsJ?vZLCTNSn6O`@_idZV~nZt1%Zxh1s+J)fQ-p@o_B>hb+P*|E3|EecKiR6 z#fWkhY=Gw~Z?uf0_uqQBmSpnzv*vc{j%)2nrVHHT9o=I?I)X4j57Iwi!&XZE#_Oe` zr*9AuC(e`?)n;FkA3@}oXbZHXlpa|WM{^EU>59ymq7vRgo|Gv#TwYJ1?2Rl3Dj%6F z2VTMz_-}5d4^WuBDD}+#dXOMzStturUr~Bw!j%!)|C-(A1*OM`FO=$Auhvt-l3^#$E>nprmYX9$ z_ySCnJcddKyfpl>(E2tL+*V5C62qO(0{Sc{`xh)HxI&I!#%+3ky_n{sjr-Dr)0#S? zF=y{FBPl8=kxN|>Pz^073rTy*Qzp5NCaLT>;%4H9fN5;$C^a=2yv2&+;glgE>MRxW z%E1lTuO>N_e8%%hWj}T_I9>}`{TxU&j{3Yy-#NdW`wyR<5}b^L&tKco-~@HM@&rQN zUx8GZQt~71SgiwSt!*!MaNy)2yeD7tQhK>TU!=!1dEGnFV9^#5bF~D8Ad0P3MFeA! z1eno$$x#(UeojsaO>!{u5%D-FQ}&YA3`%9%2)I~%@Ir0lZ`Z-SnD8Whp4=?3hkVXL zl!%29`EtGlc$v``q-IM<5so5FEB z()VHBY4ss40za~dWHl(&Ca-P~#hg7*H3@|zIQyM##o0En;%&ER?u}489mUe}plrrM zJxS!doOk2j!tBYm8oAL+jDw<N=fj8L zpCmamcFw~s`LW9fdGArrK?rBHWi8}}RU*|UmTIy#RA8F^91(wgsU;QSoGjVkklPsV zMk`jHiZ{C&WSz@ChAFY+MZ6aiBSl*9XX7hN35Ad(r36_a_gPgFcTbt4^7Tk2KL|BD ziouZI=ZGr*M}zV=x%kMEU0H5X@pWT8BLFRy^+lUDk9%n%)jPZl?xhtXcU!XD zdkQ*#pq2;UJ1)QJirtXo(Aj1vnfU8BQ#&Bvg-7Xrl0FLhK4!A_mI!d|V7;iT^vP+C z`jSJ0+L9{{k>;m!@HGD`qnc=s0`JnwW>i?>Q`Cn0O|{RB*cG=&wU(i~+oQ8?N39k& z&=^i=_27UVbz4FSo!9gZBcRkLb_u5(nurj@S`3W5p~V{FuaRpbPyjI}ihO}H5LG*n zcZw~P1S5s+3Y2V7+PfS^0QWekbnAdq|4C7V4F#Sk)(|ufz?X~KYLxG2AfLnGP(au{ zhD>BT;It$FdBoBvZ{MFc5;x}`$|3P|DFv0wPhp$(5Ee=kfRB zi<2{)dh+oCojN~$|9J};n5sT16-c*E|6_5kTcRk2))7U?$Ezy8-tcy49dC#8h_}Oe z#M{xIQ4=W$7m?r)+Ni*2d7+*_nJN+qN*6U@grODdpr5l)L=stg5P?|53gg5JP~9eM z-KteB`oQ_R*7Lp{pg;D%yd8fTZwI2%e#h%!KH~K-AMtwhFM%;*Cqm&T;8r#OPoVX; zNGlj`L}H-IH!w_m3=#t*|KOs9Ryk@Bhz%6fE*>hZw4h4WXhDS-ugLSh9@dwS$FKGE z*cuX7Kmj<2*I1R3Ba?`>7ZOpC$Hupuw+WEbJIFaW@zzmh`yDk5Y1T;tjr{vkk!#o3C5Ln4EspBrLM}3T}ERz4G}Tu{i|% z9CDEH^Z59qj6D=*NDNg_3QEeNhY=6iZKq;yC*Fo5yEB@_7Dub$guXS#*;fHZhB`%`)MafP?v$K0w|HZ2Ki&+p<#XEMiEY_0E;}5ut(W%4r|SZw z+Q2rWF4+Pa!3E^NiFL@EfXXZ~iC+PHXUOdaQAnfY8^k6(b!;}>9m*9X3i2N6}!u|S{(5;+dEoC1jJfczcRsUT;LJv%fF z(r5y_1=TFBXQ1NQ@I7b?_-V5MU6%K~g(fNj)|k6(V)<5HXB)Th`&TKHn5KT=DPB|emcyV|{ei7cj zlg}?X_0JbHoiaW-Kc-Cjn6n=!7M8g17bzv*kgrwUP>D|^#v?6ll2cW}r?UNA&uNjR zNLbnwk^F#DI#lnAR@raLc(03M2Fkk?z<-F)PAM@UvlvIp+{_+DjqAzjXE&=ck$ z_<7c?)n=4`8y|Ki?lbHT_SYqV`dXmb;xi7)OQnbH*>qtmC)@w?pZ@3n_kaDb^_tY_ zH~G@Xd(g_iwoL`d>tjK-NdUDmkG8y+mu-}zt>^dNDILoXfZdDSKtljBf!MhQ5(#kT zBIe?wVi89LMK5)r?=Mj6z`fgb2KI}GbWv5IW&j`Rx4L%DL zz6yx#LAi*is69VULdV=*$=EKOy?v4|)g9#tUpnU~U7%DdqqE8Hnm_!y?>QK;u zRI6}Em>ytFuy>e71iDxV*9)H@k`RYCs&$LHZUSSx8w0noje#4fl}AlX*%-=bJ9eRB zv3InI+vdXAaqjwCzaZ-cNtA&|VWfczDz*&Q@Q8XvM~$!(n*m-F%wqrvtq_BTa$!(Z z`?dzFC4q8ewL(FMrlW2=CtYFv!~Rpv-IlV{BpKa2q&&h`zGl8LSirCg=5 zCb$l*D!#6Y-dI>(VJB;mM!w&I;$n0+z{f#KOJ#P<_Ay_rS+Pa}<3MyGsCY7}lDzDL zxFncy4W%psRGoWJJh;?Bst6q6%vX8)YIM2b{MqP~_oX40#KV)jAbQwO77?K$=#^H9l9}mx@+lllEMzOYC=a6pf4677Ba5XpDn$q{-1-Zgm9l2tpl6XCaL@FHd z8GjHZ)D5#FJ?xig3s7r(4KipxVXtp7`6A8ki!W(TarFX#V#@olj+EkVm&qQg>1KT{ zYLa#uA_-Gj8)89N5qB3VMbOIb&^U}3g$r6X|N7Cs8$YkAeYva-I482 zfK4biP`eC?nk0+`=w_flZ$Mqwh2l~epzzL&1c8 zmz(+%l~L;i=LRDB5Y|LR9)L-}{|N(pxKMC4Kw%YlNnhs11`NdJPnY8lCr76Uc>|rW zo$C<@3xxF!KdQgppI;!_$B>BkACE5&KOLXGSM-nhnd;B%)cX8Vig_MmCQFYqQje3j zv2hUc6(?thr|&jSr<#KVyW;er>6+L0n7;ddibPWB;<`Y!jh3EXD_fA~!wqSQ}3))k*1??%@LjNjV4wAq6Cg9TL3{eBC-lB*9!r#C7lG%gzha$cLS%oEjY-7APS^E_qI?c$A6}6 zVG}piZ=*Oqyg0lZe}8`T6Ye5Sz^dxzQd1)Ovd#R(ZZlTrHlshq`p}}w% z$K;BtC^&B!)!M}VFwE817)F{aPt)(z;9kUB0Q;)7gp2V}7Q9|EvR0AXZ9c(#!4hJL zjK33=bTFtOk5HUt7|K1$E70>7;8!r{ zQOV|!x=&gN(+>x)N2jBABbKqlUq`3=;2RHFp6~Sh=ydRcfEi1v2aYh#Iq(?Z4Cl~N z-X6dkx#&IkKJq_UG_a&`4u(K}o0t=w?gu6FL1}LGGSZ;-)~3 zKwAm-OD8i3!hTJjYW1j^n{-V4Nv=fJf?Ld|Vc;$2v+~(Yid&Ip6 zM)r)xuyH|#f|m+qZe*b~@df78iRB%K#S*Dhdv7tXi@PiG1j+y?Dfm#mkQr|QMs7jX zr(EMqdiP8oNi>B-9Le0F*+OZ3cL>QH+~Fg_xFJVvqcRX%7v^3OX9)A!e;?>e(@r16XX z{1^TC48YgJaOjev@83dyKg9<%8JO2Sh#=|0^8&~_!Yu*Up6^DGL=I6lrU44$0bh|A zmWQ8QytKM+M*zNuZe5k0+?i)rr8laW7|%`;&{GN^J|dt_4L=BVVfcWWy0)+l_lvCe z&nN5Mb4hk6eD`mm_o4>y*hAPZv{2{>{UcvM%OQZEWDNKX6zPGz2_c3QgGJJSQa!XE zOgQTZB4SZPB0^LFcrL`8eCzqezKx%9P5)}dz8lw}-MS9#C$B^M$?MSG0SM}9U~E7= zuIZT2ako4RXaE-+kpLY6!Qh*b0kJMUb}a*GD(u1`N;-rl1j0f9iB{Jk0zu@_%A&su@1?(Qs>&f*%g^&eIq4DrWB`bs}bhlemtQDg0O18KO zvf~2<3XQCJ`IFRlAmYK*iiKTMF42@sJOzJT9dpk;vl)#G z`=>l2@Lc)cp%uU zP-C2tb8!z-QIH#Eym}Zt$?_yiPC(qna}>$pMMKJ0i3tG@y2((?(Fve2hEy7a2Q4Es&1mx3~=qaUNCBr1yQ$f+7k#aPfMH(P3 zoxWun%c3~t`!adeP@P{;aFtgRUBWD9a{47fAGziDWi5rHxh!Zd!KAx-+cE|vR>O~K zfIcEjbHyyQjXrmF{?3l}n6|7pC+}WO<_Gx;YV>lfn=jqbk#1=O1yJF^TYUcDy+-N5 z(c44tK)ieZW;BbNeCbjvbw9eaCx3IrNdVlorPzkdsBHz?mh+4*vC&%CZ8;tJy2kI! zrR8*F>Dn&o49rUY z15iGQjil=HI>_H`kfgAo^dtm86&U0vp&y4%9Qs}mqAmp`m8MPvE=fjOQF0Zz-ZOh($dD*)!ul`oQMZTx0#h4o5gEPRS zn2Lksu8zMw7qWt&hfhKgLLAM5M-Tx(f|?Kyn&(!e`~J_dy!0>2tG2va%d)NAmzTBs z^6FoQO`4Ui4y_A4vHaL}5wx&{A)?p-ObNm*i08pobP?y!QF#Ohl%NF&$_0*wjW4QI z7eWNX)Sr2IS#|uLb|sOd97PYXPiI2ASN*+Feq{5pniFZ(cs@2u$n_4K`zvm+ z4skE}W>wHSsV>tcWV|XSp3HfxXPWB+~@Kg(KhG0Pa3=qUF_ z{vCff55_&@Uy^T_yiw_YbU$g%B1Jsl?BOn0CNNU@)H0uVMvD!%Rnqn&_rs%H zNZd*cV)?|MFVlrlPbCC(hbM5 zR?(po1hPAy`E0J6yjgQHv&Hmj{!U%CS=DW)_jlMt6d@iCk*_#0B0tt+fUfZm1L+&^ zA3=mJNKnK<>PnW0;{wcn}_YOhD{gdww;vC+HD0`yRCr!MsI9FRk6=0wsSKE#J%-pg9Hre-n!7E^Ux@$g}1@eY4O42N`A(lnPtL=M9L^!HLKK7^1hAs7~+^WliB}#X?8P=TOqc+ z3Y0bz{Th&d_*^~#{XtjU2lG`X6h&tL1@&{FC%N$Zk`a7e= zxQ!Feu8_J@Z{CP(&17kZ7OOBjZRc#mIadF-*TEme^)1dhXk4p3G7sCEX)P+IO9#cZ z^=tvXp0IShoBWKeNdqP)>@FjIHnX#x2+`;WxV7y$mqtg(t^K0@q8tv;LckLRo^BW+ zxEw-!Rze>oGAIh7$gbrCKIot{9X1JAy-Y{=fdDHfqPwAGRLgmM$JAiAJ|jvl-$YAa z9~eThZJ}@nF+b293aB9@0QmVn1aqxO-oAx>s0FRj48|CC4e>82%G)jjqd#WF>5o}) z`VKNMiO@YtB^Fqd;jk52JcD$ofn56z8(Az#@VZv zbmSbYAM?4O&pQcrmPVhBQc!0ouO1E#XN##q5}XkeJ4N^idcp_~I(r;ZER`x2I1ofX zy+x=DA71+B2Usf0KnZZ=3cUmRYR_d%jL!P~nvfLZ49#(Y02lpC@@S|^c|`E&C?BJ4 za?YdDLfFw>h^8DZz5ns%pqTp}213JiJK_?WTIl40FD?JNQBk}aup}Goi5ims{{DV+ z8$8?weAa>d=S&=&&WeWGQT@htRDTa8L>NFJCUoKR`m;DfO|?epOZ$^ zZlDnj4P>=JxP{bXZ2AGPqes;n-?wwD6y7oKXhTA0%{OLz`#$S4~J-|twk0L z4w$zvF+ijl+X$UmL~K!ALz?F~&%1^o?PC4atf96bo2@-|4QY>ELpw00_$KI7Au^~Z zfeoid1Ba48%@D$GC>;X2T_BO~VQu)(8UXDv$XO%c*TK#gyILI8){x;~5kCJKGX4_R z&~|%P-VsUm;8J4UQ>=&E!jx@3zG=mJ`-a|8oGuTyS+UeStTAqW!=8Nr2o{-t%ywMx z#BohFO8{|N|LhZ&ge$yyd`CG=LZWiRQsX^s0;xAY-N@57Q0l6a6ksAFIDM{O4Jq14 zRyMV29&=mJJ6^6MiK-sIJ3NC?42>h%FcP~2N&YdTswd<$2`&#dM>1EhCV(yx0e&p^ zQ$DZk;EPIq!*Fo*uv~!D^;S@UGuUib^1w@#O{RYEGodsZXTQY24;H_h6wso{UtTwJd-Kr6Fq# zXEMDiKm0hMCpnPAt$I)Lyq!&X)UVIPKJe4CgJRnD^B16pQQz4@YryY}>u3ib7vX}5 z0Nw@Rh+qKYn*|iWqU&~wSjz)z^0wHXr}v?W&6;ZF;O~m0G{3EvURNNc^_t#aFVtB9 z(`zGh45lyeaT}1%0V}ux9&X1Kz*d@;sUu8*&xIaCF456E7brE~(}_o1t9`wEA8@&y zx0@n9l`Yy#*2sHo{j-e*kj)0W214s#I8psJzuIO3qHCWIXgo^V|(LnA5ObRi~aB54LISPqcoC=2i^-uqKAn8^p zi*tXy7~BO@>^uVoap5CFSS`+Sp%gM2oTMwq-HOFI`s@=Dn8jeQtTC+af{Ym zA|p#o%3qKPA)(MB?ayHDn5S#TzM;qkH7x-7%>?t9;0K~VkyI*$z&q={V*-G%Qo9YI z;PMgPJ3`y|u<*xmxme6pahQ-oIjS9In%TG?QNp zRWyT)Uck?nF}=$`SW1#E0thfJ&xsW&FUA&Mt`&VW@oPXpzVgCg(*TymbYE6M5g7@v zT>MP35UW;u7B2BeQCLNfR+<3nEOmQ0_#*|+rVlysy8`%$J4zguP-4)NycWzPj2ryx zpZ*t`!n^>CjVQ%Df)QNKmvPxL@s zaD}8ICIMyu(bdxw=Wod#=V$X5?>A{K6oktdT38`n;aW2A#qdP2eL;Q5N2k0R`K`ED zPd-`U!Bc)7sy?G6BvQMAN(1Rt!{(HQCDZ1xqtP99q`ot*6Kh4iMa7=6Q3zEtUb-%> z+ccRCQfz`^-HW+|Pu>^ngM8EM$x~9la2w_g&hb{zo38b-P>j}4jo#*FH+>#1^)}zS z{TI3+D1D0YWQ6fb9Y%}s| zoEsFVakQPVEOw6qq$-m=fHx#;i6Rv!tXn<^5kgdn+rZl(!W1S!-@5Qu9|7t=Is|Rs z_~P{F+wY%)8c@mtet38I{VUMmZ)XQ=78j(}(d5MibehNIpO}Kbl{yfn4?KIRF5NBq zysfHT{ao!w(^~rpf>U}&{hq(eOXKhI&iMUfP^1R#!i2Z9IF4sf$?hM`5KeW2G$_Q2(ByN3C^uZ+EKm|tF*zphut*irJV@hxz7 zd1d;GooNZMvIIFy9Qt?!iHp}Ic7;LkXBR*UAKcc6kpfs?8AL=9#()&qapXA=&8*jn z89EWy`p^5ySasC&msjR5>6O{Sx(fK?E%}4<2hedM7Ir850N#k`4i2iKTitTjCfL*| z(}isSGKle}9gsn-e#fm=9eF-5pd+dlY*>^u5rZd=rgg_1_=^9jz*qd;fPiuAhcR2; zSeBTP9-b6Oef@qseu>jcxB5J}ncx?y<00RubVOG5NV-S(lqfG_(obn(51H@WvXFb%(*Gj2q}Jm_{TVM1bLpHA6Z zEcj1=X{N$8<|`Qx-4ddDh$fJ~xM0QC+y%`G%2Es+{4tPkw(?U}qO0Zga!{Gs5*-7* zd}k-1VJH|ZpQEvy>vHdL!Rr&o#C~MF>oy;-eC{K{*zHf&Gm70YG`nF`dXDn>$>(tk zZ^rlP+QA2Ist#PK-e5a&rDxy%W}mO)qOuIe5>8gpVgdufn54nGo44NlAFbn?*H`QB zUW@OSZ{Gg#_Y?Qz3{mbzMm68aM+P>O>RZ*Ef9|k{%w)h& z?IZ|iZ%It6k4?$I(LB_qSqTc+V$|omFu?#B4r2f?90L#@tv9_Iv&htb%uSMQWBkiP z<+fL!Iged@&SMwf4kR)lMy1?LY=F-rauet|2n}whowxzi2SJr;=$2+1Ndz@1!-aAT z3V%?)WX1^kIALw^IWFX&^_@@=eHNejW$|rYe8!H&uUprU{n*8~1F@uIT26>6Hn?&j z(u45YLgJh8A!&(H1=~(64GBaqh@24UaRd=V4^?n*sgeZDURB@D)*zJWYR{`M4oHsk z7qs~LIyErW)C~zQy}G=+omUf)9Vcw&Bh3y;=KOG*E5q$?P<<$R3T2d+&K!LA;aoX4 zO&Su}S!Iq3S$8)czbjpvfj%UI1T*Gk`jwcM)dHzBa6FKoL+K|p^^mn(?GDcP9mJF| z37~k8kdWfYhhU0J!55z$O9yXYBL-90ek*m#dJ4ZGYkSNYIPzlb!K>3Z`==kWZqsk* zDVD~n^wZ&TNfaCE?-Dr$j=i6YeM#k~=8)$8MHsA*;vz&lIdS8RZR6wkP~IFY6XaE0 zyubKRIDM|1F3f!E*?2NttPs&_jCZjz+W$z2~G|cGpz8i?>*QIos#q$Fn;A`!7I`t$W#Ds7q@F-jVw?3yUBe0gHnGGdwU<{ zrJOnA0m+twGty`hui*%aCI6nYg^=t476p>X)KOY3phv;hLI5bNqD)l-go)EN@u}n^ zRFm`&+%cM$4@&bRQIsMw^SGBCzJNyR4ax{!l#%H4-lgf3Ss7lA+U^}KA8uC*q!Mqh z;Uecfxrg%$woB^A$r1-p)G)u=dm(~{Z}G$P4i8MSiorj0~;o-Sx z5|P0!l5=uufsjl0OMiEIeLb!}*l)!y9WF`vXYlQ4LmWVSbN?gWw(hdTZ3@@AZuH6x z>TO?p*+An5zhle3q{~`9_uDS(Uk6|bEec!*pGp%SP8YNX@OvP64Nk2PsBRBD<^gcH zMhq5nd{dps&?0!&EdrxnsNrb15@r?nb@*$sJd8GxP-7N3>%Sl@x$G zjO!sqr{w~h|BOK}3>sZiz>g7)im+D#Z3&WdJy`;W12Bw)FNwviWKu3_&N=WkKQR((+5!UXb}i0zi#hHLWtI>1PFm~Iubd{RmNA$ z&;9~{)+xhzfpsRvXacoNW^8cfUFY829RXJ-aX%wOoM~d{T+lGnxrepB%Q}A94$BN> z^arE8GeDFjpEl`^Lxt&z&^-45rh%ZCY4zvdg!q!A$!s~#p zenfSi>|h%XrgpUNcQD0(0;F|K=+cAg+tTAJ*m`x-H?T4E*{7u<@|n{HH*yA-nrH(7 zo7=1DOru9NU3)Q|*`<8bz3V|$p+lBo>$Uw!WCZwO3Ez@0mwYY~nn(>6;+gO81%1<%#^l!3 z7rCi&W=92wuCl52vVBv3!h+MEu;BWm&r!l37(qmnhDv=+!!inN8&&%PlCB9z%wVNO zg4QAQmi$P^yi>Yc7@japLw9Qn4lQgTy`Hk*q#%X)GcCC1TU}3GQ(F6)(w?xUv?r{o z{{BFWT7U?}zyfu^0hD4;6(Fp{g;Wcw^Bf0y!=B+dC}2PkT6aMLY(usSH4tFKa?QXs zs%r|OGQMLy>zdkR!urcwQ`<}PNK8o0@#3ba$gBKZ3;9XlrPW=6U^Q9$3$`1m!K$T? zv_E2K3XQ&GE-0xvy0~Er8V2QRK_)i17Nn_pFND+qky;WAwFf^#-QLe`sDgkc+T0=G zugS39PbSNhLQUKSHdc=Sq=zr8?k5WvB-7PWokvv(|D~k*l7)~E&5ET03mH|gB}WvA zDm|Q32$>O#n;M@>w?itiz&gFXr5~7-m4k_dtbfRQI#|pzJDUP!BMM!~0zWEx5YuG; z8mze@R#B_Li+hBIn5Am4T+2)8tYO{`c??-i%S(b>q=Um#IoLb_dGDk!GuKAQ`ZvD( z8U=fKB^-?h`4K|WB@Y;+!xzt~2JebReN;JNB~Zx)5w!=U#ww#dr@B1|w@`saX$WMf zFdzFlsm!GOzuEZXjEnVhV<}0`K~_Mnm$JH&Z}g&ZKo3tTT>2Sn4xKC}kK>19SuIhS zJdRhgZCZ>Y(el<5C~?}UbY!yI=Nx133K-2&jC)owQOq0-e>T;V`Q3zz6iIF7-QoEG zh3CjudAYY&ehCtS_vFz+<;H*qdL9ebw;90Cxid$P1cj)jekR}J7Eh?yVYdkL}T)na~$o&K%;?fByNht9>f7bg~9|u7T{LIPGcKUQ_|-EUlmgJ zV5|!CIPiCryi*BE4q2XkdI$We#G%qDT~ecs#?2Oayp5Uq#mXK`7MZ3Zk|#G0v&Hod zcUncZqI|941%$lP*a9#gjQC_0M{aaeM$_)#Y+ zlPm9(KRgxeEhu~%NjY;a_j{JFp@YhN_xPqYUb(%1 zO?&#bzB@|Dal;1xWzPO9akH?npSkZJD5@65w3%4aY`>0c1o`mJ=Xl z<$wVh#W(l@2+zU)sp%bgfap1!)oj+2z$jK-P78JQpzrj+orP?f<%Xawr1E+jtl%bC z0rZ9~f6I9N)WDJi^RNbuMgU;<-GmClCBbBj(rMn?6o29N^J*C;pXH!heNK~cWUVlh zBinUjoZ0fGT8ABMU#0qWLDrvrZ2vtqM-a^Z;d$KKR@~VXBt*et{<1KbG&oKK*>m#~9z04&e6^3QE?; z7unRbWN;{gZzZF2E1^1nKWHlP$R>&2!aQ6fuahpfssOoKDD#9%lQy zzUD1PI?Zb=h78n#Tx=Uvc=fuS?UB#2uLo+d^KG+-LDGEbj-GZ)BVF$Hn{1p>D#RDp z-EK7@-!x1}3z)~~QRi;NRXoptQg{du?3QH8^RKxbqwKTTc#JAqFVcG!tF+Z=QDzRa|@wd z$4MfpaHbs)Pis*K64S^c5KsaDJn=BKyVOVT4?m7~=P-hybx2M%{`fS$dBJ&vnk{;V;h&p7bv)BnLb>axgs{mZ) zkk|FJ0Gcjwz{NTV>VSR7MgAO%S98BC-k)gk8m)`hc*^26p0arR!$puajT6s>l8~7s zfQ$%%btw!`#vyl4+_{zr%sA2DH@b0bLEqOTictentGXN3;35toAKjg@_glE*H0F-; ze@%<`IYFDx8J-MQBgC~<0kmYrNNUYUf4bp7&4Jdb*hTB&jq7xv6$zCp;vMWuxySd% z7j=I+KU4NXnW9Vzhe%*X@BeNRE`=h%l4sCeBx%;*Kcd~&UsK~l{AWD)_hgONJ!>jp zzBZ_Unf?_Pl9pT)4&dlRi8+f2y(rDa-Vfhg-Ak2oWPeF3|7xMmq(AD9F1wrmdcqI& z71vXTS$7U(&rnIMn%FUgrX$|W$=PCi22Q5 zvw!mP5l?zgsaloEB^WFano8J|(as^K@nEjTLvkFi9)!=g$r>zfYMQuqwZ>4wq@HDM7c537lwOl19S_}euctw)7 z@VenasuaFboxn5USyPp({==H&Pv^wbhksyO#j}ZIC1VRe!(>=%HRz! zxYMDM1Rfj%k`3p0!X$7*s!3A6kREO%e|)w0VMJ~%PF~!Giyx$>#$v^^5q!t=D-z{m zvElWy%%CTCl^>v4wq!3K@%JFEy*f3_gp^w57xm)p$pDC)!Rh$`n47`z`=d{1;LRQo z4(H_K0H2HEH;}3TX;Zx91A4Xh_Wt#M```b+|NB3c7cTStsoe_z45Fe~zLc}_f;hMN z-ybBw#_^j1L4uh<&5uwm0>X{h9_N4Z!jdAg&#rK;sh>o*;;<{Cx(kZU7cx>!Z3 z*_`1~O2FZcfNlU&K&-#S`?s=c`UL1NXbqF{3{hl8)uJdV=K)^;tcARscY)dqKbbRt1le#w_vDhEq3PF1v{xe@7?D0@q!`T8IqhxRJ<~{EE`R&Qa6Bx#W@85qK zygU8&q_Sw~TXf1=eG}eK7<%DK`gRl`?FWX5f1n zk(HWz#1KyEIb2{+`;HA>d^mpn5+@uIeJ4sBz8?JJ=?DCM3Vv|<>vtdC4Dp{6{P)f2 z>*3(TIsWzey~}bNTb3m_=T~@US=yyC0npzMMMmYN zNJ?T{QDh3#RopBqqxqBc5D1{)fGBDdW^Fy|S@odXtjgM~th&cN=uunF;s>&RA%38L zq1WE$_!I7cgQ9NSh+tO56#=*(KYpJ3?7j9{xZ;eMe+fU6FPZ!2=f6FB^Xxg{m?C8# z9WtP0CUROrwYF)7O$j`M+fY%xN&#PzLCL+n2k?plN(yUrUj*IfAP=;w+g%?7g7p0gNq@ z)VntXYodK-5lIxF9AhH9%b*z^FPN-F)(363_k2GG3zmd7y9G!M6Na!=7@e4f1iqp| z(&>=i2!*WpJ$Qk-r*a5^Mfoz6I0E2U#M@5-dRHW(f8_fGOa~DZG;=DlNW?%d!X?t6 zr3})-Xsh&(@Q;@rI1=NnocH@5csZqByTo9rOrI*LtFlkpNZiSSD_@3ODlD(cK5%6s zGJsL=OtfykVGrbHO>0cvz^%KANVOtsC&W?^mGsf6u4IpOZaqI}8!et%JX{o#h7i;xMxYFT`Td~i8!h&%XpP@6dehUhZab#-#tsxD{NBXC+t zLDm7!QDMnHfZnRgXq*pV(grx_MQfbjL5tQw^&CFhjpcYtIi2aym%x}}^|>U!g1?H_ z#9QU^@qP2{x`2#)bf9S)bJZxyhYHndC~Oa!_V8b5rlqUkA2y9Hl%hE@w!v8ly0Hz= zVs`qk`Sw;9V2jV?3Ridd{5r1g?c_66CvcE95ypW5@ibr)Euj9ObmfrDxam8p5A>qv zAOk!0wGeJ>FLG?6TLE@Ox9b8C9+G=3eEo!ilFe7 zfch5c9GYOZ?NZuoX|G=rC6C$ZGuAwJrvc;cG+^BR5Lb{rK|IZrYQXGbg$io~Q8gbK zfk>{m)W`%6u}LED7%Rwq*L{TP;v|AFYeNf|@?cEMeq7!$Bz3ueJp-l_3GF#M4H##q z0psi-pcW@knlYV_P-Z%kwc@};c*}B;zlBslo#>pjAVH3l8al4-CXoY9hY+M-u@P;miM^^37^K+0dg|cDtCz1%E>(oYo!cu7$iPd;UJ2qH>7zc zjuUhC_ux}-FHvZG%t77-&OLP68~!Ii&d5P?4Thml1d%&rssMT>qm0QjAia5T=wPI> zr;)s%XQ#)f@_2#6)oqHp!v2~@$&3yq?BnP9TEOFD8yfXs3mE z^E=Z^f{197UIFug59Q7-kLbFg(RPhoT`V}yQT6NM99eFc#*dr z5fgP-yDf5M8=*xs(Ba0oWsGOR<{+q_10}*=TF+!B$gi1yrPu$Jlw98yJc2h zbzvFWFYdi=BhKr<3IuW)M2-O1Ab~aV6T^Y9fTsGUV`3eS@&AL+wMe`p3PRPh)x>x0 z2&-g0dE2%f$bC3F0h_JuygOQ*We*8AeyAIuq%=SesQE4}*H9Qi7@Oz{!j@&bt&zR~ z8UTIWowKCx(%bqjz1=_9A4Lhsl0p|LN?w92ATr&O5Z#?%96*Cbb0NZ#5KV+_n+}GC zuYoke^$}$U1Fi3^^|tCgdT*=RFSED%ks?OfeDvA;Y}T0&^KTzD7y5X7G2}AWG3kZg zq)3^;;yW}O-(a}mp*3+bmYLwYK z-Bp3hQZa|v;12K)MK^x?=3S}Eh7Fq%XBRWVCFglS95uL0iLvM=oy+i+L=`jFPVS7% zb5@y3XhLpKavEl_a1K*Vu*FKUqXKd)NSNqZ6mM8^Ua_CMle3TX3yFd*Nt%j&D+5op zMiuetLKc%Z80Dn)O?;wUrld%)Ic(zD9W2*|){wRWi)M!Oa zx0XftPD>4 zrq*D9!jhl``%d0s83nXMRp4MCM($2 z@>!WrBzo3lbzOWfsk>r1M+K=GM+1g3RGLpebbLU$Crf{xGo_+k7aU8t;j5H#y&n|t ze5w)$P3jX4kq3D+$$KWv_&TmNEjA+xkw!5mO%ba=IiE$SmlC&G9(j46Ck>1gAy#5;^K}?-9FP4{vq*t(^|)Q>+P;Fpxquv2;#1yv`qI;P~Luuc`DI!I-+U z-Wd(L@yKBJ$$RexmD*bfJoBx(lOBHk!rs;+2;Kt=pitO|Q2@nWE%9v~tlFv;g`p3@ zG6xPp%ZyaGdLTO-sZgdj1ET41R2S?$b+ski1=*+eh}L6U-n$Np@EG-SV#9ac*nyLh zvImSv^C6Dxw7GcJjGhNtj@<<1KANw)GJCU#b~p!zawMB*#9WE7&qWkMbW&BF?Rs zHh`WiSfx1|fu3rowh)n%ylf-md_yrVGO>ug1Uo7$9Zs|wP{bT(^qAC3r>o^c080`{ zQCM^KjA;w!0%Rtjpvqz9%Rus_5PB;h5=RjkrZ%fRjFBs!;*HP!rG%nFeH<%u^<&5xY6J1e280&@2-vi9m*RfzdBT z1iw)=fN7QlIwTz=N+H8qc}>7DPVby%cOo;B_(KEOmI|iTJ$Y7 z^l%)~+g{VIHV`)VAvN%c@?2aX8FZwljVz1v9}mglwwh=1290hoal;`awXhvQSzewE z$lr$g1I6%)Zc$7YoI2Ohlz@PUCOHXbq8iUxl$YN;1_usE^(l;qr=v57n}ARpD2xQE zV#pptbt=aDz~qy#P>`zJ4reJ@ON29+F(r68vL+OjMooegr<-eZ6+!(lCb*n>aqutt z6CdEmkATs@=oyVh0CCWc73bzcHKTA}EEmi-ih<8sy*MgO8F*3|^GpFgD|C=s`?x>? zy@;^~-pg;LaUy=cA;yT6MDp+)6d1S+1|HdadoqLZQ;JmDZv84)fRkucUzf-`mshV8 zsam_Hl$;Xy-U9Veyc)cq0!y1ugPjQ)j#EmI6lhzS1PQjL>n?sp z1#BQ@>{md4X_mx?g$L70^FrIxVc$U-D0Y0;cXhZGL40ATCNx?=#E!r)q)>EZZi00d zyzNMPYv=-LrU8@Ew5^>?L3=x%ppo*X2YwQPn=vrR*^Q*|zziIC62WZ&!7R(zkpiib z?$gG{*da{Wt1}*Ial-YH4a;xfw)kAO&aeMgcmI|Ns}T^C7HN6ysJ*^_d(baDxfYu2 zHhx1QaQ2}RaNlnJwoRPw1W^oI{c9(V{mTWxdOpqOy@qk7_@$E$_^vGP2jfGmSTbc+nZR3a=KU&)!9%A0gLw^_uBd!L6O%whhC z+#TS`r2rq_fif(8$WM)8xXPas@_ri8=e%|H3xE&Y6o+7B#!w)h)axmyj%XfXUvN5J zQEXzq3eexk54a__(NH)TNR^r$+|LFwLo09aeH3|gv!Lh}W#Rh-ZF&PAy7I}uwKx?} zl!YqBR~~~pFv7WdLQGiYg+%aow{R}3MV@=%nhpb(fK==^<& zqi1`)$$(fPOsrT{105(xU@1JvZ91V3(Mrbvz!!pB zsvGI5Yx{u)r+E}Y!7^rweY;_Zf)o*ffaZ3pZQ4$CcN^`4)`V*sVG?StX@;QFH&o)^ z1)Hs5`+jJGYQ;%=v zMQgvhUH$6r(y#6={n{T+52jGaK)5FGRtZ2?t_MLLBhf9IS1<}8Xa{RGBI{L4Kr3p3 zRTK&mQKE%_Bs*HzFkc~yhs>6p`qkOkuP$4!+Ml>zw?w`O$~h;zE~00CP|=%h`(@n} zGpgs>^|+QZR6CJYuMb}|EwLXtx-MbGd2Y1=69aKQm=jfViK+H;PT|?KX_vLteq;ft z-v+fpO<+d|KRKa=Wdw#}LAMkbqK8TJ0BBYoTuA|1V&D$5AiNwNGn|GgAYLAw$u!w0 z2TUvkwZ$D&(Uw&SI3bIMh|~&m0yu?&!9r3`&zKdORaT)0 z!3wU#bdbqlTb5UMBs2@CR5oFM&BJC15KASfM!FqzvDU_RcArex8T*APStM)VM)G zK_DPboM2qy%%tY6fS!MXSDl+Z2ij3ogcuk&?Y4YTGB_21IFkXw;vH65EGRM-I4v>( zY{3;!GIm_Z@L@>}fUIT;!03pHW^-ghGZZa~hSYThnQKKYIY9G8jKk;Z59*C1+xr%c zAZLFesysqgyiX~`;+6kp_`xSWkc%4x$N>L)P4KqETmK90IRWhqrPpGR0VI_P(uVO2 zBY&hE=OH5oPZjN!SDE2d#p@PrJdF-0tr;*XIVhLUm8SA8&0&U{M+jPE2SpqitGCd3 zkwa~DoeE}GBu9RX#SDEj1DGq$xlp2N<8!EGEXM7TXqx4Hwf*Ti`Jk`PYCtQOsldu% zB+W4B?n^=$3fq241I9vyd8q>bhpMe^C6I_3N;3u&@fJ!fj8mkcB6)y?j3cv%;5Be6 ziYTLEAcBUBk(y@nRO+`VznRJ4cwXP)(ekOHXO>Ed6u{8VxBUA!_wo#5#rlyl3IAYbz#Xxgt zBW*h=5RTg`a4;8ljBb{&pMF0(Nl$ySL4Ckmq)wkr&g5)JNPc~_4&JF@tnbXG}VjK^Rtu7cc(}E2@Un? zi+EuM+mj9()%arFLj(0Y1${LC(%oOb8rw0E1F_HL82 zKQIGFa~BRF$Iy_B9fK7W$x|vox{d)DnQwTp3C|HG1|SzPh%ul{nP^0ip8&Z_(2Z|D zIqj;_`_trnnw*a{IjzpgY3(*St=%T4)t@FDGTSED?GX*OBXEI6pt}z|-`8#5ME)h_ zXA;5{Xgg{cMSz3&08m-b8Pe6j3Ox1anVe5M=hNi;c3HBm&kO*8?snwz&+Fb?QefVg zH>>I!ytxR;Qla0#rTgTm-NjRT2|*_E2~3e(kwY`MqSsUvI1S*h%4rn;rrFYr`H*|8|kM`7o1eVRPu}Jy zZ}XG4`N`Y-&`v!}~BFRtVCZ0>M+W?LO+VsnQhba7ArG;}wH z_Ky<*2BVsm1Ltypgd+fATpUVmPXihXd_m9%3UF-I)g%aU@J@gi2SXv8)mrl$4Vm0W zKS!$#jFjh4LKM-d#(H46IMPGK4&ZbXG_3HTKb@oh!p_k&sK1q`%h)_&jR#Cv;{g-4 zKgXPoL_iIGt~k+=>J;b!z=}HjNHKCcz$@or$^t;@sV=WM!~uWPaAdlB%(;A;UPAm|~r zP*Wo()N~7*EM~H+gE0^ynD`e;eE!cdVH?@KPZRcO!almYnrezu+G2Sh!9N$r*}|88 zfmI+p)|;+qkUeVTwAPI-r*-jjdp6i>DzEOZK2%<%OMDNGG2;55qa^rI{A?**Jn-Cw z;6`lXjW<ZX?l*8%7>Z7oeSk@`UfHkOmHI3z^plu4{qgsWBqyk?VU% zAGd8l(KJvRBfbu$0xZLI>1|}*7^=04rLsl6xVh$YOx+5=?ckffZop0f3_#bC5E2^} z^6J2@r}fPWUpFx-+yDEwmsjuKihp08zBw8HG`>7J8}F1l{uMFaY(;#uv@=ZZg#+wh8jLo#4h#c%}R((EZ%2LL`Z>6#vMWb(KEIP_x zkCa-QVe6LhRI!`0s$IG156C+c##lo`Ze5Tv2iiXMg%MThi5ekIk|;k9t%FM0GII z$Q~5Jx4D_effgp)X^k&_mPx@qhcDnV|4`QHeT%k<-tgt-s7BEJ*r0G zyW@<*d`+!?dX~3Y!Bwn`Y0aU?DCwIb+|1$Rm6Q?Yd4RS#!gRF^;!Gy^f{1NV41bz& z*f%eN@1rVLTTF6`=hv{+jA0cb&gQcv zMa_j7g_U8!oC$;gSrnZ@#x^*%@fsl!$y0+0Hbnpd7PoAS7gZwl>~g)Q+9K>q<}OD# zsjnI9G8N-66-?dq;m;-y44T$CH_b6}GMi+OXf z)ke~wI&zB9!ppUo+(m)B%3^}XvD^@7!leu5$rR8HTI=Yev3?-Rq601wn!ah6ZNB{9-G&sn*Pna=A@ zD1TP=xyn)p`5E6Tv}O-j&hp@;(AyP3*aHkHIxTTgd|DeHGeL=XB=f+4FNB)xpWi8T zeBdXNvU0ApL;~+~Y$R zO(!1K`QmC1*7}PVMeY?o-xggaBxwKUVTwKZ1n)Rbt^M2)?#-8w{xRQ*42G6z`Vj-|<`4h6Al~&d zU|c`T+0ctIw-JT|Zrg)$L>ARf8ZL_H^fGU-#IN|?4c_Lh_x3)^Au5Ce7*dHeV4 z2|f$k(jg!hK@1MUm5_l6eXxc>vJIqPUS!9vsp~Gp<5b%*!?;e8psS{#uW1ev1wgmk z+1z-TD~=hJ!yE`OHIhb*Lf3~hQlv+T8PLC>j_e98diY^Z`h;7&yz5_GjW14)PH}RB z)3rPWcDQ@nkAVE`*~#(gq4effzBoNAoxHHN(&H7DS zhS4`7Co}X7Vm&kiJ8AntY4h+IsDH%_)OVPH;F&}!psG3$c-2&-_Zgr7 z@^u%k6exB>O)U%!KQ=H;eMmP#8Wd`do(YZ;WG#4(7c^!dzOzXH_c3Rn-b_(=nt@L< zFrRYP$nBhgmg}kd17{%L`@k8<_x9)IGD0N-ir^L9@s-7j$3`K^oMnT?nsJ-hPQfyCxc64vyJqr4vM z3?jBl0PKlakuJMoVtzVgbL|U6FCY4ZmMb}{27J&W955aGS)q$H6L5B3a;8G==SpSh z5U!-^Yh2RM(S02Fi>=23qc^2H?nrx_{KRquz82Al=YSz&91Hqy--#4zbCX(YknGbJfoe8g`M*}#E* z!onB>9&url>zk zIr+rRfg>L~kGQ5{Z1Jm{3ulE4Bun=<%~7etC&a^8s`t)^qoVNQu`U}zs8Y6N@lk}2 z*~yGUJ&CXoqt43rZjTEG*|cL@U05>U=j?dOFDpJqz#n)n#ASLtIsU*@iL z;SV3=&emK@jDE_WNPZ0RGmoe$m}QW}g2R(_(55`cNOO>MPL`}X^06_Ye1h1|5-BN@ zg9n6;S`Qhs%6MOwGVA9Ac_8M!hdeRz?1WoEEakm)iEH+ zPiFKXH`O|=tWHVe$}ZO}Wn8;x2hs!GNT5}(tFVqDxDYf)B`*T>$zl`KrO=s=1KWV; zJXt2HhjWYSxkzTl8m>l3-EUy122^d=&}#NqX;C;$N`3r^2rzK$re=hY<8^gAHex4+ zA*H&3@LKdSnKoHQtp|@S_D{73ez9`)j31K~oum~YR^`ivC1sM?O2oUJBK8jJ4!2uU zTwdLfqP>zaK~k_3s}m&7r2T<`iQlmmBHInYoBhM-)aO{xA>~JYik~$tCrTQcbIwqa zRYZTJc{UzO;=H0X|3zdNRRni)bQVi#&Xk?1SILw8E?-pOv-Go}FY3!H)pv>*Q?grT z{Fm~AsypK5I4E02{B@WqMeDRah<`yF#2Mu{b0Rd!`d*w4iUc}>aAmyb7nmw`xN|(9 zPw7(LHIq8ErD;M6bS#`DUX&gdf9AZ}OeUi(7TC{3S5$A!21U1tE2+y$ZP%=lURaqC zh*dWo5Qg(X~G zT6qQfb2TacoNzZ?=8!oaPkgobp>oY}K352`Rt`RXPl|$xA^Ibqm)c7h=Q*w?bAgOh zE~12R1jvEzc&N}@E1$#0)JxdIE0%0N5z|#niuMu^_@dU_@M2>Mg|+^EAR20SVBWC^ z`Y{fZ>nN)@pnuTLgK@|(NQufj;XF^yXKPnbD~sD%NG*aHz4&My6bp2ZSW@Gj9pgvM zyEo+wiffyBf)QoG-+cNwQLXPe&3sB9Qd_}A05K&~vvNdm~oCI@={);prDpp>iA(WLEx~}4BiQ$5LD~2eIZkt}B8Bx$; z<&_Y=!?*A}b$GQN!*@L1?)GYQ*DuNoyiIw=`)0~ByOPt>oaz!C(Dc4jO7A2oeCle34_xIK#q;7rF04Qy%vaYWYlWY73<)AqD0u>dl6i2?Y@^YQDWWuGN!{OUUx!lKvSWf#UOg% zAEEDMsl;i;ivUoU1lNS0pOM9{VuEAcw}!6xNy?Wvz2=iN-2d=T;3#|(lcYLH%Hy~t zE;^23$RqkPO_eW@W(TNo3dXWvIa2-w$GCrqaZ<-&F(Tj|7(KW`bR#+3Z&A!@nT0pm zoFw>2PF;EDgDhx8j^}LohkspuSrTa!Ve83Dl9B2zqfmxrHIy_|#`75YdDMKwc|)L0d{h$P)s!11!NEI=I^*IW z;jq9NXT?o0{lkAp?a9S|iJ^pF&tmwF7DUB{aL$+2b#W&UrP*>4UkCmIm%Us~Khg+G zQ8ubdA)&i?N^=5wc4-J7!Y$e@LRvt9wL}*NR2d3XEcr>)`t+Kj1AYLS65)!=bUu&y z>q7xCRnVT)G_p&XLoatypxJTDPcNVRnjCaLoL&x;H)ror-g~}}hTO~d>(QQVV($HQ+&5j> zfWX$ceS8N@IovDM!E^?L#TPcY+6A8wjBzK zsyIK8E@N##8vt|~k2|3CW|HgEfc{wrbmt1KKWISf4;s+@Jx^xhs?aAep>Q7=MxX;f z9J{^|AodP%Yzr%OlvtMM0)mW%94e#^1QJctRN>)WZ zM*z6Q_rdIQ8S`KNt#SNxNZL}hHgbjQouU6TD)a>vT0eolSVb5;GSBu_GN9xYZ)03p zujk12TE(~XN6P4awLz-srsC0*n2S_9O2cD$YAzh~+ElxoZyP+?SxUGzpv`a;*gfj^ z5($|U)H~xxE;`KUuB$30G^|^0hau=)acw#q2_rZm_Kp!weBCBB0CH>+<9_v^-rXAv z>CU_Qg#Ma!n>Q-Y($@#)XD_af@_P^3Vu4^xznk8_0}Nq}I5b830126X52)N?He2?j z3}r%J{uAV(>Gcsx=w=MHV8L&VA=+Law$Qw&_7oRygX495_+Q!H2LEg8_5C9oNesM~ zj*Z0vGEB7~Y)!JscIX)zyt2^EFu)ArnTZMR5a{c8I!Rv}Ns>V1+VkzEj536;jbf*l<2zWdf`797J*J+Z*kjXYJ62(p$GZ2X<+FBmVSI)q7 z&d;YA_%s81&A_g3zU}Nd0i7KuV1JoL--g({uen~L;z*@3Yp0{R8l{dIc9`JYVnsIo z_8~HFs39b#0I@L=I}BXMh#Se&7Ur&@nvXjH&1N$7(*%5)fIA%yDeRko287Ifo+$o2 zE3@jFzcdbp9>YsIG2!@4-+&1}1Kmq{#--LzhLC$fR1G9T_)X-n(B)&0H^xg`-#i<( zRdl2o?I*+hnJq^{W`6MriJi&Ro3a)RHLle{utCifKy<^8MAgNFlj1_3ZW3v0lFw>N zX>t5qq9(as%vN)zB2mh7AcYh!7m&rlzi)sr9A+Ou0Yjf&&w>CK;IDVka}kGBpRXed zm_-^0)sDTr6U1{Qu}>CPiWXcj#BW9UMe=s>svVS`$ zkUs=hBM~C#9>k2z5T~Zx7l}W%~m}e zOROl=ef&T81dPD-UEk7d4c=a`(rCzVSA77F!GmFV9@#|IvEXQ69JL;IET~O2kw4e5 zfR$la&|Z7*YCDaE{_s6NcI+gGv>1I0wN^0Pqj3!r0W}@uL;N>UCPwJ{U}tD#5(4ArncgmAVKbX7Su9#Ja{Om87PhyblLwx=(!t;SrIFdE za`4gXw&LW^9ah|jbyD?fqOB8(g;n-XHsvl)#>Vrr88W!;QqIvvf`38`bZsvGX3arP z(zNRS17%_frp479WCO8(%k18aJzK=Abtf~SzyEyy<;!=(t4XW|q~lr3%Vt%$tYGB> zMQghJj>;fzPa=3^doNec+^B?^W+OanG`qdy>yg>>A4L-P*@sHT;V`E=AQFUO!HrsN zJ+?`KS)zQeTTYp0CB7+Xyh%A!=&-`vi+sUrNJ)9$k+P#M9)T|=$X}dI;vreHbOV=P z^uUO8>C)UDmUS$YA7$Hs$sqDMn_Un>Qc*=}`wT!dNd$m9NGo;89Fic42QF)YB)%En zfaV-9*C`9SNu!f9>X|hGC^zQ3^+TEU);35UV2{3|cfXMD{tEBjVU~7Z*M?xa+W=q6 zb3XGPy&<|xj0?Ucz&mBoD_g)hF+FHOEQu~S$OY+`l(9%lJXkkpJMl{(v$MCFqwcoE zCqb4v28BhuHTNlhV(SLA$+Oy==U1EDs_Pf`-km(~?7$%L0=$GDYKE8SVF<4C$kyCI zw{65U@Du)yz|N&dy6d>`n?Sn-)Lj}xm1?^a%oGFn?F25@-7Tg{eJQ4m$Ma1GNhT`H zW&>IHF;egYsGNgN4E%j=Xm0|Se+5_hH}^as9yprc1b6tak0;a0CAO@VAJ%ixw}@A3 z8}QHmAlADOqS%lgV(29BuhBqrGvwK|H#$*rAsXYJKug zASM4brQ6@e!`UZd!HxeNlYQsySn5gBQ}ICbFmJMpmC*UE2}@scdNh&NR!L>kOwhTk zvI>RozK|fHHuWdZkaI0adbklT@`1SbOQ1~I)PB7{v1@njn8v$)|E5Goof4@iJFu_LgsxzV27&+UVEO2_Os?*QY;&h#;@4JxS?GicZRXXSU$NGT{f@k|)H1>sMf;ztnsuna?S#fzd* z#`&vM2(hUA@hyOqF#Sp9CjmJPacn{oge!zLUEYzer}F%I`NexGW3)@NXna*`DPJkC z2lo#~BC%^NNKym&nEM~%;P~*zXZ0t%;kJ88#EgyNKQJkkJirX{2T^fMB`FUqllyvk zWA?8>y@iF9vPPLl@&qKiUWryhA!kr&7U-zNu@rTp(ZGT(3y6x;A9N__D$l2lM;uoU zV#s#|W#@sT_%!}7WteL-s*c=xp*c}yTYLOeb`l*D?+dn8aYz&dst}Qbp9z-vy&S6f zxv?^=KHS_Z_4)Bn)d6<$8R?}=O$w0HZ!KgerEC$p$NN%#y$5|v@0z%rmGW8o)54BN`oP8*6aH3D0{SPWop1ldm| znQt*s_)0lw#j_K51L!bIeNL=x>>?KYyd?JZ0K_^YC0NC(4KWh5w~_ZcTX0`Tka1GrFT|M#LT3jy{*jb39SraVH9G zN1bbkb!^2F^aB&P-R58?nqcD&kAPqou3+nZ{go5|Sw#YB;zL8*1Gvr&LFkUiF`|hM z)JxPDa`&F0Mv)h~G2EjXMV&(<4)i#TT(gcaAZiXB81$X&LVG*@Rj;Qw`OwSOeJi%f zkhNpqvLj@}0wS+MRx==Sk~O=q-zh`zd0f0X{E4)-w=k4$Ar5Kd2^qf^!VB;4#5d5> z7Q)o6n7pIcC&%yKkpCEfK49|>-<>fGull*nnA$iVeo;gt6;pZV1J|Y*ray3o=?|P? zyRcUw=$-&no~S0K4AM9dng@JCcR=!MsQ`^yh%r!tp%+uwB+-3MM_LGwtnetAR(*!4 zHv9p$^)$ntX4v)_R-9ZPX_9FVoMhSqCs}_$3=&&CaukFBV_QB%N$@|)#q}WlA$f>Gol0tBFkU^$Hq zyJ;;V`tNB^lk91deJg9R8}I2o%vkguW-EF-I2J+b1=tJrE+3h(q3xQ8IYWIBJC>#r zkA(-$VG~2x0aqAEhBfF(#t2XInaphP;DZ~_10{z0xN}Tv=Jo!0&9Ns_@DZlZ!z@AX zVTPdB-ws4_mI-^#1~@~F;*hk9iJ};XHQfjyIPQ4Zn|$9u%6tOQPYCQZgpFMyhQ!PY zwT2xCVoBZbo~F;!^m&+_w_yf7%*^v1X61Q1P~5|{4(7=StS#V(Pk?{Lml$c}4n+^N`UJ7t7IoNA_jNXQkhjSMkj88M@X$JK&;A`HG&~IQ&;IMxa z9Clt2^qct6y)vhZ_X93K=B7PUmif)~%)`y^hm&O+vqbe00UgT(Ktt#ULXnkIAOXPv z7A1$`a0M!8k6QhZwFjIQm?H?7gd~Th{}I3r#(t5&G$kthitr(YSm1UyukpruSU^9I{jGhMRZ-G!i{jQayGzvgicDf&B6J|10+#)Qf>Wh*`mz zK2Vbp&WIJO_k{qp1TQ_Si%bt8D3G$^wWvq&>Ogux3z0J`iTbi=U)*uH0s{pn@yvbd zGQ4hiJ0%_wZtRZ8t8Cc=C0q1xZ3(%8W1RM}g2n-e}QR0RN%oZ%JQ(!EJP*RHx(=XqMR^ zErUq30lV8Uj)p$MvUmBe&F%y#=||D>aFWX*F$~PEP7gJJ*%dNlLx>w*Kx*!#Bb2fV7ba zs)APNCn3_0AcclQB9c~-L!wdki0-H$fi`f8gn|Grjh#RPotj#Y!XY=w(=~Ocq!DMk zj&#GY#c9rADt^P@xI_R$;21#63Q4CKr`ZUJMXIh$v zb3S~^AWDi1ouV^PQAq*}PzSE z4ikrkfKo00=@E*o9fbZ~A(c6@p=;(XTjKex@^r^%5geuNMXt_lI>o^Y2b;gg855~B zr2Jl{RE78WzO|w`;|e`GR}?>nokjFONSoP{U&fIu53|9U1z@Bz*2&m;KHuD2d7{Ae~w z=buFfwmPtQ*OMc%tML>{`iZ)Qg93Ipjz|50xA zALae=2A-{h96{4e%?cAEpzI_spntW%1g!$blV-S}6%TyVwQb7-;gbP%aA=Afk!4zb zgA&ovJ=54}ly4I>UKi7Q8s)#xQSNmL@9BE~VXpTd=KXO2IOv2bq{aXO&;!HAfDZsZ z^K27z_?`;MNS7!?%*e#K+W~b0$Td9>IpBKa?LjiTKFo1Qa@J8b8`<)Wjl*1Th*i)eEG1U2dYHm=w_85ZLepVvxu(iX|g=7Fq zK()VOp!3445*p-M<9Dijk+P6#T46=fcLnI--=oOaKT-YY6`x)dSDYL!cR z3XaZEER2It`AdqbfJ||Lw-QfG#5Wnv6!XRG6SDLprjb}p_;HHDqyv+bK8bgr0%0wu z8MqY&Tr=@&*R_1Q6X6{Wka2bU+;|7-NoGf&uoe@o^|>obqv>}`S;JA~2L4mNRxSES zvA2*zTZn;Pi`EAT9SJrsVXv>QZ>W=RM{T`O4uvc~d}0VQD*|`965NA-Vn)4)xSPVf zbv$JF2>V9%1d*MB%Yc{xQLCxA7_=KcTlf^|iYchYqv}Qs;qQpb4{@;x1-+&m!ELQz z?M*qrnjwsg%R znOg4LIl$Cb#zx)%aR(^xJ~mkcWMLx|2C{%p-(SXNP7cu)Tz6f6-Nl;eQ;x|Rz}Z(!SM5<&MD`CdTz22r58 z(82=wE0N*_5V?*hbs0j1$Z2uv9C@}!gj3pM!mEl6tYV2U1r`X@EGKl3T@!#bAM!oK zjSi5dW~n<;mpzC8cF^7Y-_jksn^3RTt<-C|4;YH>1BPOMzTqHnW5k@i#8PQ6S|r03 z1z=QycLR#lAk+)sdAA@mr6-|l<40stMS4Ivz%Y}|=Ft!1*>oRWcUpZKiVxL|_PR%- z^MKLlJYY2TkIviRe?&ek=uk zWDvMVKSbb1oYg)+et4jqk}EQ#715Wr~G7Ao*NC;tYYd>Q7g*^>n2Mm&fg@2cC0a!WOjtS)>)?}^A z)|-O%?H?)+X*n@81*>&qn^-jw^=wzG*1Ob(`2N=It!n~b(FAVa-8S9Q-_%0lgcbl> z2JnLJAX5QbgpSlbI2#?L?**O>p)#cJ*~qYU)i^Maz>d=Z+>tu;A5^!VmIU;MXEnX- z-h+i5)|3)y(}KehY4IRZhO|V2YQY_9`Y|*{;S6;`!)n>uAb|Py%{g|i52u%}&)?Ub ze%m&;A5UMrqph%){KG~8*NU&XF)S>7=V4*#I}ZzM2W>Jfa$stkq(2tBo=b_jInIO@0x-IU+%;fceB^c74b}q3G<8$mWms5Sh6Qb~)?do7=!#8P z_O7GCvUeR6R)2^fde%$K(6{Z#1RsoQ5{)x*7Iow^!rZkmRtyta!M1MersYE0F2)!M z$u!nOS8Hh78^|Ws^lt#l!zI3;Uz^^V`VXi$Zg+U*Ia-3GBm`x|Ac09p*;bq%g#*Vd z*~LL-JE2;{+oTQHN6cPNX7b`D2Zx?4lUb@ z$(5TO!MQH`_wn+XQr0E7FFOUJ977cCB!ukbz{&#_BB~)ok$s`6EZ&qxHPYs)V?Z)q zSG%4y?C341=PQvj8saepw5D`e!ft`1A4ORzE7*v|19QdWy%wDy608#gKOs3fzxnRS zB!WB|h0kQ4lx6J6*IdM(W*z+pO0sfp6+KumdFQu5KW>I;{+MLE~1ul}K2 zUU0PJmCVm*(E!@+S=%x*P~N;aQ}z+Pm@38f^2$BAG)-gv7K@-yBQ;DHkeg!qYmQIS zm&4_3cq7(OCPW`7H|e}_<({~bevsFELUJH$Q1gSp$-Z7XGPw|DYkV2tgbK~;#<%Fu zk84-T6&525xpDH! z#~9$8!>OG5tl&T;Rd!G;eAjqS@`0RyRDVS;QD05y_G@^_vSRVTKwHr;!`uz1pu82R zxkc}MC({B`7Oun!m(eanZBfPkVfy#iag_N;Cp5VTDbQZVS+dFW<`)>^kd3v zIi<<2IFFcoP!oPdetr14kcJBBgd?;%iPZ1oiYOIx3`@|_mpH`sa=cp7KPS^`OdI_1 zlBPEPeT5kvrgZ-+tg!qq#_LD8@K7v&{~I4leKAGP-_90y@io3wUd%q;{^9?e41k#y z#a-YPR@WG3!kMDqt#xa6g9o}XhIk^uL1|R5ALTPRNb4prwVrv+$j;e3Y|P4zN!e*Zh-? z--LC9-=-;lIi_l+A`Mk}Jtlnn3GMr@X-%?`Ktm6q3xxrOJmkH-3y!{H4#j(Ylm^lD z@n^X04hnnZ;12H8gQA~?qIY004vR}lu|aaZ&N1^#b-_Y7kaY+J;zn-&6Y>s{g;}*c zW7=hexv)M=s$=3b$ze5QY6L5+w)t*^^@$fvUd?BPMOfOsc#gPMVY7!+O7`ZWp|BpN zf+(Q89Jl|DJjwKCDEEO4S+>%vboqJ}lx?(oR)QP@XPmFU81zqQuHV?@l-@odwz{R) z@9X7~Zh72p?&y{RHX%^x@)2FXrnZ>>@clJQp7epA$lL|0MtGeL130+RN-!4wDZGt- zeU%MA!wVCB=ty{ z9LG7Q5~OY* z32JXBGjGdMeW2dF0f6`hAnv4kSEvB6y92RSq4~pCXItmaoiDFRa zVr7ZLgpzuUFjjRfilA2#fx-ZWiWfHGreM~!3`2d~rNn6FW1ECD?X?lM#4>Ijnro&cINw&A+z(9?<;FlPzJKgb*(z9IgJYVR~AC7 zHYPVKlC2PJMc|dcg10Cn20p?`!dtc0Z0WRY)KMiUz#jU%g${`hB5xqU-3S_lKscjh z7=waQ;EagKA-~>25#1hh5O;)8R2Z6={O;Iz)QavjuAR%5B}O~j!WXt zEWfCn0UZBl2PJQzM0nED1oBN+DYF~W1Np@nGl0&V{%(dqJ(o31;BK2ncn?Mq(3q>h z3)sC`%nhuGpVDj5#>_J+NFO)w8E%B3ej|_I1N?kLv8X-B*~j;OwZ~7C@^x-M!mY-O zU$XNLj9(*joAhlD>JwQ`y9mrKCwAT>ue4o`?fEVJNx>s%x!7<4f>j&I^M>gmQ%zL? zTLo<*2+J zL2TQ=(I!D+K&l4%yrJnLl@5xBeUmf#F*h6^ULIbJe|mdVMX$9Q!dnbr#=Hq=8i$eq zw=0!E(|4b``tDP=ebwS@gr~N*t9X)}2Q$3znRr#K5C9GR9x!95zDaVIdLy;9JR)bel=0EiW+ARmeNG z348F*f?g--U5616X#fUILxIU-Ap!D6EMm9;SdAg4>S-Y#qWsYOunLImY(gh}cGwW$ z2gh#q+%T?tc8lW}ciFQ+`QD(4xqLh@rKxEzkB3#Reh3dsUx(sdzN{r3O5yQxJRN@*ZO@LaLcQNza zNTk%OMM_jY_XNBg;daRlBU59a3uzbSLxuQfkY6MI7$}DU3PMF>Z7+P5Z6GR}n2ThR zRKhyK!G|_^&Mu?j5-uqCYUmrh_5!Pea+xj(7k$V_X21!@FW!nGiw*|5U6d&Sdq*?c zou12*fv)mvG(IR}3dAVWMvZb`PGO4suEfzV4b zou#Wr>zS`*jY zEIwh1<*fhrZ)a0lnBP(wch|{D3F|0Bw!lneI3kimIUtn$hRmOjoYI1@l;oEm zA<=FirOW%~vjR743I1!olHo=~K!?0sb4T(HB z7Z=@SX~TzAD%y`iv*n7`_+g!U7A(PT*TJ>;xnm&{B{mkaTnG}0zpIH14$CY*IutAm z$wN-?reht~jFZ6zfv4d13z8=Z1) zDJy&~e`3vwvRI^=5(a`J;J)}KSYQp75%=n?XBURJ04wpGe9{Rm0Fo|>Z>T&>`EdB7 zxPfv9mrLZ>SigBqOizD?k<3|=Zv0QT$#|T=2gl?QREAXD}c!v^xoYjzSyLA${$GkX;_*18#cHt_Z|#;#`3q9 zLo889;#?977?<#7cHKc2^#)~-iVW#-QvK_jC?&~$FP69Njgtl4HdDg(39MT?N^-TV zyjPnNYSympA4dn$G|)loLz>z?cwH?d&Un5GT{tKYS`e)dz`hx2F60-Wu?;GyI1W|Q zH4>f9&9-x-hXcK z`P1mkd&wyyxDZ*I_Lg0TBcjrffH~%Kk)<$)JTa<>3|FSQ5W|TbcJY!K(ic7@zNS-r zC(_XYJ7jbk>ZgzEaD}}J4!y**_?JC&f#C>Iryvj^+LD|v9K{?BgqJ_GSLiN zPk$;ju9F-($WXHs=p)$%eqOVV5%NGt+S>x)Hn|*5|U74x*oWi0w-+Ep< zzc_h&@@`1XfCuH=>2Y3byKzTUT5A!TAZg+Tg>F~_rQv=ek^H>;o)?$pYFMl`QQWzO zxm!1}7G{el*)*I~p^~&Ai=&>dQFt-8HRvye+?>XlxvPlP_R(TIc%1czH9_`S z3p5X8nm(jVV=Ev9E3hgCkbMSook!&606aNhoVHxZ^Cr;ch1iv2CYobHEvW8u&|D_) zw|6qTwo*%5O}v&{o7Z97v~O^+Mii;_04hC7x>= zgq^jM&`QwFVUR%a*@5tsr$O04!)ZX*b=z$WHjwIjp04e*s%;%?T9e28uVq!+?wOlV zV#x~s#v;8mdX4t- zQxu;nBwZG)dA8%F*&_&@@-Vtc!@<4ugd7-hcSBw%U#6U(xfD)I5<*cNd}pU1a7*;z z{V6PTc%J6O2^Fp;{(>D(z6ic7XG~lvQZovQFY-Z+%CF1KVHz?P}&b@6(vt37mowql$qCS0!_(^TDV$aeqT}JLawRuYD@n!I^>!xV;x{@Xuy`gwHK~zRh5(AFX$-pbf zkEy=!Es{pKKiI!Zu{k;u^BJ%h&;_?r{D}u}DQvc+19(v;;VyE@x`0j(B*H8R|7)Wf zkdlZq+HU7Gtj{k_$6#xMb^yC}LEyH?H3H-vm{BuGxp8X}jhf!3(VsLu} zT!q_+SWkvAg@?It!DIE#8SW~hkHA86tRB=Q+>n?_4+s+y`y0AbBubq`tFV6jY{U(> zT-`sA$P3>1-9f(CT@^YHeWHmScXx*rX<=O;wmODi#%~@kC-T>fe)R0E#QD&Q@CcB+ zIil#PkBEs84{Q%Edg=ymYWvo5MO#a^TebV9{-!<%^ZfukbdcGyT<{HPpd5lNWPFkE*!iorm#YP?3Q1D1 znLruXmAWyb?R(Y@Ih#fvy;jzo#8fgzsk{N_s^*NJ5!fj^C8`DKBW8mJCsN=BUCjv z2%%-|sUZJ=Pss*Hehg+1^m`BjF=t0OZ#iHY!e@l`RSK)9$Z!Htey#gm&&2RBAFumK zW&;5h#oklpsF@z@*{ev`vIATRsaX{LB-EM#eLGI>t9+bG;H=^7I znFE~N1JX#9HUm{5my{*G?LYqhKf?|V7x(j}praKW4R=#1(XM#GY@{%nLsB6Vkbb~t zByq+A@_Hh9t0f44FmZ%nK}?I=Yf0#Jf@#xhzWz!1>HPh7Z{8?x&VM{nPA~WN{`mX< zR^iOxx{*;!Z5b{6Yc2!<@i`+-sD6x;{~bsW=oLs~jj?sa4?!)03mF`a-16HI7k)(S z6pQM#8yHWyGJ-Ca@snA0VgADRXO zmCqxG=#05JZ<&yvd86r=#wChC8=pu9414E7nFF4o=3MgmG#*krist5m`CVuP@*~K? z%;>m1o65sK6wS)Bx8R$-hSZcO?*e%aG-_$U)W)%xi9}E_mt(37chYPuRE`DRHfblR>cO(ot7D;qoR|g(GhYj?@%OhOa_Uek_XfqF7=; z8=ZSRFpMg?+k+w(rjl8(w|91Ue4<>QD=$wkY50rwd4tv)5=4Ut`)Q={;A5~UM~LO3 zl1X&UY*9!o2;zk7nW*|v=D`d{BhqAmAObO(|7LvtR=7Lh*$}tVF{Y`Rj(7g4(p>zvT+unZ=DzIk zhlW=_ESRaL58P1q?K?w< zHWtNdbguzgKR9}Q2rJ~x`Ku1_(8&@fY0Q+dXrSgukz3%NVBxP78iOxl~rg{d#s7S|0NDFBfkn^=10|_K{qC;!d zhajEjP?m-n$H)}1Eu5K*y3({|+4`=jB32LP-g?kWwIie`hj9Q^RTF?sDwh= zU@y09lA*|B!yDxtmL2%ZQIIYZzyU{*|4BgGS@~$wP2|%;QZsWg(0|}!p0iO|H#!e- z!%aNoZKTcIq?a0OwmIpNO~5_TDQNG5tq~IH#PXhAVUhYRJHquEH*W&!;e3Fk5s2*k zE?Ntv-12YV^AJQQblQdH*7ykz-9DyaH#dwdqBCE(oG4dfqIBm9H!s~mw!!c!8XXKkC|Z*rS=S!! z2rt&8YMQX`K}-UnnFu&OD}fE7fnE;vQSAWkP^V?pesywr1#c*5UM^*tFNljT50COs z#P)(b-^)vQKzHON&huHJ2er7ZX3LK0nQso?oehc(Y9{Dj9*%z)xBe_rZTa`zvVwJM zLjrHL87OUcqcLK3Dvm%qw)eQDOM8x6y0kweRE03MWm=92H@>CAk*`~@H{&4GBR}%o zL=6ouaG^rt237zL?bw1AwrPPwJGNn^Hpi`rDBxp{+Xjo#)42Va#;w{pZq5GV*6crS z&0WT=4PhH-F>Ai510HItNr- z*`IY&|9lEWTwDe0-THG0ayq{}%N_O1?nIv2R~Nq(;T2MOBcFFJP1|-RsK3?7_SO#t zdRI4g$!)7~B8bl5?B1qoL8J!nOHQYkhl4CVO#gl5dtC8K30Mj=^W~)hkaSve6 z1b5orLFFCu-soXrZ!Dn`d;~V|Yod0afE+u#S>rUIUq?6c*B0zigKGKXY?yH7=D~aC zz2WG;`Aru;GQ6hhR&_Mn>*an`JB<}8pmW8q54$T2yY3}_?b`lmgE%t07+`77L*|N` zAWOx!HTYJs;KS$OfgBYMG)s4gu?LQ^#4!UWv0air0((F`tpGL*?E6Qi;<2lm^vt*j z2#jWifsr`y2fLo_$BB-8z=X%d0yV?8&>)dtet<$q6RYs<1jIXVpp*-EW5M_)4qLB# zP&qpcDrbj5<@6s^NG)**qXys^kZA_cAmG}KR3xf^4kLuxXb@!;snm6c^|^{(?9w_)1I-fgjIu zjX-rAK%R8N)?EaHY|?PlkOt*|g9Y@?L2Se@VYN8%zWtE0hKBmrJ*2kg!vo|ESLXFf z=FsqFpA~uVt-r1(rFff-NM}P*3e#Nz5&-z{a1p~O1PGfFv!AsP$_RySVU7SZ5D~OsECegTOyiwo2at+XUj061GGjktu7| z5X5OB&>5uwJY57x73De*+-F7k-_kuDq}SvamuP%u6}S&4{1gI52uTx4x<}!;2~4>Q zvpgw=5LH~(JPjGtJ=$YW{(|c$xkn|AAF!co!f6rnCegYu;~1zZkpPd|1YU!sVV2c| z`#a)0Wxf_pB_O&QHq3vV3F^uzpef;+AbVEP4u5k3|2wjVh@BLzjBcYml7S-VK&2fI zW>yrpFJB+rIPjjwjjuR~gcwu-5}`&ce&Un|@U~E9ghCi3k?H|d+;hgBfFuDq517B9 ztV!(#I@gdgk#h3t3>KNI?1SFm6RS26zQ-5x+0TAOSr|e=9XcM$LG%EPz1QLxz-Us<4VX6iWVWHYf`k2Y${fLq_nC}d*nPf?PeaeBr(~Qf{%)T?` zX3he0ITD<;FPei2!Pts4I&5GC%T*Hx@m== zbjHCeXq>YQy2pFmg#rE`>uF*5H{#8ntn#{bEoiN(v;Huj*1O6|Bv=HWyjo9QttYS6lUM7> ztMwP?)p|@n*5i4u%02EOKCH&w{m-nS4wR)4YntgBun|MBZ$^d&c4y0rBHz;jD5v9Z z%hL@5KgSMeI6=wacu;OcK)T`0f|R7{nBUl&Wha`AWH#SMDhHyGu&NQvj2#@NBV;l; z-jg-^AZxZ5K~H|IZ{gQ^JWp0_uJ7#AYTVr);c6LH6q+W;c{SipR1ijn9%wBw-+^n= zT^~?wqO!LW6USXWiHU1Tk2SEyTUy*ufcHRrsaonc_HI2b6u*##;;-1x^?07G+Qi-2 z$JMyIe-e3M>!umoR-`6AAnfpBd5%RB95Wm2Sq@eKC^f+<@GJuo7M`ghDZ@kLD}b-d z)f&?t!7f+-#@?={Y5z3se;Ix+LW*$Fg&2NP^7`#GM8Yy_qAnXyY7R0AB$;El` zmi(}3mH!29f`Ug_((?!k-m?_ESa9=Yv3SXj*HiXU?6Z+_2)bT$bs80NT}&r6T+K^; z4pBaHHceTvWFJeXP!D;gXr853B)!1PbHmOgRtbTJkyVM|p@L&$Mq1I(8j*r6lDlL;CH(f{t#+P;*f=-1|Ga2>he)~6=mb*trG~3+dYv; zk>ywK!(uER-5~WFY^iq;(n7giokVp@iKGZlTcS6UWMITqh4uwWZ^0amv=z9eR@~gT zB<3Z>&!nW|AXi|?mbu@N7@6an6ns~>c|tpn%!m;bT|@>&4-uMWLZuBvLm;D?2^S^? zKX`_Uboqk9`fj#Dw*}0&%v9AGi@Nc*?1d}TvxP_-aZLLYCLfM4PVR+kkobkEC8vTn znPt;6De@UG`_eTC$YQ8UlNwpB87IfQpbiH2GdmsENVe~pF?f=&+m3mQ;TB}UcEPwt znrdi(MV$OvtOEJ$zDqx*#d=0goOpV0#?;XV;zH746JM|{n@^vGYnJJph%Oq#>-vUU zO^|g*RMm+;txe*hs=`Z3(m?4%49`N~hX1Rd>grr#l($Zf><#)5cnk zZSQOlwUlUuhau8GV`k+Xr7*L+Y!J=4Fr{q zB%u5;lTdG_LF(RDpw8A`jyn6f ztSCVIv7~g@;|ZZ|-q1FXZu!6l(k&lo&i89lA_PiJ91}o65jc17gC3!j$%=}Je~5;;?n4?c>e;#+W?pviML*h zIS&k!{MGg1Eg#rGyyXMU*?u6^mMsF`cBwQeN#Gg(7jMWOOjGPJh@ia*osDLnY@n=870j38Zk$5qsUt!yk zb%kUc#?PiVG6(mFNJobXG#I~Lz*LvO-uHtU7bZ~C%rkrLT5iO4IH(ZG7d!qM@=2w19mT1Tz-^8#^x$GUFLE~d7K!Ln(u1$ z08}1Ko`#pZ;LvU>tcgmx?4n&$LA+fBY({bg>t{Gf9-HBtWpcT@SO*O(o#n?i#5t>I zU{yjxc6%ANAtHt-$SXKw)E#SSV=zNqYn_2*>MzC`quy7epXw`UBbGOfE7~;bDI=;M zXkN+_nRWoAO4wDQi<1v*kCqWToRf;fkyEb<{u5c*S`>v|}j6;F1R6UKu9tG#2V{bj8t{uS$Y%zq}nwB=Nc6me+?i~gDS;**%fDX3}?IIwkvMsiZf`%)my2~ zrc2J*bjdYG{|GD(#i$<0j)3+Fo@20~0r#EAXd^=Y2FzUyxz}M*L~aadIw!Po6UAsH`Kr#!i(?#>5_)2j^n9g6SGhZNuAe+~!q zPm0SjMG%qNBFH=%!+;oMl^!8c%VeCQ%n~c694M5J=UQSOB@SuBV2{_|1w(0;a@$tl znTq_6>%Zjh1A5$%*_w)&Zb%5wM{7ksgj%ynA#5(qg?U{)Io)nI*I)-E%_u&ZKF%;_zXRX00N~<3|66!7 z>K}n1fNmHc$nS>^-mzo|D*Wl|2Z0bA5hytt9Q%88Ne$tCLc`n z;|M|h)~8@H{V{kLAefr}(7zPA*>v6RE*5YDU&GQ~Z=3zEXFFg8}`J=wN+ zwaTK;-L(Njg%QG`j-p!^N`sboF@T_TTk`?Gh&*H=5OhL>DTxEeF%p1F2X=&dT>wfG zQLGb|%y=I$DBIiwe3#t#t0PQ>jh~bk7vV1`IIb7EAYzN4;6Sm0hf+hPB}jO@RX+IU z*?s-J4+r~~M|3^{q*uY{R^#tx1OK%&19Qb8Ue(X0NBGl^@0%`?e||)^6MT|Ph|YuX zd5}5c>u)F>?b2*q1+6A8S0C*k2g}}gS%0Z`4B0>IbR>&ni&)dg%#nt?qtquY8P%Cj za;fw*eTPiVi|`)Q++j3uh)*e7EKakt?gV)vndDpM+wh5qLyn)$jbFEQIFr;J(~P)7 zaz3KYkg}?fHpJzn+*ifvN6t=o?P((qX&cYn_q?_9Pdn8|6mjHHe#AsN3$bP44{*)V5HXt9Q&q=HQz$htmfpDk@8gM!ovb4Ue z1U0}zGpI`#IA=k3Ia3{n4|jZrk>lJzHKAgfWnl4KqJe8g#W|v!yp+yTPB(N2QS%{b z*5y406Rl@6OlfA&8M;IbHfLg;A9R5;2A1!eF@3!RI2mm^W?dAX{)aK~ zPBL?gnz6xf)`2#f#zJJRW`cTM64Zc4piW_0$RZ3RE+B59+Kno)3okMiVz8&f>ld4`bDJRAxNZ&4VM6tRg zOJqFed$~FpV^nx!OHw6H#zcjq3de7VY7%Q12N>RmI%vok>h~zaPCm=Uv-~B~<@B`U z!;5O=X^%g;{eBYMBQJ8=&INSjHdPzNkx$k|a2_5Wc!TZacUVHIUZYk)?Y;@butU%8 zE;i5xs#WEawE)`3^6u7K++B{tpo`yB_PvI9M%!3Ft5Uo+RlDjBuzDqI)iceD#o)~a zfRS#R26(JBq&>jl3(^C_jC^>Q0AOr|T7r}V3z)8!P8mSD=~==6b+;C|c9Ap;t=Rs{ zyLpRMBLXDgCcMtTa)DX~f;%_ST^m$liAf|nNrgtX*E>Eq@6|hUat_lP-%R~reZaMp+x}#kk#5$d3vN~uVNQa zu^+4nf<@q7DuT$~BHxRGDIun|A(XSWgv3Cy{g{vyZXS_4O@j{YV?LV<V%$;vhDHi9t<3NAFxDg#1WwnrUP}OFZ-cG6XaObgWh}2fnx|sVtm}S@(@t6@$ zc=}|;L9NJ1eV$3Xq4B9Pr!h$evk}vplk@3H*$!-Bi|Yc6t#6OMQfjXc8%;j+ z&gP$UK?uf;y=j#1GNUq&`y1fHNXHYY#~}OepX8oXS7*dCxOgUyJCg$H zpQXU??pj&cVyLoGuJve&qs0>M<{>semepEKa7{#LLrn2b&Zf9m&qhgBk+tcod6sD>QXjMxIVEcJ#=#@w1 z{aHVcq9t*q0tWt}H~}FIk)bxd;n9hhgr0kqb~!(4x#fH}{>(P%fLdVut!pe<0u)=A zUfBlP29Xgan==X9?7!&9?GP^PMQNHWIJ+k;3tP)*mTM^=L80O#wL{D5F0OqX7Qv}_ zALz)Xtx%`h5@$-jC&7*P<2VJeqWy;Ju(EkeU!5Y^j2Lz}*o40en}G|V?+DgsNRJ?dEFrAvio^zm-HWomLr zO~>xUa1@Z5hc}I2uum6_D5x&^T{2yhim2$0_pAU+`H-TXNxdhh5R^`L=^waBw>^gX zqQm_$x5?4jo@$a*yvCWKZ#Gqb7@TXN3_^rO{D%E7H&l(Ns^6eQlCs0$7<|-__6pWA z6ki+5t)G9S@qNzZ^rUxfXB_+D`zuna0iM7c?5EJ)#? z56_0K6OftkhJe0Ef$YozI%J`X(?m$c3uBF`<&j+YG$8ab#4MhZZ>uc}FqMliKQEjG zix-|QVySN!EUcZm8PRc}jt(w3oEDEMmUOw%>;Q!ChxtIBKvP*StA=1AFHDz|@zaGP zOt++(DtL{ThL{UXCtRSK=2I7*HKpb~jOkhS%_&!9d8+R3v+?J`Y;~Qs&S8L^Xj16+ zK(1tz2jT=A;s_tglSJ0@CX^wqbbUENXp-!U!BqLo$mvA-msIBEVojw1wcX?@&F%?e zsqo|Z>>jwRu190q>t}T4x2dLFu!oU0ciLj9RvDe=sUj*o)foA62h#K18$OU-fnO-9 zj86Rkp}=OGX~GdrD0-7fmPgvD;P)28qXV*6xr*1 zZ<%Sl-U+ALt9HZX+Nt!%Rh#BlIn%t_C%5*w#(p{C_B2fwK}XvIf}8I;R%Atirz0Hd zMF>>_#>D`{6A;S5DVD?u)(izWsF&hjyUjRvW(0>k; zM!q1x8FbQ00?oI)XT!N%9DY2!_>LTHr|(Y=`Ny9Sv}I+s*@uJP5in&|VZD4fe2+f$ z5b_1->J>$HanDKr^t}Hp7#IcO#x6k~B79Bn==A*XpnvuD5^?DL!YyxmeK_~tp6-8l zb#ZVE?_}@d^8BFUmg9qq-rLh7a%|EI3pX5{p6*}0Kf2sI?N?nnJICxTwMwsi*SlE$ zv9=#{@e^Ktes$dI_e<9x8cVm%iKMaX&lQ@9{tOdiw__d&M{IA0lb5 z{NusV!AG8ytM|Rb@^F^1^a?*pD7|7o-eH8!A@N6t$8Sq*Ng0xI_TiBHuiw>PKRmgj zv_>gw<-5)=mgj8o^VP@R`C;$v;SttS=@v+6aeh90w|_3BH~%XqI2=QEFU~JXn2rvR zYkBg%aL>^}??lev(k!J_*L@J&V_>_ak|2S*XjuSM4R~J$cblzm<0r`SY6XMBY@r zpU7)f8;ZKJyrakqOIwQkxq44g*XlMEdAV*^QCIWZiu|c=U&*f4Y%KD6&Ca4O=64VE zQ-1qUKjrt2+)vfwxN--Px0JRJ`QxVwL0H~IYJy=;jICA$EH@|_X<#|%PqZBLCt428 zBl-bC^ob6>Ym|rRTBHXcl1)5N$^so)*8q7Fqr4z6Og~D@Fp10{OdQt}t`A$oirga5 zfr(N{1JE{G%VBFdY%Pba<*>CJwwA-za@bl9Tgzc4ELf*6E z5UBDAEDKN)7Sn%i6QFFq^WwlN=1*%ZR1~PIA3s6O&VKo;wJDxZzqU0hwnoL)sMs16 zTccuYRBVlktx>TxD%OP~o>@Qjc|N=iMqOo0G-SFW>qJ{&ooJg|C)(!LiMBx~18{P& z1}=1AffpS};~~hbgmA!X<(a;v3n0LF z-ZVS|$ySyX3oEfq-EcKW_@<$SaS+>}nemBsJMl$=${OD>6W54sqNIL_l2z! zr(tt)4Y^{^6b+WZiw${nkI@ww$TkB+Nooua>Cs`-j3hPAM+&a-Vw@#YJPJCb? zx)F#jb<+k#G3r2UKh~k?8>k=;EDI$ej;Dh=MDvi~6@V(%0P&@6M2;0ZMpPs#cEGpg zEprt=*EpdF&ChO}Y)eD77Rc5D*;*i53uJ47Y%P$j1+uk3wid|CSRhT&tmn6~m$%*2 zR2hovAbON|)^%4)h}H(+tn=RfWzsUcqr0_;*59^{RI5c$>%v9Ccaun9DgU`kTL? zNaR)|&L|MReEjywSaXL8?YiBU&7a8TYry8xyEmrKlAoG^#K~dqq+;5D?qO8}8}fIr zY-$ygx16Lhew}RYo7o&dvOqdr!QQO{HUhHSN!Eba;hrWG5?N!F9tUou(%(?GuC8cZ zRr?!OJ<{0U0O&d2i()Wzn?{&`BEk%8PATG>aGaf*mc49 zr6Fb-7Neo?UD%HA8zqN>yTaclUVJz>xgfinwAek^RTusC`-6+cA6cOAxOW1Yg~bK+cQya-(y!nyz1llJ z*r&Jkj{0@{73x5Sp8;q61?d9)4NkejX?F(-B>peQdI}-0lx%HH^NtS*t8Y8;d(e*r6_%gkY!F3Zg#>m+TYw=27uQW+;3t_?&C&vVc_+=)= zPlOjwFxa@U4+1>`q0Vv7SzYZnt${?>9%D2~yI)Wv2K1D|X$K(n=+`so<#r*jdD-LLKvw|M6xFu7vnXGC5vB*v^e(=Z|N8@10>E z^?87zyZE!OA!uV8cE5`03*h_aA@%?q>Y%z`6c5_@RHBeD0sb zcSpVNqu=}3{?dC(%DRS1DB9XrYwEhcUQVs5JRc!Q-SaiWY`V)gi_^iIwTF%P3j6ZP{Jq~_ z9>BZSJ36{LL$m~2bibZat}4v7G><={N2*$m&00D3Hjra)13C5@%dyaG%W%BViDKc3 z&`ko+F9%4p1xR}2hcG@}1IQ#o53~TmYy^&yE5|tVysaGn z0_51Jm1B1UId(UYW4C!xtKkIDq}|Ab07A_ULS#80zrhH7P;7$K+;KE3*1R}M0uMeA zH;i020LLXzJ(I}MOLFWvAVGggIkw4>tUFsd{slz{&{J@R&7{o-txDN^nKE8=u@oQB z8FsZcK7b?G>rg^ZwMza;gr5X>zb0j6oYhniGf9S~?6#I<5QH{Ngyclj!yb5$@y&=l zW?I(QGk)j7%NE?uB@XjJ!?zY+3Ku_I>IfGMh~g&m^uxu@#FNCHGk~L)20dUVX6EjR znZuH}lj+TKCLkyOJ#$$uh^a4v>FqQ}MopZCZOLFqK?Y@7%;>J%Q*W7|DNho%zKwAC znOut?{Fz~7);ANW8*|T|5arR@2bmZt_tbI91fF}cFGd;tmmq2^q6=pd#N2aCE-8(( zRgbc_J^$c43j7d@IU3A8cnJ#2AXZ#M4!>HofgS$&8*BowKIw-QksYmTb=TR7-jbyVin2kL zS$Z-a5k=+++4F@9RSFEhtx|tkp@ml+0aq(D@#_1VJH;(Wpwi8@v=HRmp>Fw_muO~e zgr?9e--&e7aSS((LknrE1_D323A7H~_gz4An4*|oY6CTpOu!A47-OXplO1PyCB}+l zGce%ZbbM%6K@i$LECn|bU^zE^ud(Z}(otBU+BEYW)+i@SPQo-tb3+<&V+G@P<(D*} z5nt74{;QwcR%xuB+gxc(skxPwgjHLQ>Tb9Y-3=FFb8{l}4IQy2+d%XQ9vsg%eR%I( z9hNXaa)jx54y90gng+|+@C^@ZRdf9aWMLYhK1)@o@c-D3vC%@*RxU)Soj(4vU5ML- z_^gFkXVE(wE<|U;h3GV1h>7Kcoe%Ng2u@ZV9w~y(g^dq@sz4L5Y3mLmS56QjP6f}a zh{4_GgXAxO<)2_h{3Hue-!8=MLTtJaSNkNOFL%G6Q~DioZ;$6+*|t zG1QC|p+b4N#xHStR23e?&oWDJ3eM&s5ojw$WoDI6V8n6O;HThL!0zfG*_H`%b3wS1funPLFbajA!<0N14Z9gay-e>x@jN851=V9j+wX9gn0xV>pg-8HJKT z8?PQ+Q?N6*@3u}VIHmbtY?6`dzqqz`GUxW1?&)(z0l*W~Xi<_Ggcl-4X3kU_n?-2=QD(KwgES#E!`8gV^rJiTa zepOR$*Q%_yOMhLI7G>97vxALD$y)-^-T4%L9(*{uNqpqo*H44ayFvElw8UGlXsTWp z+;Q0?FXq0RMfo%GZL~Kn@_*09Dv;0bqIP*X4AZaX$-&H$@TZd`usFznME{E=iE|(S z?6xkJ8mDh#N_FahpeVPL8+sMKd7{Kgz8j?Xl6$(B6JO;X$S)$KfLxyCXGrh14!E7d zA{rDH=dY)&w}+?w)~n%oe9IfjzxrW5p8W$oLCYY&yJE8+prGTiEj_n2UeNrVja{F^Ksp=cIp0+W{cfhXtnhiRess4} zOPl^XexS7{A6R%P%gN;$`65=jAgv~8X~QY89U=tHzdE@kH}{{2!ROfC4!g?buB>>AdrF)4fWh&`8phqzH4ljv|=|v z{w<~^1`+Hy7i@`%t0PkDYXS0Uv?Q__W+DTPUrZ`gQpgWrH z9j4ke4rTuHZa$s0Lcvi~w9O|^>GAH)cX%6g*!0k(H{Z>NvjHir=_>}K?D$|ER?yjq zvvv^O;94@KGiiE*0AF`!Z$6p8V#48qunnDa^cmDaKD(p_%%4Nw{27rQQdIJ1axeW6 z+8`fgZ*s>o{c}F#f3{lm$8bP@oQsh1K&g-Fvs$nARo~Ar^sl)Id2ivt{lPT-?m&GO zMoAvWX&D>MG_^b&U^HJHjVXK$4R-pvTi5D26wF{U%TJ>FHTD_dL*!jsjH zZWx!WX=GJE@&i56dOw~*eJn>MZ>}wXyx92vZ?4xwCG{=}y0mpEth@^}Q4C~b)mc-j zky`a&Y4^EOyHCA-Q1?h<1wr?ah^oa1B?kD9ZN~{xTXomfQB@WhL>yq53D9i<*9$E2 zx>{7Y5Sk7JN=(fux?4@xw{>rc=Gb~Jz)ev@U}eA%4$ya83lN~XuuU%#MjR(e;_FrZ z*6a|j-sQjIDZ5$e3ret7iI29q_-LDp&j#>Fao|}2oZZ4VfXE8yR^dkuGNo-~m_im% z$Pyv5PUx14Vi!jTQj)8qx=|Op9{?jMD2k7cxSOl{FDpJ~Ih*vaSbR|Dv_gFFshf%q z9@$iU@JRE@DTsic_^ubm*n`L@0no2*$A0AEPcyU(9Ko9Inv`mvL>e+B4UHy@_VBe^n%CEY}}ztLheV4-=DeqBRIh36VhTyfklX5#Lgx5-+wr3 z_2CX9CssLwa^sd+QD@~jdj~%>U3wLQ4)RrRcK*I~fR~WIcsUhs;I*I@Ig%mRgvh!q z)p(>lkwb3EfdqKFDJ8Q_D(ZYdv${}Lp}^mg?w{6IaYYvOnWGOddb)V^KAgEwp3hWw z-^hQpV*Xirxma1vb-hJ1A$tTb)|gybZ-;X+qj8}okxEDxq(A-LguKj0gK$FM+S zcks;*?%QMuC1N%kNF)m6$WvXN&2cvFR6* zz}}B16uFRDr$aI_A-ZrePRK}7K1UiPvXmAg#I9r5h9bDbJWQoPP+SbvW`dA5@P6uF zpWe>?%Z@TV-k{HS;x42~l@TVa>ehY0*}tjqsFYOs#I)oJ#>T^D*c?+TM=`z3SR{PI zd?dcyOQSE&OhKN)cW{tOVa9tXfp;<<(iDDAHuZEU#C;3HM-xfScE(B4ZXF%$ljE3r z%P}pC#U$(Nltc>QpvBr?DW;w+%xiixnJumNWUr%-{rNqOe7de^=;SuCa6nwkTiXna z!rewaEHw0pvLH|xS=uH32gN*Fo<_cJAD(JlgdXoML4N$WR=NDk96soC3utc z0&d3{{mu1wjPUT1<+)K|XQ677ch;p+&ky#u{fpoCoQrQS4lKS!4&HC)li{Myvz&$N ztTA?-Ce@nGhKWQq>OyTIQSJTB6NymGE=&txttj~kgaaTI$MO?^r&wUai()DUf@4{j zu}w%T78Fa%bM?Rjl531Y{bGTTZ}^r~rj;m37VuU#|nzaZS<2s1tjs_}+bE3~8l;S9uH zJ{doBuj#Pvo}L|FNR77cYE=c0QCk6&U9NClb7Lh?i1=L7X7R_ z6e@0LdMFs$33j_(IZ_Qyg53SJp}`ZbdM|F+BmDNwVzN0LP`ybPZc=+{_hxoCTrQ*! zBmIZr=O2AB4BSU^^!c+h8=BhO{NDG6ccVv`x3h_s+ninIB#7n?A3MwBkng(I)hA4#yd{jSxqtry=rxW;247#5%_Cv$e zf+$W*4I>%FK(;c9_P>pQrN31Epgk+u(=B5H;mt8z7JyC(96JW`3)03TEA#|vO_4dS zsXclV8#|`TNvtyb=V~dER&jCJ#)ga3TABK%SI5%MPPpMU!L;l)Bx8vOsIG4S8#o#u zmq8j{gX=lf`ORT2v}FhkO4C$XgQ_xZS`}`q__i*9ah7olOZ}ATf3Qsx!RHLrV1;9t zA6_z%xK&b)m%afoB=Z?3ejLkhNz2n@tg9^_&EH@8KK^zxF|to=vk7sRi$G=>XCi{f zVD^|Z0aS<6kls$3-S=i^=U@KmKm7+XtZt~rQhkgZ%)V-8=g)uouksJk>YW{uJ(Itg zEdiLElgE29lhSHEdgwTf6Vm8W2V{5eQuN1okR*blfr}B79u9mTH*j`%Ldh*E6VmE9 zcmx}Ai#8L!;q3r5a<5*!BlGfbzm215jwmGk>)n7V9@*Y{%}=vExoAHXFAl1a zrGZJ4-B8u2oh$)y@x4)2p1LSh{Fi_FkN>Wc@xh>hv`8{_U#xMFLMd1u4A-Sx8rb%`r_lj*?^#jY)|Ax_4vV5HNh99Tu z%rt#V7OJ-M9**J?;3qD|*OdRtKfqeug)P0D8q0U?jql*qf*i0XgC1B+DrboDmq{~K zF4wl85C1{RmVy{fH>b3_ix0^%{Zz=eEG#NVkX8HRyImHsCk``7ulLmDI6T;W!NIzL=&OgvE1ugAkHv)cFr|KtDK+W)Y3 z1`ygH+t3SwD8ER^yQD3i2@;DV|F$&97nipAfp?3I+M$VRwtHp5!57y{_LO+4U(s^#s*Ql^&UnkInDd`Kr^5V@5m>6^Ll6J{NVSOhvx^tE5E?u%j;E{Saw%K zt~QQWLx0V3!U)GwAkxFp08q-}nq1v{BFnUBmGREcq)R%=C2EkZwxpWu?5HGH*`1T+ zBzL^9uzq{k?D!s)V9Kfe(w{07jI<#*E+=sZ4r#nPRa@lpkwJm{2BE?L+3rskVYV7l zz~v6fALaMctpo;r2jKxZ0MOqL-t4?Txx~niV6MY4I|oWg1R-LJvh~@dQ01n!XS%i5 zSe{Flo7$ZRvx$J7xSP(!)g~D*br08Cr0RkCELCr9MhevACg~_%J=v0Nu5i^=Xy#X> znb$k(YMyAGNnkpt?Q?ZAN<1%2Y|k`xO^% z0>qr{u#~ClnYQb=FPur>E}>|`>`M}0H3wE?B1VmD2+dD)-AB=(>luDie_a+IraCfI z8h~Qs{BV!|LWb)5gLCW$Co+0mWimbAHMeo1!78~jHkK=6W4SV#Cpfvn3vHXzoPn3E zhk*|0GvAB>&>Gpm(RKt9jw8)>Vh=vx*fYJ@N+JOu1$ZVxyOaS9C@EXFw{o?i%lCyu zOW#QUx%Uc?c*u9MB0Gp9&$C5F>UGa_tknij=6vZ-y% zZ7E}upoIOZ*UX19VtTi`fDFXvMo z1xbU59L;G^YW;o70dr$Tt(VP+DrR-97raw8h zMXz|>;-P3ud`oyv;S9W^B4Syn4p&&9+&On}yQz#g0w1_n#m^wWA|I8Iw{w5dysQC! zCA!yLaXCzRqQYoEoJnZ#Xy zOFQ7g{6QCWSVO9pN?T&gfGK&LAbI3BM31Sgiqah66ry~!Rsbg^MRbtqD1kG$1Wu+4 z2K?EN68fKVY2o@A$i!620i|5FAz&{lk8~%ks>|f^j$s$@$W%2&2@q*PyW08EnXaDW zG>?!(PYouGJ|Ud8fGPl&XThJZ;3Ig9QkmlM) zWIrwf39X>&xyzUP1V(`!AP@AR5?k_Bbd+#8;~7=ir6FK3xx7vK^(iGcphUmFhnw$# z>)w~9xAlsjl_J~ib`=sqo3exDoYBl99wk4GLtCj7(9%GrtxOud7P4?p&UZGwoJLzI z)vu_*_Ch*e<4*GRBc+wUgoCN_LHv_Ny^XhwCKOc7)@U;Ue%JOy^L@{TCX5?3B(Hjqx{Pf z?N+&o>01tyMIaojh!=Y%W}NU$57bP3TV|a;q)T2!Bh%-3RLTEZBUl)Knubehs{~kr zlv3E{b|JT3R&@ad5Boya60Ch zP#MTHylZoh=_br9d7ZDRe3Aknox$`D;>a;wc3{Vx^Q!Wl3G{j^D7$$fuOAsE$Jul@ z1P!eNUxbUYZ%}4IUL8Hec?4iofszZ&6?8TQZWOpSz|R9*D1|~iJZR}JxT1*v5G;t> z74=uVqE>sis8CNjR-!vp(n?oW>BnWi6)JS>)fh!~rQb@ni7c}F>HquR|KF!c!b~do zQ|0l#KkJtrS?^&}L%|-fQ8iXiP_LxHk0&rZp9j~A+~%8sgu{#;qapu;T?`X2It-U! zS4&^og)ZrUmfBfPwa#^2rdcQWytYaVBRHZ(*Z75{j-^G?vLEfNlgN0vz0G>5x=g+t zn1kf&=#ue7Ma_3)7__r8(Mc>5wN-Mp7=1rR=F%OTsn_#COb(h16A#t20|xfw#HBEp z&B(#SmgA!GmgyjKiB7TDn)M|h~fmi2lKu!c2!<1u1HWCgVgt)~Er9e%)oNR>>At3;Y zbKhZR9%Lnthk%lBCkQj{{$(=dMlH^kys>wLu zQrAQX2~a8*M-job;M{=R6oi2mtMXQ|v#aQM>-8`4qw&Y__>N^(eyx&95=VUGFoLsD z`kW-TDsXNi9DC$$!AtV(?jKzA4yn>uuKvrjeS{45%j3?Aof(rPMweQ1YAJB`Fu714 z7(tV=My00)ml)b>LaD(laWt67;g%?_myotw&YJ{wq(GYZ?TD-3;Ug0>O8T6(r3X*W zt#nuM6vHV1d2Yk~ibP3TfO0J(%XxOAmZL0}z7fNFICArOr3EoNLo(pS`N6@(aqq12 zw%0FydO^VDmh*rYbr=UgE+G~n=|+ zF5IG=J=>C4bQY=q={X{o$bdoUlzbv@6A=i8q2|Mtr2A1VTAXtP9YHCng?CF#h2cGI z&S3jmtm-1N#1ckp=iT9XaY22;S{{%7{HOmsOS4%e;x>IW+8S`#Ml#^C9G)7)v_>32 zmbR_)kh%5^6qiQkeh#MgBR)ntYk&r)f)`GV(ReFm_p5-k-3X?%6?!2*DuYGA4uw#1L*g zS`P{;gcJCy*0o9s&yNJ1`X{}QhwoX-&41znpU|JjkhKmS&~#kkkQ{YFfU4ksl~YleWvXZQ{qy)7!4Bm}b zqw+`|(zFqz?T|pr1%-t5aqD~hb0i)Y%wzMJ$N^(hT?g0Qrcr_X2z@yG7I9y-&@QV}SEAbrVk#s1Q5^>LDtXsVH1ybhr^_Wo0tAml}b_Z(lmUFc;~K3G`r7=0f- z&L0(po#FB=lEY~nPWrT#ii-m(m`oCvuqnJ1^JzPpOH>imIaAdXp*2&9$l^BeYKxeQ zkheU zI0$%zE+655!k-_MNbluD`k16^w?7XdkNJLJSoXec!B{&z-#<9d_vM^b;e~pP{V7wE z8A@7qNliNkF@w(tRm@VYwwu;i!9{<3)W(1P5k(Pae~?YQ7vtacPnj+rTS;NYK0%Jp z$S#Gzg3zNhMycw&yv6WZQW`ZKFqzb<@bDc8-36Qo7{34Vk96Yy`A`2#idw3)OTtW{ zWlFNEnvzVFOK&UBCOQlou`rudtT_ltnwAbYxgY1w#Pn@+zL6$Hfq8Z26=Q1obG)GO z_0GR-9iQ$Olxwxb2u`P!tD;dMOwv4f)u-sIkhKxTzbW)gXTMU-c>|pBX_#sCn8mmw z{~^t_91DU}-`UDhN7};9Twzs)m4=%%a_G>OF=jPiywChf?ft3An7*Z*!dX?Eu6YR_ zC>~Nie5SwJ*;%qh$zF&DqP@`AI&?Y+9Nsyy2MW@EFH%<1n>jg0Vig!xGJu83S|MvS zYEwW6`ypivphz=jeU5ls53D z^^N)_`lKG>v8NNs>Z(rM2O3y-?&(6u&r!PN#e|G!0e|MYg z|CszS`R)h8`Acv9*R){wR7KlofJx*>wREi<6o&7ut2tfr>N=fw`;tg?HGZ#hcQs-c z?IFbgMXwi}*CXO4WTVwxs>b2Wx>D)VFFES2GDlst7r*ZI#*TZ0zI;;va#II0Iq1nF z4a93e-vqfK63aE$_8qXR**elTquA7aBy<`H&^nwD(3}=Q{pJZs0SoM!8}h9+$=S4v zFiEZf61X__6Ce@`PeTrgodAwlc*u$mJiR_=a}yeX^lx%IpZADB4PO8Ke)}1W>~Q<^ zuKM4d?5!jWsJJEr1F$E*BaVZWlmZ)3w=ZH0%IW@vC^u-6=&H}`U!K2%6}x0pcA?2<1OsocN#|0M%&{Y6OWz1OdLO`5u+fmX1D5L+hxC9_CNo!Uyt>McrD_NxA6O67$)Nd&RgliaxQ|NKin$mU9Fm21Zu6AY{1@A z3DFF*hPnSH*T5Qz#Xlmg{C44sA@e^auSwnB8Hvp@pj+u9x`wS)Wf)Ax%AJ$GwZ+&j z=*BD&aqKxV_Y4(_lz zrNc?#V{>rmA(zFriQVR^OyDs{90ZWM;}2 z!13{)aLI7BO`b=x(7~K66l0Q{0kTMB;DxLTy8^!dDMbya9ic~4bbk5b_WmLg(x#zp z16hm)qs>(^2fPyNh^DFthRuyI@ysehvI;x%+E@c6vz0Are*El{()B zkBhp5Wfz(3rt*o^p=TRiv+J#MHrZMuKT{*el6+saqG+#T11fFBSF{z^m;P4V-8`FG zcMUDH^gsi{j~3Y;ipzoJ;|o9znD7!gA~YjI_aYaFt^h93EC-ffWH=}dP7ESU$!Ul> zFoQI-XS_5HMk)If6&0RC-YpYap&lcPDzsBjYm1KOd71XaRs7|e* zdK(F#2p_G)Nwt;TFb ztI*F?vqk$6*)F6fPgKz_=lVdERXTPCx((Rdc&yL@q>b6RV{4DoOsWjX!pC$C&#N*Y za~)H5Pa@p!eU!F@dNpN1Ao*FoovZ{JtzS>2bu0(13k1&p zl3t>(r!P)(LHU#0S$-`2#0D{cF+*67_(zpepv*C;5;NbgGB{I^0Z!zQ)=iSgU~GO+ zWTAQ&c{@9lyG5BUK?`JM$!~w1)2peYtXcn>l3;Q_aAr)vnK6v}Jf4WU6yh30guNVM zbsn&KG4-=xdOKxU0%eg(Q;iMP={)T4om|c?^T*@^iZOYn5{6@#MkHQ*BYs6H3#C>K zvUgm|X-FG;H(bcyP$V;&@=5A7px8FDDByAEV5sM^P0n_b4&1W`H-RG!rj%7a0PoYde#K68RClWRtlry&PB6V@pj+=fDSW0PbLJkn9mq<<<;Vkg^Zo27oNZ)w?hEu+C2 z#(IKc&uan)D_g48eY{n=Phl9ZXkV_^e3tHQ4yfqprUA!2Y)38eOveEnS)hXm1pa%} z<#}KMbpajaKx0Sst_uY{wt$0&SfZnaDB&yUJpf#^wdGKQ`CL53VyFRp$ziCYu+cH` zLc{QVsA`rTB3SDC0I+%qt!L9}!@b_o!3i=D*Vi7ZQo|Y@>imH7P_Hg}{cro#8r5pF zt7Uyl7V>2stf_2V)&zb@g^XV#Jcy;KclFw(>1?nxoeh@e25f4+h*1v|3ClqifUi+B z5WLU8d2rh1Vkm71V!7~vth27&Py*%&E77}?b7@z zOSAsev^H3p)&@(nd4xWO*AFe1*nizlDhJ=tyE2owTvf|F>$T3)BK!|Zc%^OC35IPo;whRY|`S5XrtRZrc z!eu6ofxNN=jJTnV1oTj|L|`I;)G3}`U5NynkI<$Zg6PCF{)o}>+zoZE~EiFe(MP!}iOyRwxa;&vAfF%3FB~ zW80El=OikW%Z;)^dj`!V$~^WH=i2|fPq{CS-|>0`CS9$+n*}JvS0BtpRs8xD!-P|O4-Pe zKJ@OK+deqq&q*2Vs46o@q4kQL@I$WsV@%~xyv~=BQpfXCxj*L;R#_(t@Ml$pth}@I zo$U;581#uV2>?yTcNK;yzNNNhc}z$pL% z0?h!|Q-ahDVZ;7)j6?(dlWSs4n8;BI!9x%uEu2E?W{EDvf<0UMkdx=jL-)YZCSLWX;U{4FJon)rIZ`Kmwxwa;zs6 zgsfU&)>BrP^^_IX+|A??p0EejJtuY}A7!CQ1c!}|LNMR)A}@3uCq`+9A8CP28nNct zkaR!MTvq_5I4rF&!$V!zW{SG8a)p_cBCos8U148qIc=(Hzkqm|b>d|{MZC}8aG5nOg zSi1Ku#)&k|3?i%k)-$4S&Oy<@tEmWNW`zFo8@SQ#fyYRgdcSFX3%J6NpkPMV^(`v8 z4@H?=e_vnn)jtr_Xm&fjKOWCRNo23cT@(roi#e(3a>KBp^aI(2DU>d>gx!TpKICULnoI0|F`d~6||uLR%|=SUXsXO zWYmgyv0n16@xPTMKh2kYK+C;F^)cWBQDrhU)@%OO1|Du`s-4?_5F=q5@-Em(_m4Fu z?usWH7`e4iZ)oP$x3kL7JqEmK01&CS<7>e8jz5m6EF_TYq|$tzRGQC|%H}#gOg;~#`JSbP zIs!X}=VMw?9pHz^0We}DC?u}XK*DGOW&|`Gng<9XD~75M_8ZqISBk;1G>Nr$E0sSz zLfpMk$3_c7YA8v$dK_t!;Ju>j+A#4+;|`ZwTKr+6EIE@ zsnkIa@9QA&))>j6D3ZSE8v0fwUrHojOdj>;$)o-}dDJ(c{f>Ms2I3lU)==qXBtBf( zff2&P7TfSGfTYA@KX>e-$TqM5_hws&naA*&piV)!xKtwQ+{QdwKC-At@%3%Hv;-kcf{LS#}n!U4}X{JlsBfali- zd%bN5uuwS(>QLjzMee%p1Q0q68Ai6@C14vzMyyeiN)r@{y1tdlpG~1$A(GznMACbn zNH(uwauY8!5VJ^74}pCyGLeoA3;{5snF%liBC2LW#6Xx3LSb7T#1b}-186c0O!T2y zKEO5I^4+aS{yYlh3VC#&Cy(y)tP=`E(?T7Gwi}v`W?^FlP=EtzMBxCi0ZbER zJfv@$PN3@obv;1zbM;67ETfzl;k(#g?X5Wetoo$g_3Op*d3Lw;JiFU!Zg&GyGJ!UU zl1|;yQ0fW02I&G}5`h&GX0;^*KqW zutGAutvdN4Lis%7+j^eyZ8bN(lgP4xj%s0Fj5T<{k#G)6JJ3*DC4zv!Ol$)M$(j}7 zRCZ9r-5s_YDByG{Nq%H%3> zG@oa0o6obi&F1#DO;GPB(X<0A)_o0>5fonp+^%hE6I{1f8khOf3Fmz;%&3Gt(ka|%l}f^?dV$HNeKHH2ct_`<5$)>Jh)6ZdTfl|tT^>{FXzsHl1FIC6i61S%xCgwyXx zk#2!ZHDv@U`4^Q}g8XYF6PYBx*7$*0GG}@`=>z99ux249TBzF9kl~;8D1vwjR{YZ31AusSgeK>k@p2AEbP~?Ixm<=AFq0s`-i8cO@1Xdh3VB!qPOuRdK*t- zbH8XTV9k3P61X)a8xcMvU|6FVGZeW&qKO31tQZ_WjxGF`X(w9b`1qG2upx$tQ<_BJ zN^UfX+M3z{;3OK(&o+r}?IgMzPolf=BsO;@VsS-AqT7_LcW!x58dOiK{a7DFJBRB$hCMzSbnJSEXpWz*&3U#qk;u z=XQ5Iy%{XDsp{*c^mkN^87tD?ZOV=(Xq763>SoZoyeEkJkn1TuetV-Sh5mQ`p!Yi(~xDaSiI$Sz-Gl>_FVs%p7@l&I&&`F5FO6%|~P z>XO-ik*wOqOryGh3;o3t-kxjG_vNmkcI8SlLZz?5eR20ZM!s(yUT^V>y^605#@0cFi53Jw{&@B)oy*?>NT_#-&4l}klwXW zs`8YV(cJsdfBbSCXvy98*VkVBCAyo$Zt%mM^`nRV%5dhlGbcxRS=PvODz#dvk^qB1 ze81V6rZv&Y^xC2=*+T7EP`#a8(NinVgB4FThyTaHjRa0pqar^uo^;Cfg{}MLVAr5W#J|qRxmX@fXpD>6Zw1kSC&$XJy-5lN?%k;>+(U( zCrPRKBq=pF+DijMz(X3c5B5C7nZUvuY5<9X*A>6T5wezIj{uWQ;15CjHgyB3pH2dk z*Gk;LFG;Cx+Dq>KXG*D2u7UmAkpyDomq(CWK8grpP_9+P0OAhKlQU`VFQ`?Ei=BA+7irRBT0>j9pBc%Bbu z${ic1YEvRVqc_N!My8%qjAEns9_r?d(RTurKHf{CqxRcqT&FxBxR(+33*V8A)#6ij zvJzq%(AtIzC4cu*4Dhws{XDx{w11rb-1>NNoCEfkRmcE}XH2YY0%2UFm7mj=ntA#_ zR6sx6pgd4Y1lO4Dab#u<}jDlCpu?ZtqOR zif?>)c<3$-;BGSbLa=VzYz|990!BhE#ypeu>QKS*j30V;0nw&{w{xKYT`G45seYI%S4~lZ!szg zBsWQ)ury{oR*Ex^!yQEX$oO7tXiM5bO#6fg42F-tqOb^j2qPilWvP*M`=exVJttxh zuq2?Mnj$YGNBp`FG>lWoX6u#cBF6F#Z%xKuUS~ZZ$T3*)(U2g@)M6nUo12#3MBgKS z%a?}ZXpomvK&}y76XWre#Wvf9(T!tS^PTH;L2QVzf&O<&t_MXqlJh+%sY=VBX_Kg8 z>;y{(Z8#DpETbz?>xjrG)VJ_=(Rt1J~XYPdv?&^X=D1p+}f`z&b&_dIOD%EGVE}yI68Gq&0Wpp)f zpgAy@XYhy?hQTuoo;TovAr}!Sq^JW#0fB%`h!U3~ zIySftou!;E!+BZCH`5~RJ_NpLl)yl>V2}bC2WdaCANZn@zPhP)^$Rl4oUUIlVcK&g zOna_`H8AfI-zL-n8*%1LxUBEm}L+VZ94vej2k`lba)js z@M9Jw%msLmzRcD3JPEUw0+fFv66V!PnBzZF!W{pZ66Q1yfTPk_AXN#KQE;dl6#n*s z-0vk3Mb?lHh&U{2K~XD@^eYrSq1Fk=hGrO3v|@MCZjN%=+t>uxHu*jqLre zWak&n_NN*9n;P_0hWyrIf3C&e`Ux#|k%e6q%i<`tNWkX~uTQ~5VeUd{(#tXC(F555 znJNVff5wS%XcF+lBSDmPieZk1OmQXxm6+)YyE}z`(1;G081M5SmfQ!=9@`=nVRzwK zXy#RD{p4PSGd2^*q$N@LckpfThg%<@rvsFylIn_OIM|@?@vUCeD)3hy>n9 zf0-in^4^jsdr3C&j#z$-Yy;Pbd~g6e0cyn!6Yv@!ISc?Gt|!DPYy}}Ow17JpqIMi8 zMYbLRjKc-qOAwSr%Jnt>m**Y%3lOQ*?h&L1bSDCT4(HrmZ#q+n0e|!GO!0I+9YRm6qdw;axF03N3dXuL;BL1XwIPlMr+K=9} z4k-DgMOBqVkfWSAqigwBehA5W=d^Rej8!BQcNg|J524k) z^c$1~rVI^y+`_2g&L5?s^+Be!zmaL~(usxM-ebk!Yl8{VMJ`Dm4UJ^yg7t}sr4}Vr z`QS%x_6@+<2SePY_`qnO715VqP;pvG+@&aejN~BW;Yy@QmK!jhLt1*cbQjEuAF(eK zd)S-SVFnO(~d)+WoRw=NM@s^7nv&1XS(Fs?=8cGj9rRGYRQin|_vskXMQ z`XITgt!%5kzqvw-__9f;oMvJ~NObq8I^Is)SW8?Kn}fatxSY^WkRfjX3oP^#pwl_F zD|A$#$7T^T#BotfWw|d{o|v7AIqgFPfXdTBqk<3cToZ7k4!|N}kX~tk*%5Yq%I4l_ z@4U|mmYjy!RzHKic=^X-dS|A-RFC-cb22OB$L<=n8Nlv_0_1KeK+S9F$X+I(xR4Ul zk+6$703LAw>~`A#wkD7ZQ8xkm8pbwgP*Kl@pr4JHAF3_@DyPL|0dic^e^CJf|7j`b z_ispmYIh)KLjiI&6dGIdi60^kShK|&Lk zpxhQ9m`Fzes&J_Q2ksr$(#@9@Ab4r~pHhInx~#0Xbnr4LF4(>s6u(|;w~1L`NM@53OH<_*PvBnrqPMvO^$(*ltX zzLBk#HYR)=w-Laj;6Y|w`~x@IXCpvbL$X!Gh9HwvjM`=z#dcnGX5%qcVr2`|$LZ{G zsPt+XNQJRB8ju}8-Ca-(QIEh~3;p;FdOs)bIQ~9*6D&_+`Q;Gn15byHSl$j0K_1@| z4e>pu9qd^Mo=w0*1X1jeOh|i48b9izZe(y2L4kpgJXz!`AyOeiVj<0+#X{cQ3zf|d z1Q~32gs8IUv7GR!GVwUhkIvAa@QEvv3$`j*A&V8p&7KX%Z0^8LWU=HbYdOey!8=?Q zJlU~$ml)&133hf zgaohRs|O=#4l=d$AjW|+rBLNuw#~18L-NmstP6+T>@-_f67QsK*yu zQ@XUcaDDOfn{>6)-HZDA2_-4WJdE7#w%88ZdEe0N1|M*J!vSjfnva9&T=8p{hZh)0 zbOu1crn`as-^EBSuOIgLEc!s<(ZWf9k=~=4c_k1kcZNL$3rS9qi~?$v-|#N@Oq<0_ zWa9!$GF|M97PU)$1m2A4o}I!=)gvsRKpvX$#)%V2K7L7uD_!E{&0Ps{AW4h|z*_^R z7HgIkJHQPFvV+-PEoB#U-@#NJHrRpSw42O_v_GhQ5_^8YL1Qd=l6iUIMDnRozJUDU z6EKeM2_s4Vc|@vKE;)s|#4g1^;t0@W@o|iq8AtQGlmku*xJuDMx539Y30m(8r_1y= zauA>a+znHT@COBHeV?mulDl0CpUkAvl*-HwfdBF7mvYGf1{pHRgKbT<$wM> zek4Kfv@Ya|y`Ez_r za{1ccf^&lKEFKv_L#AP& z)hsV#aK|+C?4G2yAjcxj;!2&HXpMv6B=hJcKtM(V2)p!@Dy+KJ;XY|SQ$DlFx5{jQ zdD-Cf4jdxb4k}X6q~04)ke(+{xt?});xj=9F7KreS$)=<7Loo@n|C;Yr>Ucqz_HTZ z_;A!Awdo$G@eRYJ_3nZnsiWoJ)w9wGjow`#F#xK=9k06UiCP)zuGGbd=1wktQ@jOJ z)d`74Ug14hzPYjMz;Hp+5~3!-0RJEG zj(vcsX~-K#6$}-W1I*L+P+^|HA!WuU63;;!sOiWlw*b6mixT@4fIUE>rEgYoz7}20 zE_9%%YzHlrhi!c>#DbZe0Y-kd++q*bm&LybtBV7G0igN*^5v7> z$HVt>Ru^}bme5n3LpXtXGsr?8`C%0#`I0LvF6#fHfoZw zgPFns!J})28Y=t(AnQQoiV__>6e#dRT^|z0kbr9nji@4h1FRIl1Om=aSlq?{!Degv za;@@q8{2MU+imO@p^R-z{zUtjU%QQgCZxGdNjEp%#^~ne;phPRm=+mPXgHpRq+C+~ zMnT8!B_dSE8_0A36tCf!ZmeU&K?M@T>fF@2L=thq%QX*O(0%K8HU zk?#P{7$DrBVhBNem3XM-6arg@OFn&}$L#yp9L=Y6K_iX>=clnKx4YPO7u)V)Kc7YE z)o)|oCKjc)iACvcU{OX!3|K2a2vApnr1v>4E{XbfHuc3BM4EBF(yr zNQsQ=*FfLCMSHDsNo1cvT=^2GYD?(8w9Mp(ITx4!xfB&hsxW9gRJWeykvW$NBGvo2 zhL4b9xzvw;QZ+XzW($5}g2o9~n4GD)1uAUVlgJY&T`|mp!v2H#)dP?Tv_`*VEQHWn$>R z5xJ?uKe4<`TfO0Q+`1i6i8tW& z37+F!{@seKVbOB_DuGF)sPz$5&E5oqNd{Lvh~uHi4G9Au;I&lO$o1%HaXto>M~|we zZ^fg6-w1|t!XNClBoqj#!30Dx2_8lnjDbQHSKpZyR)^y+sgBGgrK$^4K-P;f)a|-%UN)p-kLU8E-mjeHr$LF$ioKU1Hcmx;sDmpClV46HKsF(I)cd~ zltQ4BL%*JMiO*i5lyfD&d?WgUn$$tUTK)sd0I5?WMh-wtD7rZmasHj8-7mlr2#bV9 z_*IVio!~kquctTB|*MpRy%!tA9jy&B)kI{MeN1~Q6;^!qc6zcNhU&dv@jBPwP_kFzCN1eRg3 zE%T26#!ku@m$T!4|DXSZirViV*?M|ix}St_G63<#SRoYBYX6M+W^f8wvz_vif_r5a z&|J5Uq_x#;t)_&C(zotNs!Xb4W>8Vg5uhU&9isC^J<$^7Gn4HqSrLV-D@tBX0ah0 zNkK=)bXN-f^dE6t#JAm1`o?Vctf~ml`a+V|k)ro>RQwl0TC80ajSSY}Bem#?XMhNW zpuD34rVe^u2fDYu&g4{QQUkk_Tk9n3{Hf6gv&L#Y%%vmoDm@bG-OM#lH0MOJ?&}t`(VxZ6uMfgk`v16oHxzWerIH88r+MTA)7HTn8~-5Kh{Gh#Y|0fxyiN zE_vj8OOgq*XPKw{*^+5)CG%O5sVTv1AfKAHseEeMrt+z28x%ytSqF+hfk>A~{3Jnb zbP%~l;t1RIT>&2-uEepcLx$ny^NbK_?Y3}Wk0V3DDaj|cUDJ18RzB@AB>q-DpM|WC zPAW~K05_?AE5%m4j^=e}Pp!4gje1(a|DU~gS#Bgr*TnXGil~LGnSB5-?w4%!=|Yms zOm>l(Y?4*kb&efl$Hjw0B?#~!fMl^&y%?K?WMi^3-5TjgBh$>-XiN*47Fx*0WMf*) z+3O+d1*AvN_y6v3K_CJMFqzd=-R#cFB!LJI4-a>b|MLCccab+)Bb)@~9e<^m(pK5> z%X4eCxg-Z;zvmnLH4##AjqDn@g;$eNin}GJF;w(wM0OwE?Eqv>=UaYlS4fY=K}s z1`#$eD6SFJ^lxpvN^QOVI&(e>b7IzYs66l~xsmGyhL<8@4aFlDC>ayEGsAaM`8I8S zoxK41LBTTWZ?DIE({)3OtO7zF2JvXYYb;NRPmlv4{^@}pY^#)2pQ?JtDc#*fGtI~i zK!0a=B1K}%2!Wo03Kk|y9z@4=6q1D?qIa#>g)#=xV-Avgo@ZhP zgWesQZV^PcfCdD&oCj9=z|{@UF@;y-p+|9=W9pAQrL7gG_-RW238wUB%oq$Jvc7ML zZW5OUU*{!}fzCyV9&0_MmW8ySpAL7Y9Y=TRYhKy1XPQ z2Xn$x63BxE^-L4s@1;2NBF};moUsBkBZnX-1I{?nHzz*y^31GR{K#DEOvu3^buPY< zRtKW}xUK>E3;Fm@>GA7({QUd*^Y7pUw4c7}1xTy=M%-ZBr&r;`a!vR1EBa7s<~Ca8 z#=so-nJw!Yv3H~PU{|jqAESuFrMz15#&Z9&v&2)n?Uu@ zpy*Qqxe`?7Q79_)0rf~x;1?t===yBPl(@@^jHZQB7gw(f$8rH*>#M-p1ne;D@h8_# z-5>&QjT6{$>;kTYzIl!-?37A&tcF(sVBT-e0jaz^I_s?+yH6Q8+AgYT4xEkqGvOV7y2I+jp z0jWIIW|HIR?qkm+!}^PyNp_o8^`M#LJ!mF%m)SCq6m;Fhi{Sf%@)%VrDYA=*ECgwi zx&XTdu>PXI;e9&zW;VqL zd>AO}=n`P1OBGSOTnce_Jh)~LV!4`Y>I(R4quCxjKi67<-|v^?G?nfE>q-z7L}GJT z`%~R)MXIwXML0uYKsK7QsZ=)>Ne6XAGN?mkGE!g8}q_&|}SHD!>sAn24i$eo9tzT-Y>Mc+kr)*95qPmWsk02zb{q$K>~&+_^dA1=EOzY5i!-r= z7lXD9w37)+=_8~L0`Rw`D0YRJn5OIW)mQhdUdJ*0BpQ@PtHthq6BB8p}YEhd4sr)fq&{{l}d_ zwaDO4bp~y=ufq5Dux$omI7`gZkg9%KvzQ$6addY*j^AprnED4upV!T$Tm?GuF6d z6KIJ0(@gp9K0!}sBUYy(lmWl*#pIf5Ka)bqO9keD0D)K?;e4s}99cb{PTwyOR@-@v z7@dsMk(u#g9DYZKq*Yn<=MW_VI(MPugrIu`O9Y^0rMFIo9A5sUUrA;&_nwjJfqNelf2E=(_B9(3DJ3{UPe59p6#;WCbZY0I9JL!3m!o9# zb9GV=*QQ(E1Hzkcoplvwh=5_?m7;c)k!MA_C3$#0Oioe@AGA#`#vl%ve+a_ zKAp0v97Hs8oVCPO@O=wX6ayF?-Nh4xX^5wbp~j6vW{j@Zlj4(nVS?a8?VZfhaLx}) zyCS(cuBn*Z=VbXCK5pma}Kc|2)wZJs!Ca;XAj7M7$DB-8ud z^}SXuk6&raOGVCR2|QuAJ{~EI`pR0Nxy6H@RMsEHEu^rp`i2eH)y`1oBf*hnwG;>1 z(fs@0|6?ZO)OUjZrl&VaM^`AM=10O{d0^0FC5)6|6r@Ut4I_~vf^-O;35B!ouBR%b zO@f{LBr9`F>7#DaYTbFWRIH#WccsuNWAi-Uv8#Rv^-#8m?o#xeus~F}k%!3Tq*HRF zqy$K=)Y7xeR8>LMyUAD?maaY`@mjg7%$DvfA7gqvxh`xjewdX?yR>yt<-@pP<+0LX zUV5}VEtd3ZX|RjR1$0k(JOde!rxFJ#6dM)w>hj5}O|cwAa)|E482ck>;Jv~#z${|n z$s^^+6k?QWx!1Sr5vhE;hmD^C~r@){RiNn z&g9zdoyStBR&K0^$wEoOU62y0lEahVm78a4mV=wz+U*+hyLhjBdv0`fZjUfve}l@cS~_%6y5!1 z)VCY96<~;g-w{ZF1-c7V0eu-k)ddL+Gw@syIi4E^jzEgcwjfmH7|=-2EkCSJ3e?Iw zk2@(=s+<3eCPiy@!s|RKyv~!Ndm6$|T|4o@I00TUw)Myd76zLGW`?JypvgjTAJfDO zZ37@LN`9q2cu&0~^--Y`xy!*6(+Bd)upWC-n8BaHq}c2TXCYoU*Vh9o9W5Q<>ZdD? zFvSUsHb2KQ7MM`Zb_BFn*w z(~Rqs(SOwx8(#TUiXz%h7f`5^-Xx3b2YUJD&eu6YM6AK2YkMA#fAVi?*ka?mkC=qCOWE^sjP3PAaw8~Rp) zU!Xw(>9;^fH58(wsBw)w!>KXBx(@J2kNFBHwz0}90ELSJdH@Lu$v^=G(M*`OLid8$ zB4WoNYJUZ6jb+59fA;qD_1iax8_}8eL~$cZ?PS?1`U( z#WS|uLj(j9RWOJMJqqdGjrvX3mS6`MEHFD5RxqKFIxGfVCeFMG!3lRWA$oP9OQ3I-Hy_xxH+zQcdouSXn6-qU`4`0DNOqH;D} zF@o7e0d*~zeusm@M1@bo@jYi0&JVr-jA59ZOrsvbJC2D_sHYvxrjwi}=H$g}lw9G5 zuNIRliqQfJ%m1EG@bhXGjuHN!#{=2jP|GlQllKfwMjB-_>9e`2Xl4dl@i=B;M0_QZ z%;+1k_!!1fL}n#pkb2rGxXHwusyq`>QjY40aYh#9OO=LUGRy-Nl9x^L!;wryFF-&9 zq9vSu3{sRs&|_V3dgIqqg$t1f!|Ih!SjB|7ew2V1A@Te4F+=!cz7e)s=~P^NPPfcf zG;TOeOB)y5q4Au+*gpq}$_JXoiO4N@q=7nn}^D*b>PM#LRqT8iB7 zX;=5NDYfz%M(TG|X*llRjAkFAAQh~)BX74EpBV(|1 z)neI+3$KXHaqRzAan39b$)t7TwO=yCRW(o2_GZ4=zgPd|939sND zb+x)}sW~tnMYB+%uv)|1>VhqP3(t?!R!`f~acO8k z`G2EHJMpXXSNJ8^jtz1K`X)NqF4!Qa)h=wS;aYt`U$&KTH67a7HiS877y-m~LI_Y> zU~-3|D=KQi`wFI47xl9a0zW`x2Qio?{7{cjLuI)kY;eR2l;iVtQmY% zy{%UVU4woFh$ELc-6PPw3rJ`S$MzD?%bO@g^L`lmh>sz0=Q&xsg8HzDpknm_|0#&b zo5%2DS;?Co!J{#q|3KY#MljDS&j0 zbSrE58@^oO5(mv~d+JrDt@XypuX?ER9mTaO-MCjwUks2$+yuE-f;?8PSoQ?` zX9%a=a}VIgOW~M$UsRAwLgK&%He+@GzD7u#;pz*D>4eKS#&|jeOsPeA2e>xf4+0rf zsTEO`RuO~a+AaQwzg&#zNBU_z%75Z-FJ_4Qp&{w#muG2xhG8Z-H8Mqf_>iOw@f;l< z9#S?KHCh1f0wPJ}XxvZrSm|RFYh@!q7YE#SdG%U9-c1Q1t-$6>-Kac`J3c69SuRns zy%uOGV=0L|fgq|9Wh;9o3%=C7ZRpvH#rVC#doJ}We{2dVOx?N(_YmtE5gdB5cXtf} z=nTtF>`^TlLn$+X_z{oHcqBuWG&hEFZpdms>=cBBrl+5ywiR@JeK&^%YPr(O;!(u~ zy7`U*W`s}{a&(wOk8|pkX)3bDiyaWlAI`mz^C1r!){fnilWlug=TeaCT%ew?ntp%8 z4E-?Q!}c(_j@2I41-Q9--nQV)h8ET(>DBStslfg|sVC{xUVIDE1@EsU^JxdN3wI;J z(_D>-p(Lga=ku_}-8i7y>QEcj6!Cnn%*1}9ww&3LpSS+NIczS#YW3Zy` z*D~}!uC%_}iaRx}ZReo1?HshWV9;>kbC{83dJfccOi)xK*-yN3F=T6qyBZAYiLNJ! z9~x2W8X(Ak?rvQnkg7N1JI=d77LJx53Y1O)KklgP;VS0fweLRecgUs zou`$2!qXglAbm%I;hF*fvw-D1Ura=atgU~uimuiBrt=VVEwy;TE!H5md4;E?V67*> zv&JO6d9R!w7~B^KSg0=~#I%Wg<2E-$*&;12SmE4Y916@=7&q56)smv-lnw&^Va zY>qk_(nqQ`P3g--Q>Mh0qr>T6?UqMJRpd1ekn|dONTi!sj7~IL@*f&J$I7Xt54lsi z15MYH$dGIb*Ed1E=y)otEO+x_GU_pLU_a|sMTqiG89p&Q$ZHJa7~i6P?0yno!~8{! zDoYTR?nb3E$2=?wjEoAqqD^i0K-QOIn5bi@P|L1a z8i<8x;olN~K+GT!+uS)?3+nN!^)N4opN1S=ml&Q5OG_h+KyyQHNES>&homn`Aceff zRu!c-pLvq|q$ny}EP08QgU*ZRMU6u$DOL|ud5j$0fG?j=sD%FA`vP7u22mX>hwb_c*HCZ-bDbqx#-I$rg>!L5Hyb3HFXy$SDS9Wgff zybW*gE$~_hcTBmgbxNS95o2}o$HAPF$^dB|iDDDgSfdRqMDAOetg}azaj3Ss+iQxb z6x~w07sW|pyh#A)pa7YmVqK07F;a_g9#nCFS)6ZZ6jEXsQW2>LRU~P|%FkGXBnt-( zwMikmm3yI##i5#U`JmBjaJMs5DH14Yz#M>D+^OVak`=R3iH#+osXvNShy(*ZYg>tY zwk+GHQ-}+{tWKz^*r9{t1@D0`hrdDJt`-2Esr5kdJw*MUcY5s<&!F-XU3Tv!m-rIh zK!2D)R-ykJD)=QcqQ8r4J}X}_kJ@VSmTFrP4+A`v;HDnbvgwth^Vfy zpoRhH6bot^xHcWJO3TXuoWj0RbrvhJn_3+&>CxSW_qQP*wI_eP5qk#*){E(NTTw4c zD>fHtc*di}eDdox*YvFe($~$-OK%;TzTxQZG&hC=Pbc*S$~qwWga}WJAdVCHkuO3Z zAgnZUZ6NBQXJdiZMxdZC6uu6`gPRzZUn}fDF`(|+_9w3FaKjWT-$2wm)F){F;D1o4 zLg&rIPGavP{G0|@{lV~JczOQyoBc-2{o%pU@bdI{_%)GhHyv?)dbocCV(Kr?4i zT4_%!ZS6`60Nu0N=-q;CmYN%Ev(((~)E2rR?g|Ie>_%uI_7+ zh;r%UT6|JxNgS&^KB-vp8sn3y!R7FzQbAC%5A5nOf71)nT93I<9UQ&soSoL)w=d40 zt(3u?bG4O*?{dWn)mpC*ci3qT2Y`FvinXM3HSG+3ljxNPRF37ZwCrvq%YE3ncvs?)X()Hc)4=yYxD|T8O=J%CvZfZHKRM3Y$;a>#ur*%RZR%@wF+=mUSsBOQ*Fg zrQyiVZz2%2Q(6**DQ;*(NEaE)7|fxt=i=0as!$A}Asg>04<#u!d@fSUg1CeaP8|{ zD97HiY+&{A*_|Ks7DWTl_692sY=y)I^^KQ=i4n(E41z6}6w*@H3GgQ%5N{9%w&R%= zD76fXaT*$7op9KLf4!Z0$i{S`RVPmRu|H^QOH}vjA(Rp&aZuBVL0OvXtdJoYYi&z??Ss}C zbZR};gZ>CTEQ~GF8s>JC%!`!(+UD<9l(2MTjq(8_*bTkKfT8ABiubjzPT!~^)o<@t zv!?(pPS$j9;5dU@qDSQ8q&-9UP<1t8PbIrV7WmJI;T@6uIRhCRt}FFKI)iyk7+F&JHR6zzhb-vo^d zs!l8_HE~0bsT(2zxT~gP1KNJiaUQ!6#;Cm=DZ++AK$7WATo`bniH$t4!HyW9eAw#h zkT=$&bo0{BzdpY>Ji$x!@Z^*n;!Pl&HQUz5qi?p16QP30F6OG=X0ti=1LmszfVtY; zkc&)YFp}7aP*84y(b9%cSBm-SISD05Owb)hUTP#LHzzqbGfrT)W8T`J!h!<8azX@= zBLmf6=F?pLq33G%iHh;cC#q|-sV+O#1175VfQj1O5cRxR7Z5Cgva%;UQ1(Jx9MW=; z?EsH$`EHC_LMw<;7^M-h`$Fb60yh>hc>*Gvn)Qk5z%cZmCh8A5QHx;V7W--^8^C

>&fDgOn|_tiTK0*!Dr!<^ZehhE!UBi3_NAgp3&I zr&p(|ZBTstX}bQ<(>0H1Zvpr=e1Kh~3EVtcjR#Ct;{lVkJ1W98J!ETXJGEmyOhw`c zV1a;DU_%%dvOplb1`nZ)WKl#^$QC4a5Za^9VZ}P8Q=6=op&P4G_rb@+?Ti)S#UH_B zRVCS&O$UdaVBe%&>@jp*Mq_>N@@%P`nI+Q89rn_Kp&5y8!ILAHmdpmQ0_y5 z#C@8jKXzNS!(8A+7$QNwCPEI!vdH{IzzU`*>{K86f1tYt;}aB(QR!+q zF?gWTSfs)*od{Kmgnmqe+FV6ONVlHm>QBQ^ZD%*My$4KG?*S9FyDCQzdN4IHPfa{> z?54s&U`hw;p$S(Xf)~gz0_X%@A?&aQGL6s*_hA`?x&cliJ6TRd8PKRlt@d_7ReMuz z*{ZW){{$!MW7?N%=9l|``Q<)fesvc>H8Dqg3m9;l1c0C^ZJ!)Bw6C?9&=?M5Q&o3Vwdft{B;nVzjq{;PwjFA0+jF8aR4X$(o zsR+a+Q=wx*IVm2hXQw_SbX^}WRvS!=P>KM8ifuK;HW^`?Q6w3lmG1#w2q*cJC=n#9->srYf;9|m zHlg1PXAOEp`$C9Y0$nbHNR%*Z(OE8dBrReBkLITmLPDY}R@l|@YWAW50wve69Vdmj zF=*b8h!RW69XDNIP_S^Z{_;xkCH^!+EUTvQiSRmbj7s z{BHmp(G+gvxz?o3Bp=x*oa7jcoj)-Ir8%5@G<2�z^|c>13@HI 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�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%0As|=bOd6SBtQ>mbn#T3q9(t7l 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 z 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

iAaroS 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

Q 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

o#5)ar74}K@Q0<#D<(yC(FdYsafS$oqy1eyN zwWS-kXRLMTw1qULE1aKUf+oB3ZbDE^4{pm-+f>B5NY)!fzOP3Zr|)Lh;q|0WPpk4a zf38lD=`v@g#R`IE$?I@;cQ=TZ_i*{eFvC(2AzR$bbLc3!d&OQndS6kQQBV+WEU%d_DS|ZBWwksroWMRkxx^8*lF}gkvRerQ<&ieC=_ZIYb!f z`!Q5-T(A#00PPrX_*emSq0*sYdzKG)cN77=X=foNL6sdo4szMe)#Q2i6IDHGJe-IR zM9BEZO6(EV)iwga&O<^Qxu%o&!>*ix&(BWw51CZ+;PuJ*(WO$02O%Ik;}2dR?Vnxl zpPpVG>_hCmBcb5o{>45@dV5gf3fzASk&9K$kJJ5wZ|K6ibNVmUsgXhO7w?YFz%=;! z^`&~CO+nIjudb$0siVFiEmg4 z$9O^Pxq+WV5ZuZ@e;wHd;^NrD%^*X%RkXS@d!G4uC-bnJ8u2)pA1Ct{o6MU5qo`H3 zpp4fa2H|L)d?2vO?I44`0XXU zVN9!XF5Zj7QI;*)K@;-9Lfjj87}x?*=9v|t|7+qJSpko}o6za&T4G2CIiBETO&Jj(BEMW{dDQ=ewm0J`dOP~GGGaRZ$>|!MKc0^LHkX4`dB`ch~uQ*@g z9tx{`cb&3F78kx^-%K*XNtFm_aKoISuW+13(LMiNyn?rs1GJO?K^RRX#U5X&9SI}x zUG5+6$XC%(b4H#5M0Vhx61NOI#mqqh-|T|@8su@1SO-SIWWoYSB-6F4JyT`JRVvv- z-(5r2Nzn%3O+O>|1YS6rli!S;sz}f0&%>|O=4dn!u#6U8QFYioCc0TS+(&t!NL568 z@mO(-+Dbqs0u4^4QfGO+#0X_DP`~BlPK$hnN+O%WP{J;d335g|8lze6R^ueG@FpTH zIJ7b8%&|WXzG+evg3EGrBY)wPcF;_Xm*g#9AzsZ77PgL0Im`0&mA@tmR%|+ys_8E!sjg(y6&*&ct;q((J zKIK@@JdcF&z%CCSiM-TGg>!E*Un&Z4J(J^Nr5b78B!2U3dAraxCyt_X84$`n2kvg- zKEK~R5BTH-ij+pt4(61ZYv)Z1@va(NDL4DdqEFN$TuQ$3ji^f` zlBKqT8zM)ZA>2dqfknSC>LXiD^oCoE3x%3kq=;UykDT#wd8-s7w*f7;*+=}65>H3* zyu~xTa=EKp_?(QIS1@XRwI_S=Zax8b)n44mOWdKHRxq6wBm?dJ^oJEGGP(Hk8@f1% zo1g~`_D>@|?;n=)f@*5$4sAb6qAUPx8s1zSCgJB9A{8If<|zmPAXnyE!O-%;Dsmd4F@a-mC(Z0^ z;&O26{2NJtx|6<>xQMR_qSEwz=$k51h-s_rSUAcUM{Wkkl|l{&ZkE zu3^Q9wt&$ihVDZ~!VkXX22KL%V6YJk10%3q%Ljw70hSibv&fCBtSo_Lc!=K&Kkr<& z>bxwEbNO*Df7!X*%9h}}51q^IL+7%)MR3&szqez!;6$)6W!)H_T;!`k^3XDnzKwm> z1tVl;0m2SFQBYGRt{)>XlOXXeO6;b&Y<}LkY?dh)ALsJpT>h+cc{5M^59#go{Y==f zl88`mWvck5tE&QX=6YQpn>nePT(!^p->M?3zFOut0yWQ7e|(QaB{{0Z$uJJJe`zF; z)GK1tW*6-&nYZSwk-JKPI&(H5av3DcSYf%vm(Kj8loFbz5r8hLlyJ(PVvZRO&6a$5G$3~MvYmdN{CV(BIs>&VA%n)cv zfrsYlmvecic3+BMRO<+z0He!ZPdfS+gZr)ixiqlpD>Nif?i6_^;*3{VQuugc!AO!j zZ3&(zMqwueUtW9cV=SL#aQE zhk;F+H3;?FP~}D{s2u=6NMHodcDxkm1>XnRIY=tOH)Yxxj9;(L%>Xeb4`Q7^ zi73I22~OG=*{nD)yQq2spkfk3TG0n_l4Um7waSF8aIiPE+(T`_wOEy)Z6sJ*j|6M$ zkA=~!MVmtKr#pJG24#36xv4~QjC*? zfSsk0;DYeVTJ=1d4<89Q;rMm!r9ZfluzUV?|MdLT$wk|cFt;8O=GH@^JC6t8#6}E- zEo%rRHX>(9Z5!0I_@Cz{F^I|lLCio29s9&mi{mkP0X-)H)X+8{8(Q^XxgPWzR&&VB zPjyBQ|8Ryx2kcA2*=Mw!5q2Wyxox_%igW?Kdf0Y+$}cw^pALxBoif|*FI3S@Olb?Y zEXcNcjlk;Y|NFoCV%SKgppaM`G%$Ybmp+t}h+v{}3H>KCL_jIFM@0CPkV8A`)BH`f zatA6@%*xy8lrm4m9@j)0vnha3I;D^3Z$XYv!C6k=?yFcJr=E{l_fo|jg|b#&DapB% z9fnXPN3Z#$po)~#vWw~LW8Y=fraQ*e%yuN9O2bj4x+InWv$FgXwOvV(Lb&26&4`XM zit23~65Eb8UvCAn!5PSvm1hpC%9^<9(=U>C>um^ozu ztq5Jiq-`Z6IpT!ERs0{ZGQPtL`TVts&s1;7b*&4lpPv?F! z2i60)-&PirwWMzicXyA10KghVwgbT)a7ZQ~GtVp&e#r<#BQPDnOalH9@L$@#L7Jaf zsKHzUh`R}cG>n}ZH@#&eJYvQ zlgRa&Jg03=&SsBhv#ahC_6;1ZCZJWo+@6ilUGHyjVn=rsyGP3nwLQcjB(UYSqa~Hl zPtzXq4KM?$%=9S(PXFa}dL_Oz$qm&B=kyZ!wz(+iQ0aY6XUA9fEw}%~@>Qm2kJ51G zi>CvnMFX&$q8X`*0^Nys;zm(AY0!>#0thvL5JW(9TD%^Ffy0VC8;x_a@Kh2=x!qCg zE3AXF@%>KjOT%tb%BzNlWW;jiJ@Ij|Q-eNX4)5+H8EnfF^(FW{%fk{Y>o@rj>aZZU zJlnb)Kd1X=5Vw9I&$KPeXn>6iv)O?gT$R&o=&uWQwanE^b6udT{h{tkY#`%Ip&Npn zBdotk;M?G7ijxT8tT4kW4#Fr9`#A=5*Pz*)Af=GBDuzbpL63LnRHa{r7P$<(t!Vo; zodBycV{Go53CISc&~?C7?z`ABAk603F<>z+^iRC5F|LN-R@W?n#@JL5lUma^KPFLV z8mz4@tKmZi>+m6ib=X~}$PTT<^xV(_`#CaYi9Xayf;c9HKxE2+!pwGk2;D%M2sZ{0 zNQvw$4ibaTt8O)^#(^{r2(drwV5J1P;Bl~i85}_nZ0lZKJM8R-jMeTouoWa=yS0e{ zKFBf;%=~}?MV=QyM$!cZvJ=}Ny0gH13p*L?{!l(QViVl(P-BPy;;Rzix)@%o0!`+_ z6)ssC$MAkvWA#TeFt;(UVRaxPi9hZrLU#u}!`U5ip}?>zOzbr`tuWael?Jbw+4)g=;T=(FshIJK5YhUo51dsUqQVv*iIEIblZjx*%#f15 zzx>*r+*d>VH|Zp&>0_6O_(Pn5qv-zbnB3;4KRrA?KX|==39^#?n52}tqK$7wZjlECucxdM8Y;3&EHGtcSIhjg`{UdE{Jljh+=I-=~nz=5v-;YO5L5LS!c7{kE0F_ zBYMYx_|PZ~A^e_1tEbCovC||jstc~hXSKd6>)cm7RpLLJP%aje*-|-rtcWlsMPMo8 ziQwVfQj$=h%jJmum_R$E=ebZlI1icc@>)bpi_t;pS9Ben2Np_^T)mpI6qN+eu^8aA z7Je8}PJu`w;7QN~F~g&zUWuZ021o*ey;KLzprJy_qZ>;08d#CzC8V*6>fq#YH=}4i zJZ3Y=G835w`BgKKt=3~kV@ie?d}{aMc+v$2k*I~jep1Fro2qqVjg~j6Zz4I`kgYaF>#7q@c z+%Ee2Shzm_$ehfoX|WP?!)PI30vePbIR}vd0}P(wWrvr*X_-(A^;iDT`+Sy2xdjcW zayaWQ>hr3m=&8`}ID;42N`mQND2i}eieKZMI?oetSjev28|6e0S1(2ytvQOCM_#x8 zqRcxLj&A1U)8OqvMlF+DE3QtdbBV~CTPVB&mV|*t1F^^HMH*uU@`a zDwcl(cNy)wiq}lC=6Dlch*}J{U|T^xeN(`_Xi8@;kqj8m^OB~M)@QjrxQY0|;lLbJ z7a)`p%ETm0e*w^&9txiS$ZsB;<;Gj8u^xZW} zVbkoHN*gM>rS|?%yYZOTo&H5nel-_@vKuw3NqecRUk=uj<+R*{4LY6!2|227v z-yHqTlai;4Y%=n66?WO4dc`i&H}1~vcGXZu7-DxX8c(A=NtWA_;s!U#)7(Ay*WZoa zk3Q)IcPL=K_X^+slxO8D*p{?JG1N^>%VXJ{h-O(LSvZNM_}5Fae~3vAm@49N***~BK zuf*Ao6>9*%8t&K4D>uIv5FuXvw|mkud-UKg9{F3Uz`I**MY8QC-F8K!a} z$xA7lV`QYI@_ko4JZ9xJd&FjovH(@=Q-8)?vCmzz+@|gzS~N7AFu$=_S;?HCQ=srd zj~M=|u!DEuXd!czBdH-9$Kop&GQ?8l5F4hkQ~HHV(e%o^w(=LLKGn6MI^OWq;Q>9812nnvrOh5z~0n_ubr=;>66qmyhafwVkYu@ocv-bz{KX zcC%YYig}(!qcvgujZ!1dO8972GI0Mrtq6aySNW+i^53dM;;iw4u5tEUI%^!EYoF^L zWU`?_53af_2x4He;PG^01JNcs@Bl9hOdAm=*U$VReENl|(E55-4><}tHgqK)(NElGWoFG_{ z;-=9ax)Pr|LGFN2O7)7^3)yz2Rh6B5ERn5nShPP24y<6ZQ5+H>cHile?2o((cwrAG zPufz1mZzk)O&KGle+fl4nJRR4pbQGB`gjzP^BD>Qi|LiHSX4<$@Rk|LeQE)RT+3-D z=|Zjf5@=MFE)iqlzL^#&6a23{TY?Uve_l3Q4b6Ae56^*^CPb``7Uc? z&{j^45TX&<4d=U~$*y7)+R<4sQQE7B2{3C&(QBpD2Tdq+r^$)J2GZq%QDe5t=h5bk z=zxca_6^CQkjLgZ#^%nkqO@t*sMbE*B5|$f~On-%vB%)JxU{%u)06w;my|7(4cepiq@ep7( zY)jC^@`ghQa(f_ajDV9jkR$9yvC%a|w*T(p)zRAv3fvxD;tmAMKvMX?tXrZ8%*&{! z;N}-TLrN}weRBjF$IIh41zn>2^6l~A(TV&(bt-5LOFuCYV@ufY9e5(2AD_HAx;Q&N z;C~&Q9UW2=?Y}^&7jR^F@-Dmxi0x9 zb@#<9l9i;R=eMKxtQ`eGPcXZZ8XHy+XxHE?f70a7XsGtUvR51$U{FOw^ZO9-hzXsV zmlO?Rm-#jHMepG{B!)`;a>|t&vqToq{bR;+$QO_bC;ywB7}xpl;&NuzfgHUdg~1U& zx)nVxrAi3kk{q%pM1wK^i4N;h&KL37qKZnSa!hKgu}T{d$2B=fM8KMguP)W?+4-wetz=}Hk2#-2(J9fZ^DFK?(kO}3o`{Z1ck!q*Bq8Rrwd#s7TBVY zjv}sz6g1{`3AE1qTCV8e(D7F{3L!B|B_?(J75aq|JxHya0~ZUlFJr|x5;HvuW^drhWEGOcyND=l}l?KzPR~; z?u?;v096Llx6;@EJ(=Mn4IAleNVUt5oSy>vZo=jW{WysM=e6RPjHCp(`8f2fXjo0# zw|vtW60*Hw6w%V$Y8273-fk2XYkSEk%8U%De~v*kXc>~roy0JaU4nC)VPvke>QHa& zown2(%i6YgtZjS8+9HFVlH|dh5FjbuOz@Si1EqC3yqZaZ`~{zcHtYbls_TZH388f) zC}eJCQZFU7ECtI#_c&Yi&RUZd8#5ulho8T9+WKbOuGzL{I(H1Z0jW!g<>MsbQ3n|T z=yq*57Ka8nISmKSMNBQn9mcV50)(GIv~vi#CCX<_QnS`Erf;sKk9=CkR4t|lbhBl3 z$@sQ+gaA=O0ELjT$W)Fzw$PH(!otoPwJ{?biq{m?AS+N+MWNCuMV2+4`8(O%AT`b& zLBlC3MXinK(g;hqpgt7Nw$y4I64H{%xeBnV@*E&;1aO(ZVNq&q-6XxEj{r82#sTO5 ztM6qN{g41K65@ea1*g3BlvXvON22YiUM2w|DM3A2dgw^yy^;y0XuoENJ_%QTFYjAn z%J18-D(_(oy*pOG-sB#G2T&8l+a$b5M$)!6*kK;#X*9rAE_cmj{Tg%T1?G%l?a4sz zU{lqe+0vM*KfMmGpl5V}S*V#;rE)2U0@}Cp(hJ(!Z7NILCEvYfuW7iu^G*XMr5Hi1p*gg`<8BUzmWd?943a6+)a7BA(UcFK39=Kd`OO$fA?4*rlxo#80pjsk&a~ zBjT3fzsO-TLs*w7%L_g@`>Vk-b$)tu0JjY^x{f|8Yph6e9lv~wS6)8fzqp{hvy;<{ z<2T2@${$rXiK%QM(`CJZNx+c7juy7a$@Wm{x?i5GI9_BD+5^)69jZ zK1IHA3>r)5DP%DSbZrp%#3nIYR41x!3=y6Q9w+MKMEyNX)I4PKC8n#n)pRwtny%f6 zBr}TG`tYt~Ljbt~H;XbG^CHRMoAmKxWCRu{Xgzo^lh89V;*&Ii76Da9;Dv5lovy|( z2;hi$oUV`4_4hMfbzF7332Stju*O!0R%5F}tI_??nz%O53322gOc46;enObccc60; zLe>PQb7SbbMixWYI))`|4CBZOfXhIv!KWi?%@8&n%d=Nl`#xpDI?M)aK2F%j3HwD3 zt=;D8R)O{4D%LmqR_&8A?C+Z*0p|#s| z-RjV4Y;|Zgx*u9iD>WP#zZMb@aM*?F0Z98I%1Vld4#apfc!{y6hBo3gn4qB%x>kgZ zmW2O6j#~9G*`8|ze&9V$*T?DlhjVDPI(eC_tsIYy89NDGisM9Sqj? zBxDazgYSb78ftKmfWusX+?H!2g6biBgJ2H)PX-*qNac_1&`zx?f*GbVlJ$q?<3xR& zsDCJj)~?gl+-ka-TTR#Qht|}yLZr((k?ms-g(n!H*2K3mKQXYWCXn(3tT2W;ePRQf zWI~t;a3>E8^dMt`NkHjwx;{?VALF5Qvn>7H^qM8h+yE_@-jGsWp-SIy zv6k*IWKnvfT@lL&b}1jMG2KBGSWS62QG(!L2~0`nk9gESE^=*j7C)-wHLQ7n@9#&< zZa-zJD}H7FjdRiK?K4{`NY;~ZO!))^)X(n_Rw{XAN)mu~L|J(>xuRsro&NbPF`MEq zr$~TUUICSkyvY2VmqC%Vj)vCD$1ySX^fVr zwg6|vLSKU4~FUd;=Nb|vu+l1}g<7iql2l}fC_iZcsYts~;KCK~Q3 zr<%&EiHCMcI`Mhi5yLUdHsA()vEr{>i~!W{^>+KGGo3hl4UI=BWfae*q9%#t+67gS z)eP_rA>|Se=rPWGjgaqb$YAvlYMwH>)TugR$ z%N?^zOuJBqNBTj$C@H&PR7@ay0QTPQWRx*Cks|Oq0%g1qCSQ~qU#=Yr=WWQc-;LCj z%|&gU*HJ7H#NMT5bcfuIhUZ$9$%=d1IVHVKSr%TW6pMW%doSiYTP0kyk%O4t8nxnv zrS;PE)`%51KhT|5(jvVaifbb*1`Tt>%-{wzL$FBtnGKy{lZ*_2K`jRf84e^Up-1c* zUIelrIQ7$-ka^(XKI1d8gXB9!$?%B+*kLBvK#Ty|2*heM#NRUCNKD(wvZTTA$t@OG z04HaMm;3Jyk56)bi3577ZaH<^#4L z8$*P45`=zY^hpw7Xpw#F8p!&Ck(#jcDu4ubXw=6IIg{PbJ8t|s&(t6JxEXG%L)SVC zhTD#t?)q~!5axiDr(hdGTmf0RQ8={yIE9}i!x1ZjYZDxV@M{{Olln1Gb4H4eifL^U zFYKp|n=az$!5{6oS#SA+S#~gk*#gsy4oqXYy*5vweEvYcyGfL|<;lnhevTdi3zRRsTu)F-~s_`e5`CtY{=#;~uTzb9?)=qHH7ICZ~Z?*(6quTAWV{ z`g#_-nQQhPeIv<#h)_jdw~>;qQq98$3lgm-1uE8$(y9Xw;DGrOD!OMfjQ6DayNl^= zF-|sHs;y7A+N-A{sPLtI^WPCU~{GAHgr`C+zl(VGwIB=bR?v_(ov;NY{7 znQadtZ)|{+&x<|V1GmGnawT#$Y++xm$9_5_1w0-AIfJ`qpMZc68WvfU_yUg zj`P2cx!(WwFMszRt2H224D3w&pxjfr{h$6rU-p<{tII3ae^S~N??wp*C%$BQ3pzFS zfQ9UFdD>R=-~IRfgX<|V%(3&1dDr4n!-;QhPR#TeHMyQ;TXFx>|N3A4^FP4X#Xhv% zY#OsB+7oc_L1HZT7BUG7@$aWxxu3Mw?jQf}{<#?W?VbXjY z5&4a{3U7&1P^>^!w)y}v8j@M9)CNKdkl+$`EPHwBZB&@P8v`7M=UkBhE<}sq7Qd3d zz&!VvmbDA0c#<20f)mL968KzWQgXr)^Yk9nRUYs>Wh526=>* zv=?R{2Xk^}vf)WpmcFgY@=OX3^OJNW8WQA)<~zubODzdEXhvh4eh}s$O&>@qV^0gE z3YrAe0HD@MNzkjO-j&F(%Jp9LJ9iLZ!BAq#LK#n@eZdAgH=|hTBOaMh2V&t({sb|T zRn;-Jw7ej96IBhTusP|{Rr`{wDTcYn^ho%C87 zPU*Zp?zEy?ZPh5+nR=z&>Ru}_u|Yy&<#LI4F&`!vQ2E}7eX`jyCo>0}WIXNi$F$Z^ zyGg$Gu~u9BwbZ6C!b$IN{O?sIe_pAqTxw@l9P6^0*?wqeA0Xw>-lT3sHDISe)?hN!k9NqfL-8YMk19#WPf1w~DiSo5`rj1S?sP5YT`#%0PZeY8Z|aC6Q@E zPa`195|MZF6XNV;H&GK^^Vt!cNALq(?4N&gUZP=c>Wk0mJ*u-HZ_AA?;}c|)YsaT$ zZ9hKo%+})*&vXYv1CRxUEiim0;SiZbhK)m}O)0#{uLmv!uq~t>xgfBC>(T~p+Coww z9FxFEN62HS;8K7OH#bO*jrh}Vlga~k1SFHT|>UqxH zwT-o+Vs?wD7#CYV&avH&2=O(afwW>OSyyM_{1&?$Z1>X|m!7T*geJoU%#$&xV=_5looRhb?V1JFXMwIRiHC8F9@ioDEl1AWG+e1a zoFcohpLW4_4J-5(&(UeK)Hcy_1h=meDT9Sb-oBLo8qe-;X&?<)H&?Zl`gyK6F6AW9 z8?k;and@8=*h~b7*vW-X0g4EfamlC&Id!SBV0qG^O;q0Tc|9_iciDVqAV=w}qsu8i z_}kNSmNp{;SsB=T6ZFcm8OG*p8MESht<8%{wKleHqr>!eJQZQqxwyakS4&omN8B7I zVaTUm3;8*C=EDN)QSOSJ-fcLVDO&F0 zzFCd?^lqmzcz{BwBPQDq)`a>`Oj)ZB@|Ma!}W z6c;JhXCrCQB;A#mNs`O6Glb^@9-H@G;2cdg;onqjr4qs6GH{e5bzkkcdf371fIG5H zxvzS0<nksCo!1*@o(1VERUk^k6Sa zt<1vN&JNsm^AuV%Y)>){c&Az6M}GVcNMrSjQujG{wGSbi+TH5M(%tK9-nWPXKmVoN zeRxFI&bL7}w;0R~cz})iIt|9$??FhXsaJk)S$#S5P8XURpRxfC;6nS0y#k=O7CN(L z&Rbi|d25R~-#so00(}!8Dj#5z0J>eEcuNu=r_>0#&w$PNLqKP6?z3GR*}FzEwC%(; z!RHucHe@VnOvs*XxI=42tKxCaKhF6d*PL%bGX1Wmy}8A-H@BGf-6QSL=kU7{Qh3^MMRyCxY1*5=TB|v|i_K(y4 z$2RSolb0So@z*>5mhW*I8Qh16!4GcD8HCu;`cr`5;BSiGiz9ITJ*!GDP`LHloPvDW z@>-mNROo>eTJ;R_kK!j#@8%FF!wu+g59Hx^9wYQUX@YuisJ>i$3B!?2!f33H;*`#E zjSL$0wl%yjRK7ueQR(e`iK?J4O-5JOi*g~!=;nr9JIH#%3&wO^BRXa|WS|V_cvc1k z{1ox={yrfS`EQhrl+G7&*ygAkJ2R#;t*mm3^XoMoCFy||OA^JyM`WX`<&3_Te%((< z0Bp50TZZd?w!w(4i(cS&IWb8cf7%$z~e2TAJ>xx^P!GnhMq&et(_mn@Q zI&?Z5-#3)8dX}%nG&icnc^#sdtx|Dq88Yi9;$>T&%%;b>XFd29Kx7b<3Q}m1C1zqp zo->TVjFUj-0LpMloO(E$M}`M2ZAfByprSKz5cdI!iwT)lRgE*P)#&9~{M-8AP`5Z%V;;BMN!@jv^NY0pj8Ce+V82U;rgr--KK_1 z8S=n>TJ8Jlx!N78`|5LX{;F;3W;B~k$-i9RM9W36*Lcz|e{e3mCi<>-DaTr7a{=IF zyj@KevIzl&x9GvoKAPpuXzgbf0W>90Hky;R8IWO+tCh^zB8rt#Je9?6xkkOb-tfnd zms};3MbQ}9)~_j78KI)|<1GP(2pV)dMUT=}*#Azzqs0gA^t4-E+^{#4qmfLA%mk?-!8erIc7Q2@b9i{Y%BE zPdc`JSOAmSYY92UW@qqbaXKfw?-LbVbp(o&8b((arAsV0DG78XK#=1bj^BEG&C&+H6 z2kZDPUEH6QSCgM#(KQK%C@fdqnh6HZrbJ<4E81+)a6!#@e&1c=Q)piXFY(|S2gCXY zx5&#gL(joR5h2kHs<)wS+a{$}_<<7-hfre$G7Hk5U`=6KpL{|6Gjt6pl!}C%Hf^bL;Ndve-9LP$!q3wGK5>4Bk4JD_*^Vjc=E^uyt zRs8L8|M1Q6+Xe^1H^(3!c@FXsJE7u^&f7cya^l5KdU5>r<@wI~?&^HZua*N` z^LrrA2S;!BpT9o3JUl+9s+Yz+0FgMF(VKDG0G4o`^r8?PNjE+Jel}Fab#?m;s^A1}-CT2!=s5 zksn4Rv1HekdA)TL*jzh-Z6<{IW17H^6Yq0RJgar$IS-w9&O;|&clunM{2-SGhCUyW zxHNR4I5IGxZA>d5M-3>)+eknQuwABrGuz}608PCgq%loX$EZy_g!+d5HWP1iPMle% zyMCN_f3Oqp6Heps_=S_{DCEQBU^y8fH!MH@Bder1lW)4S=JDpW-FzSnb_NgfIVTYv z34RXeq$d~}PggSjiJ9pr2VCy-FUWCQ{Lp{$hO*nPQk?3LOhteE_Ba2E%%O8Z3!Wl# z1|O94MT2|R1eo3Q;RADMu3%_?`@j5e{la@)s^xT6@?ooLzz3jkf+e=AMXK4ZGN9kU zD})oR2&@yDjrp>O+X}E(9Bu#YU;h4o@BcUaM!4f=Y4(?Y`s?L%@#p$*g<==WL6q)= z^VetB-@ng}Z|>v{s>X9*<6`PP`@8(K};&`uoT)D-oA9p&N5=c_M=_c=o ziS&a!fX_!R8nV>hr}uNpnoJX1yP2fqCy3`y<@m32{>Z67{tI@`GdOrTk+U9F#zccY z`|svyPG=x~6QE=npoga8%v5r^+zOo-lBb9TZMs*0jWgi-0CVP3J&HRglrkq-@lX*o zA*Xifw!)!3Z#JAzX&(OeH~*v^CL+;}s*XoDBRc#~Aa9TFp=>%;s0vz)vJ7l%$cIFz z0=^jZ^^lqcyx=&fyNMUGhN$1I`2O@8+{M9!gTz! z4~t|s8UZ(acHkEO055Eb?z%PhZK+)eohbxZ%x;Oc@U#J`zz{+5DX z?H}yF-nIHe(~@7PTTa!g**|dlmLVnfpA_LUbQotR4$cQh16nHj%9ukFRa)pAjw_S| zpWN;BeYs^A!Aolvy{@_nX`nZgHWrW5A}8KJ@xfkmZYwV)6fNb&m8K2q_VwvY!5hy@ zge_n1zI%K8-Tv7j-Tq>TfvI|Ihvr^1B4=;mkBtFxhEGcRhdiNzb*4&XBU+V;s@?RT ze0_SP-dJyd{R9JvhhzU@IvrO=M?RfirW4{3gk!aTFdp4TQ{urnfWr(w?-WC;{~QD9 znmkt0OO)wcd)>hdPT*Zkqy-IGo#|bE>7jDest&^<^&Qq3kVfqEkwLlqNZGaV$@x=S z;Hq0+Q&(}yZ9Xi*#k2Mmsk##r5TaO*+VAO=7Ng3Sj!w>-J})1+HRGZ>tm}qGo;h56 z3oYsNjwhp2%3j$UorcIgJ)I`?9tH_QUm5A;itN*xCTy7T7!LZy?i>TQ|25tKm+0tr zp?iQDda-6UJi&tSlp_=!+j)^r2b~(b<;J|hx99Y!H@a|6@@}z{k9PUdda=KAt}>F= z`@q)}+SxUZc4OzX{@h*uUP7$HTHR$dz;3FW93%^~-HlnwpHgZUm-=eY>|q=4O|AUQ`RW-8!J&CUgFh%=vZ)L6^t7 z%VGh<3l=%YwmiiBL(2q+!1G3nJm{Wa?IN`wXb8jh zVkboMKA54L*gyue8E5X$F_H+l7)1La<_L284BB%p1g9eW6%4ibPkkPk(8UNo_dFoc z<;QvOBc2CFJKL%AX*+`RX*+_m1(h1GLzYW0BOC0vAQLd0%mq+83kiJ$PjG6ICXD0z zZk!DRioJpsFZPF!kiqs5*Jc6~Vhqpu>{J8Z<4pLm%!KtW-Xt9?E|YNb9<^RvQ&3|L zkJPUfZEM@r8pz7yIJ?u%*lO5(xzUw-juL-yR`=V|+5H!1efpR_y%VR@y(@i$mBV=) z5y#EYxIO z__G)1XY7n|)-xfInoY?8No=OT7xQm)z~0eRe5x_>1ZVZdX|GZxa(9VZfrc*S3E9X? z9$gX2X*m%xt|GWrBEo9{ORJCHJ)k$i-4o7=cis@U6w{uGpK7k|Cy$1HL{r*wlO@$Q zV$2uLzTn@AG!IpttzY7-5~nSb-%6=?k8)k4+tlsVh`faNKMu;+7c&QUpR1(zz0#oX zfR}1}cuUAsYcY+esTyn5-QC?lF;`Jp@zn_4x(R(Y;-5c@Z<4>fobP_bm`9EPot%X< z;8NrSy5N0^VIoKx+$T3BbM2LX@#{QfJ2o$Fgh*5!?f>t>ggPb%T8Xg$Vy{dwg z=>~?o0u6W8g@OG|VPLy%)(#V!bgn?#Z(7939ynk#3IQL14l}eJjE2ozWn8uFt&KZs zcMJgaw%1A+wRLYBTlaSNQ%sl{peq~N5Ddc!ei$PaAVNGHti%r3mxfTkhU6c*&_qMR zGJ@hCV(ft2K&%tV`$Ya+O$0zvuKyW(o2ZI+4ev+a+pSU;!M43^2HW|tDf(6n#Z{^6k z9^x_F)Z5lBr40OddV4b(VQFRIfQftx+(Gf%s*r%S-Ym7+&C=!US-%$PfIC_F+3VKo zKFl6CKu3uFQQUgvsf&|~Q}rk)jz>ryAj+Hm3jpF5&6tAXqXxq!{wB}5c(ODr6>q|dQe=;%mFK*)mE| z4n-*wa#3i3@&)fFasDF3Ln>5V_$o9V+m-ZIhRnmJM8loD)3V5uB>fcgb7l#GW2NNn8&ewU65>XEAj za`ljT9NvzYif%EbRZNbY#s0AFuBL1;qb&;?mravtG+TCzlvoIfts5k6tKu{JfH)-x z;K@~$oKW&pE;d8O(87%DsJ<$# zVC-W~MZ#WCcGY68$+f{E$cy=%2t%g#ToR=)F~!_mh{8F?n%Dfvy`($Uiai&qML0`n zk0S2wYvK6I>wzK|HB?E>!XKEuAIq5_D;@`Y?9GUH(qDxtsf|7!jFS`EO{S_L zRE_dHcFiHaU{Rd~rNG^!H|fv%Co{@%2gHJ^s8Zjj?|;^Ng@SeA5@`{1-jv)l zQ{=aW0&C#>t~sRw(hHZS6(i~zNbsl5J(==-}0#aN&l@#Wh%yb=AA^j zy*o{DSbt+Z6Onv1CI1waUFv8mq9>KfJ5*KmCn)GEl^cn@=e{-o3JFkta5A0W_ta1r zbl%%v3J7SvX-C)hZS4s<*FArSOwNSH@t(BuF1PaTw`%3+;1}vvR4bMFOA$G`057@fbt%Peg^YssQ_un9m&Cf#Y%8b^vwomEC>#;1hkZVU>1wVs}Qe z0eGnc_!%tZeS`ZM)L@nwyUEaR3dTGvD6-9=zAbyod1&7|5AFNzDI7^OM8cwLq{9%d z8^;Wg2a%B)l>?P2^naMaOJ^fr1A>J}mWuq;A5y@}AXX!fASjhcobSUH+^X;0O?_|F zQ#gKh)|v!qK!#o+b_{D!z+yjqE}H44PG=56sX3N_6oqQ6yQ1S<4N%lh>3K zchK2Xm4aU#rr#BVvH3$rb0w2Qv>Ym@LwTwY0v^f9N1RzI{)-lRakPK&?(FDXko~Iq zy9dt?%AkF&8r}@o8thmU#Q84N;*%-&0Kv5JSMCxnh=G?X;Q+@s!U601M@n9&6#vzf z7Az}2gZVwWNx2vqO`xp6?PY;7D(V^$Z)VR>qGXf%J3f0Af$>}+3Xe`T^}2i~I`Os4 z4CMx-67xW|iWL(pI5D8;sITI}pXPd?T-M30!nc?5Cxiw>c(Xw)o)2}UKgE-hAAU>3 z8Jt$sXCs(LI{ty&k@5Y4tbP7h9Mhp>-s_#79e=xj@JnWfugo|(IWx;X{8@^SU-K#cjJ{WM~hd>2o0K~BL9j*QD2>jR!PfRPfQyQN-LK; ztE}cL<6do@?;^6=@^I^6=xd5c!z<7S-TRnSsq|^676*Sn-OWbAgqFXeJ(_I{Hfc9G z9l6EZ1aX{o+~RGU%cZl9Wqi$3-IJTmfZ~;Yn1L)DzQYuxQZ8cHz6S|S!-SSMQl(8q zy$oQ$K!i5L@dK+t%l`x5+HRc#)`RAN^`JS>{e)mU7)mBihd$ir zh6RG{3?6il??$ofhlYu`KNRAT&S_&B_-P2idOO3Ba)@If4uUn(KZ6K*?cw7b_z};6 z-wpS4>ohPQG!4uLO@r@><8{2W0L4M1i; zHa!R}gAgNfi5k-n>O2>=5g5VaH285(gWoL|Gquumaf<^AtU6f8s-5st;WlW#UUM1@ z*=u68ISum5ZB7FMPwe*JP3Yt~$6Eq))KOx5>J_4M-fdbbN zx!mPEO6TH=NKgaeq^gH#Y~vEiO4oFqHGw2zN1VXUAr)A>Q-a{ic||HlgaT53FQXAo0ctZhRJVSzC^L^4FJErfW1nwPh55ffIw_n^53};4U zBx5>n-*6eH;r$IA>(qPk&(jd~!k-H~%^AX{@K$K~CcjBBQpVU2-cK=DK zZO>^;AVXWJlhs<4ba}@(@eA*$(28Wb%cXmnX_Ml}wZ4U?Y7rv5nDY(@iG{Qe@@v`0 zLe3@$wyLW;bbwCP@Y2Wg%A*G;7o%`Dyc5qGwHz&5kP6h&U0B0LH_ID3!-J_imWIg{ z$h8_aeS!jr_n$bsK71EAGs;&zEwzx6L04!UeS)nu6ySLOuK$`&_cVwUs9%j1dXbtp zv_@T{ds2?@mwf=*HKCP|;j>cynO)`CAzBF>M$^K*qn|k>A@+k@h#gP-Dg{3R>$9_G zV4+<`bLBokZ=H)XU{?{Fi&$XDWFqV!$fdGD5wXy3IxV)2{(eL}CA<9-iutmT`rFa~ zcn!ck;q;ZFS*bmrbC=z-6X%9#SZYmql%Wj9su5Z1mB=L)nG#CoZZttG98fS-7zjDg zywcG;qVI@mg^5JRnYacN`m`$1IO6BtsFnnz0qz(U=)(pvS>%p3?jvpr@O`jG zp<)^?eTW*Yv|i0?U!r#xe_()D04-#>PgzJ1enjQYdt35QDsE%?+6lOQ(j@oUi9=3F zL2B=1sSc%4U8+K53slb0TgF__jRVFp_fQ23jquwyJ z+ytb92x0rs0y1nXv!T0#_#hGMmi-TotB6h4DhR@AMP=yzlqLIU+NHWcAd{u&1R?W=4Dw@txv5Jn!@P zD3JheBGQ39k8DecgV6OIC00CeP82C4!GxbF+BUl{k}n?|>Wd^pc;)#WGInX#CWE2VRp43dbG8vina8=RTVS_@ z@m>It4FzqUkZ$4qc63W@I-F)=dshJ6BdYc!{`gBys)&~sW%<&gO6bwTgvZHJ@3q*N zycRp&WOw4&Gf@mF+zPUi(<~aYF-Wx{OH2JtHz(%^&iUyYm3H!GcaZ}XY(i-cjii9t z8e5gD2b`DX(QZk3hPFEfL6WHRNEj4C|9UPdi(S)Bt!#Q@ z?}d}W*X?M3b!%eG>hdyHTZ5t2W*xrLCV63P7UHWM-1!9@K;i`qBsQq=Ro(YPP-dH^ z0n|TUv>K@K@q4U+5&eSwy+UXpgJ*($$n^Ak^E4dno$phR$G8wynJ?MpeD;n}xm=ZA0c$04Y3zd3q$aQw}%X@DR80&lb$?}Klw%{`;fMD31a zL@BC*6qEtB=`gfW?H3T^I|kf_R&)T1QBhsR3F$)pAVDG%bVJ%p(-RhK3ywwKxEZ zcI=prd?4W%Lx7QeC2(cz&DHV3d-GitMyPF{-B#lK;?Fd`uuIijr z_&f`Z?CcEikBm2y1C;ah654M#IH3PW*}yk%j!=b*+r_^qz&@Ez?~u_nhl8Itd`H&o zUUWH0ux!SB3B-!m!U^K=Sxo%rr2aKyuL1Wrl(-p9+bbrA#N(E}%7|(Ph$@G0L(-EO#3-wF1%o=Z$6#455f+aXM&4Ag zX3OrKU1o&YG5}181H?_&d^OBxj{R`+xMzmv?H(~lbCJc9FGX^1 z<-ZZ9j(`2je=B<6MiwknGlEVy1GG{*I71#n2Z$%axe~oc-w7scipP>G+B)B5H;5W9 z!DpO<4J9~HJe#DO&nJl}>dLu}xm)t7E>0upA%<$P7Oe}{m2#1#*#ZzE?jWSc8fWv7 zo(ET>@3Lvh9klau4ZnDF#N(Or@DE<#sb9#c7=lbYj3`&aPJDGTidjSRH%y25Z}*4uNdTPSWD+e1-73cz~3S zUQSODA0WbVCXM1a#jpS6zfRXSO&`%p-eq!x=?hv%v={91@QOPZ@dD`{BY(%)7S5be zvW~THsYEZv9npHq!UV!$Pu**o+8Ml{`boUbuOm4CBAb}xAs|KzKVv_e(n=K=F~N2D zmGpdEIz2^D^T}C8BRwcbdKZHlV*lfZPqz#T+jC%Hit*GKGR^0jv*|!?a|VA4?7kCQO=pfgl0xk8<=W>%{AwV@P7I z<~gRcS$OjwSPPQFRg`v-P3(w{;`zF**Ip!x2yf5}A9p;b(2wj5G`Sf0cO&{QnkHe( z0JCQx|7qxF>Z@n+66pr^yy1j>W=~HyQu*5nru*(8zZS7*i$A_%EWdc$@t4P>s6eyo za*Y5THWHHH9EpM;V)DAJ~Qj6O-d$6GH=(MFZ@9&k)f?saJQAVM}A;M`dkRwHiQ;R_0M)(y$Q*Ocg09JymFp!I@ zP(GaP=y^ObL2(FnRJp@@i{i^cj*R$ipR$oOu~W`G+0ncJaWnN-)8GZ|QWrv|M9vnv z0><#w(VN4;;k*6QZ%?Qrd9M=-`-UD@MtPsnvuE>9-iEY+#4Ct9V3xg=_)2nJP-m2% zIqR*$!~Xnm|NZ$P4S#91KTCJX=V_k{*#ds?rwfqx?ecl_O}>SXLqg9FyFt z3_}j(T;NqN$Oh9crHgS%H9!2>4rU9xE!(m-B~iroOGy!w^u3 z&N`x(1Gxw{Y#QfJ`z`HP@`^;mBl&yMS1sF(nYA&-BLjtzP}K}=bk`AfdrxL!GHwgD zzUH!;MVMRCR4&+e+5Hh)0B(=jB?Rq6!8&}3Z3&0c0c{zF*09Xm4(7g$N}tbU z>4E}@um|#gB)+?d1F8Pjhy*bxvA^qRER;TgpA>Kg1_ zdIa(w2n^fk4Gfj3K(L;z0a1mSLU72Y>iGI&jMX}2H(~!4LI(`5U4lSH zG+ocGC7zcRiY5uJ8IJQW3SwgU^A&`aL{p+#eG*Nf496ht>4dBu0sUr>CBOK6cF|U( z#05L)hQ7+LbOxg*he8x?Nw|hE17TYa1;dh$t6sAu$ZTCWW3iGyW%rvb3inBM#gn9P zw~zy!5jSLZ_=<*R1jD27a6Uu=$(70uD#_aw4&*0E4_Z_^Y0MvaB=RB2dy|YGp8|QQ z7e}DefPvyG$sHwmxIpqGVYhiSBG-82Xc)jg>uE>^DdqubUQswMyS2PmiHDj@9#~

%E%ls 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�juL^N{HbA|F$4j7oPONlm14Ovkn(hy{9S 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ⅅ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

CXgoWIcN1JcP6T2XH}F7!809Ti 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

BqQ4<_+EV4h7C3=nNG2Pe1n!r7iO|5 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

Y2|6JFSU%^ZRUcWIfw)>C~ z(tXGX*&(~88zp@lrjUl-Lw;)y)NFQ`At}QLk#S@@u^(AhYJpAN2_iuBV8MZAy$3f; zn7YVkubrxTU2hr2>3$K6)34iWe+DC@X$|ooGD7@^jF9a!c02%kk@F5*Z<;_!7}+@_ zx9)>a&4y+kSc-e(pGgusMjlTJ?rQ*m(=f!J0CooM^10g&oyQ#^UOfT+=QTn$2pMH} zQvf*yIWhZWUOYyqVr6_CwuWa_ULkK0TR3e-;vmETxmyZr&q9twKI3*~+mAXa6bgKnyh zs_os4KjM!0?JguW>q0HHi`$xcCzszN`Sn0Ma{t|EU%Pn=6K~~{JBC|Um$5S9vYZF& zZi<;W+oiy|yLwmF4BTc9%le15UX#HvURJfTz0ti>S<5$fv4$kIO+`EuYtTEtq2j<(X3O^J%iTJJ7Q zrXR!aC=T!vLpr8k&sDvUQV0IzFZT8TJ~j$hL~)V@gqYh@<*|h|t+Kd_5?=Nhh*o+* zDyr~=z9Hn!2XnpmfjOL4M6z23*(a|uQeTqZIEaaEB+8kr6Rair4ektehZGPrg9*x6 zG#1p@Wox5UT&1+W%7+O}CTKnP-j$uV*u5%DH^~HNs%SXz-N?Vy)1r_N#iz;8VfNnD zn0+bg=|V@BryR6?OY#({^Ub&r#}g7cqb}G7gPi>N4PA!ou9pa?uWH-?4vO@+plC@74k#geC{?b+bkb{R^|l+)xU7qJr-8IIoq z8C3mF1L6hA%T0keCY}{jOur?~U@4?3q}C!pu%$$fAegj_@Y!YXT;3X3Qz}+bAD;4bwM4!`w*eO*8AQt)xg1%PZ?w zG*(4|v1li;ir83~=b*KjWM-^#H%4inmE**Wh)Mx$m|yG6Q-e(h9$Q{>4)}&VXTVUi zV2UW-PTTet8$zl-XrcOD1ED|6@9)ni(#DGN9aoD2{P>^>#=+>gmHXPQV%`-U9W(Kv za&ytRxmT0p8;xuRdlbux#Hm;`6(NoZosHT@+WvT_R1IeIBrD&0J!VP_m)K93yyN*) zRzj=@GJ$T#w}2B)xcnMCoDQU7@8MkkIDMMqi#Tv)c}#`Ih;1NmmxXn2@4Kne?U;3S zzM5eq0oWO$9G~1MRJrIUOp2}AW&(B1vKNwYKZB&4e^JnL` z3lsU*@*xWDI>BGE=|nSKytJi@B@DRKlv4=s-n(qbG$f=w%R0e)CgNa)A!sTI=E-Dm zt1~&$o0L*h=Rb8o=V^MMXT6)Qpd_YLw#vc8gugpZwkI#&U0ghEnIlAsb$|?@{21eC z#?mL39^i;xLUcV6;H$J>@ZiCMy|fMSWz*Bm)R)B*O|E7OS-7xgvD`!(4jN1@-E`1c zD6Ju_cTF&NwRwjW+%@ss#%H!aY-CWS0?%AGbA6EI;ap*bu;7yz+Cx5kKVbNvJQe&t zLEyoG9D;-g^d3+I$)G}%c58=?o(G+WM@C+##a;CPMJ_2CJs+ox$g`3N&LBuhK)|mb zcLSx2*+{$pg;0kTvC{@LQbWx27|0?Ndv*vXQd{$R#Y`zWvQyT-bMh7yL>(?(!|`5`1Ip%P#3q6|0R)pVD7%xm4<}`W%y&pD z1wOz`wb>FvR?_z$eYSYN%$7gP*%HcZ`EWg+^#`AuTw0FzfLY@`VAgDp^Nj;P_M!5F z2xA0qRsh=Mt`GN>2N9QEKZ;#&vHGb^YKXp_x!{g){0zjrm}nN_6JdSUgcQYl+*z~4 z^7a#*HCtm~f0;+yc@1%Pjc(1C@OV=C3+q>w*|+Q}pU5)%RbWGdcFYNVFDpM2YK{D%7})BXB21E6-Cgwf|Gc`VM^a5 z4%?D%#na?Mk|`>9Ag+edTs>uZ_P#ib#OolQA!d?~NBhjF%$mO{!_jDO5By5z*BQ^4dP`59j*3NG)j|eaM^dGE%GYe`;_Pg$Nj556J|-bB|GA!@ znY)2`?X+-&EhKk$Bx5N8O);E;2jtdJzn-#(aJ0}BrxhBf;5nWk+A|oc??w{F@JKSB z9gyxo9(p&)z`x`OGbgCFOc zSxK&u>nPFLz!EW9=vvl}t2Xk&_<=VOE~d^xHe%Vf7)04nt`@Wt7$rB^I&q`5q`qI4 z>c!JK4(qyL@~r;ipj_iNx&`F1g$K6q2Gk$gF(|q8_{w->jb`pPy1$Kv9o#LCH<8O( zop2+6Z`ifA@h60huy84$(afuFT_14!8VhgL?OPjn_)m%eC9%|Y8bv-h^}*%kQ3=EI zvThc6a0)Tf?vqaIR-dX^W0#H$ZqLggE(C3eZs25)aS6g2Rx)tIrTij$-6-V{UPEry+qc4f*Zx|g^ec?B z4I`q7*BIo`JBp}K&aYO`Uz3x8ZhzJ3D+qb=+<%rmvOOb(l--I%x|I*35kXMws z6is*3@EX96I{S{jR*KCDnoeOT^sN?JPo|)Sc8C7e04@o?D$%2Rh|~|6n3_E2by|6ZX;|{J7&T21H=$rh`FV^o^On3=H9K)u60SIjdgH0VWFf@WF7O$w zJ?JnpNx&e(8!oW^5@LpS$Arf$vOQ$N!k}kKh9DDYbq*O1d(9|3i(%{$TucHF$q28A zOf?m0k}xDDwJSrfCO-EOx+5B09FQ>tM*6>NE%d-*Q>$%W?QuPGl@%_!nJh{?iwPKT z#aAj|f^A2;eDB2|72r$JzBi=zqb6R&C~ef|IW_NI#)@Hw*QzRES0V9XW8Kl52bt2i z{}<@iOdDZgTuAhJl1ZMX@1IG8hIL=Ww#Q#B+HAu%?KS9JtEML+gzVl zYDYd3j1;0W@wGeylaMbJmY@4vWrmeq^|ZPs44;n7E^t^GRhG)ws6Se`KIGVI+#bb2 zs-Pdw59WRBTlu4MLf6MCgoXU^8^XElOF(hjMD3C}ZliV{$yd z$2yxJsV~dquzgcJvHHO5ZcO*`Hy@VW2c=y!Ss827E}9?Oo@%+P-&rY&nd_rx~>2{Z-h`V2ux}|1Q z#rQu6_W?pirEqAc0-_P6>uLi1l)%Vf$d38GWa1ZFG#xxXZGy?z25ISR_X=-*tm(ET1h|a13_SwE;`sFH$P$D?F&PYO3uihjPYA2X4x&Jz#~O!LGa|NeiSEA#fT}flm9nDnTNcji2XX_f(urg)4K(0)WAX&xP;4^HGtDhVo&k7v zg3UIUBL)v~faddf%EK5>k36PipA&)m<4VS`4y6&v>!}plV}0(4!f^#^p9)T%Z4pdzwoG0 zPuC0uC73U&x^N>7&S25J64Jzp;WjAq8uStQ{)MNCmNMMKAF}zJMvS zsfObyt$m*K5kcvdE?u}qh_17;1ylqe2fLb4p--?m$XtD29kJJ7vrNcii93z%yy(bl z)09V%n9<|BxIg~=e_?@BDAA#`8`9gJoPF}7^YKTQabPk|mhZ*+lnxH_Vsk>vUxYR3 z56@q;E{>0|c>N&Xq9^S6q%Wv@rBt3)N78Gwq#&Cc#!l-F`0a%O^gjQl zrI}oNv|X9XclsUQ4<<7*|DH_-pGfEl5(Jjd%02o1?Cn#4!ISF|B8^w~)i2};t!#79 z@sj_wa5Bj^=7ThEJ2Nk%d_|be5yBl7IDW&@adSJL8KK(hNEa5+m>!ANxxlt>BVn3_ zqybW;q@M+aE!S|eol6SPh>1pHtOzd!2QR@?85JdpKM!VC-&N3vWTF#wJ-8JnSajN_ z3};(H1Uf?TfzP=S+I?}mDPhRepvQIei8O3ZJ}Nl>eC1(YgnY9)9H>x0YFH(X*9JV} zIh?21wgio(fE>oo$3@IvWen z|5mB5&0;!XqR*!&F>YELIqTchr| zJ42Rao?T5tV+K!keG(^!rh;j5&&vZ~;0s86o6D5=2{pMS&l6yw%%xy>p6Ol#LJQ1A z0jwBj6(&T{v&s#vsl=v1*SHThGrC-GR&W^bC7PRIw)UL}UGfulzWWQ?=!WRJx%Z&7 zakhxA*B{z3y1w-II)6ewVBfqsYG#B1uznLj-`Xk_SPEK=o3z$3t#4>Y)%BU7tx z-}+b(kdq^UmX@0Vh3MExKNeEdBm(Np3gV~-4SNuD_@0fFUn>N61Blt6dK3Yh*t2T9 zE>_TkUQ_*CV6QzFSZmJ(h2dK{7dSv=B8(X#-8UvY6MulSwF{~^HwNQKx`r3^`11X` z^KT2LwL16O3R1NqiuI*%B)8bh6 zZnMbVZ5G+vlM5p_3iW6o^haUhVGbc_J_9r*@F^aR*nVcYF^F%#I}ARPK6n9R_)wr7 z>j28)M^0@Pxk1niy+@oy_0#1qv*^!y7Hy7+-;EJ91xB*llYu`ItDp>sue@CISU3a? zSnCvld;3m<`rM}r$Pg?`%@r4T0+w`eyZuUvAz7#;zhl3tx%bR@i%*R43o zQDTJ-8j=8L^k37=M-`DbQ!TZmXHlI0AQUoBE(^LD8p(Hp(dAWYE#J=@T+kFXA`!VThO@gkvf*@7Z42DUwuf4(>b_+?d-D zU#vXQmN_FV2t(!8$=8m`U7bR+88ioEFr3^gb^+P{uP-mpww=ZDwmZHxH_Fa|=s*<&%vWt>xWK zrm{o2@AZaqu-Q&_8cD;QV2vMrKqr>XKX8<$X5- zO^<~vA}i|kVbeH)2N?vU;kr?ngll&Bqj!hr7qZ6+rZnH@!Jk|CvDnwk{kXBEze)(E zQPjA*``GF3K6bXJM+63x>Jxy0hEd`p;%fB(0gVFiC0cP8s5+!^x)?LSB&S{I--1Ni zff@xUHzOa)*miB~co5rt9WlHdh(K_m@&$ z-3%5Hu##GbmdNuV9oLX)GOE5y^oI?V?bz0eV4b1<_z0i3c+zj^Z5O&0p^Y(i<$|~# zd6Dw47Tv4`*m2L)S6tMSOdY13)(iMYhhwT3*Q)<*c28C)!C#K2)VjPkgeSjy4-J$b z_VoJQ9vncITi90Bd*5mDuPEDLo)J+ zlIOJ#m*@?ETsMTpp*$o>-3URe1hD-G`S92@5Z=I!5hD5B%WM!e z17fkru4^OfycgKJu8***k^9M8(&X%F!i z;&K+iSRqnDLffi$401zykJ~Z9zH2>t#{@gF+c!0IxMRocK(GR7nJ`6~Akqb+o&!TX zwc)eHXFFhD0N=CUL*iEC_lb>jGT=Y2qQ?QUlnM-q}Q-GED%YA!&RnPg74$pu(kUSat>&kL-XKIV6W=m8jj^qq7b zOD161x{UK!BX%#Q4vN_Lbzks7Tl8`qIhOvCn6uQ`mMhWbwFPUTnpOYX!GZ@O)v_PhqkU*T$^Ngwed&CqajJ3*W7 zzh~Zr4zoEbpU zq1#IWJ7jbmr*bfIK`S7z0Il|s&I`&q&~qVsb8(Yk_GjSi#E?y-^_s(XeGTUNZ}rH1 z1OWwXVMr~tlU^50I$0RG2;QWj_1eQGvNagxQM4{09cEJ2)hn54Iv5iV%FyiB+qY$DvIw`9eBb@bvaTaqid z8~`^qR?YGjZh;*0JyxPm1`3A?$a-foF6#&O##(ol;kEjH-4$m$y%me=%PdO`!V35x z?4z2F)|R9EfLhVOrOg;Sp{zKTcx)y>IH5ODEcjMZ>=y3^HEs;Q;} zf`McmsnpGj-z*qwH@Cb4fmziXa_w>3llas%PU2#&BpipLhYO% zxGOw5xcP2ha|(G#yF6rBax8B3AZxZ1B7Iqaw$Se-*6Z<;2$uR~YQF@!n!j zxhAGcQzP&9XXAZPcEdx$qc?Uguqf#Om6X=0yj}u}6-skO2CH)f2u_8j5~$0#r~&Xw zpF&4|CMnKQiWL#+^=!(E7vFLm7-sP{vq9&~jvgffcZBDPBvS=i_aVFYQ8Zh@}*>}#Jh!O8bfuws~OK`Zwk_FC2P^-D{t7a8yB|^O;ENq zPi(L38zg-r#Z1+p`;N_r^4R&yYm(YlBPHqG6kcbDuJz80lw0GaPLzzoRdRA zZ7K7a1*^is`>C8N&V`Xs=`D7%kUko-kku@EiFOYa6NZ{3rqlxbnM4gKUS#q&V313b z4~(ly$s)2z(d6Rb*}GxFmhwr^5YCE}1a;5E49PK_13n%aM{Kc61*DlOMMaOJ`KCpZ z7GhN=LfV0_^{=0Ibcw0mpU3|2_V0D@wk|YX=SWg>3N?kP z$-LrV-7|yLh{(Xwj&y*DF$_*m5FL^f1d_}vawHD1Ok8LCl9s#5zB`r9Em9(=t@Bt=`Yc1)w#rQ~%P7EPVqY@P;> zBEP$x=I*XBiRHET!#d9Q`mNgwBiN9?gky#giO7W~33OpzlK6fP`UlW9i*1UkB4THO zlpzHvm*>Foj}QeoeNyCUEIOIGZWnaY_G6P)*W`8aV#El$kjCt~i3=Q4AOAmCBO(W) zacwVdZ32(C zSM0f8iF4XogAp&jZ40pZb_HoOZd07|6#(&0@3HwH?MD1xu>FhjKdwC?YkYBGg9en8>Lpt_L}Niy@UH+~Zc25D~O{P;f-p6W;5mHsv(omYq+? zc~>74UA@IIxc<8A=eU2$A<=YWucO94Ys!8*~ zT}Nl#@Wj@;VK=mTDbJz=%HiDV_23dmUJLS{9AvjZHxc4R2&s@r3|23Y2f(3bKsUNR z9Od0b-EhxwT+3Q=i~C==8Cr|xE%2}zNbnLMV`I=B0_g|Dh6~m+9IfLxP1m6!+D&ct z6%tc02<(SwIifh`co1yCEC)L2Sh4W@lO0hZms`HL z41Mvcxq&O8FK)x0Il7br7dOo_W$W4Rj}yT+76};Qu0K41OZ=LMD{)h=_zB zdM=Oc6;*fwAPH`sq)!!t${JnYgN-^ha!$nkJXI(UU9@QAYkAqAv7*!2FF9(bq}kA!unEn4|KMoz{_lK~CTGig1@fyY*@^{(}_5r1Qg*eKLBXD*Qx4 z*);7)rSwx)f7eS8$BA+Ftr-B-?|1=y!$irTim@v++7YF`uda*)7?tUz!@#6{Bg)be zV48v3tq5Y=FngXbCt>IsD0%tj>VcE-R^NNZ76GgVjO5iSUMP9DVRz4OMzDn(&<004 zyeIGZ@5lGdP7>6T%FP?25JOucc)||GXcmeo$f~z!Le+{#S`>Wx^D78k$#bj61G&^P zqxHq)eL%`axl%U@m=-lwlT<#dX!>bc`W7vxr0E#9=-GwZoDnn@-5BCF+ge;4*us!x z=UYu&F%^1ngso6koFVQtAo}}B*177mTmOc3_85cv-`wWY1Aot$o_0Ox&z|xXf1s8b zzm~&YSjy$QZduw## zabU{B(x>ExAK(Lc-b?SX6tKf^*AbX2*ex(Z z=1O*|Plq*6=x>(8Vi1M@!Q4G{s_{abBU;p`^{y|6%(Uw5Syn)@fyB{Lo z$j$ILIX!OhMy$DUTi--k(Sy-d|z z7d_wFMi`;j=)czzn(5k>PXiz1G_Z!Ut>KaFofus>Eo|S(5@;=lt^=O745A2j5_yAC@b<;K_l-1f_2Qe(kv}Dc=uNoCts%3VU0&nt@^+tH-tMz& z2hlsvNsxe)ctH#<#4rX8VlM+@NoIM3UHjm*3Hu@9Qz`v~n0ttNGlYhxkQ?pvv!&T( zV}d>U?6QBEUBAq(e0I5wv&-FmcDcLHt{oikY2N}Q1oJTo9gyv;#nL4@Ps)HsHX3 zQ_f3xPm*XbMXu=Vjs8^f5Ny78SxtrGugTk8Twdwzo=^fr{h&_}8155-eEoEZsX%TN zGRa7oIrYBQ6Z&4{nt70UY>|-TOSG735r=)Ci6fByi$Xna>(u?@+kJIsr4GwfQxQKI zzp>De<}Xs|1zt|F;wxv@@Jd`=3v)euHb~!xfjvK&0zReRdDu!- z3PrOfFoQdcCB9MHq*bPVSRwf)3fpa)atuSz8;asP5) z^de+t&;xQmRIfYUs88{>Gok0g7w62V`E8OR3E%3BkpVgbRWTud?tMYZD(`3>!XJ#$P$veULoGOZ69MgfYF$~xm_sSXpCnlH!t_?K;OWvCsC$Q$uRwxc;BtS5d zmZs~UF==-q9&#Ma4Z5NDmOT#tv({znwDqQS{> zkw($pD~WLljV~xeUcLQpAC8QcabK)PTR0oFAa~=C*mQSI)uFA92oTmYb9?KkmU^4`@fYj`AAh}u)WL_G8pKC{| zrECrfdC-7z`|2pQpc*(UF4U|UODH~iYqa4&0^&7VGhE$IO}VuYK9l0&Y!wo60@ zwD;E}|5k%#RZV4=x)&UrnqfjLAOZH#{3a8ba`o5C4!Fc}MA8F^Sm|!~!rI zE>`)}2q3XKfFPuyI5`yMN z|7>r+Xj9mOhN^;QSFUtD(xHO;;(-Rpm0#4@MfjZ(*SNX7aXOEM7pwZ~-ibyv47AY< z1PyHF%}kQ5QZBpbJ8)}=v%JupL$>LRhM$RwGU zhVL-y!OA+GJZmisU+og1ay0?AK4)$^yyq|(jSCJBfdT1HCXv&ujpI+ohAOlO_YLT^ zz`#oJBIYEOjeO$30N$;^EgIE3WRc6=fDZDWV zg@#JTALpYVX?1do>8vbei?GyIB{kYrZ~1iVTl09`np!CPH^5(DF`INI^TMXuHS06j z1}q!wnEbV3V`|L)T6ypGzV9?eBnm1%Zid7Qpd^!S;P%5Fb+>&2XMs*c(nw&tkl2cS zXz&0@NI`+5*N>cFQFGh1=;*8GbJ`C#cM6N4V(xT7H`-4T_KLbbq;`?h0(d0c@_o-n zhNnQ)8s z9)_O_pK1ci4;Y!LYvZSSfAnnIeboMN>dAPI(I5VfD)p<{*x#W)c3`xRK%R#KKoIoV z1(uaSm>d%H=#ns`mpXQR(3teSIDvY-iyTS_(n-Q(6n2v!Sn3bU2_CyYoGxY2D!tgGjyl|%RPGSXVBPuY}IFt(VFxi$> zSbIr`DSy`@VprXSwZbb@b!wvE%cfMtoy!?BEsq6)juaH!m;|s;n7up0(OZO_6`WN_ zJC_p9%Bx_W?CAe@sRpYfDVW|6Wo zR4Jt5qw7;TMZ~M5!vy@3lWB-ncB*eKZERq031-CTN$+9t%_j?z!teQZg;=tFm!<)0 zpZx|kQAR!y)|D!%BQ0+Lb>0Ed1UMLuJnzj%;ZwgGnJ-ix!Qug!k8?dN8Q66^AAZa! z_ny(k^HK0&dHe8#&S5#vH z+l)W`m9=dNVH5|Z=#a5`2h#Yws90oS0**-Zn-SE>Qf1JYfyp;jtAB`x%_Ltr9V^Ss z2v{^a@Xf6te-wH376rsiu3 zDR#cyEaUCgS(Z)oujjZq!SDjslnr^r97rwU1~}@dlO#z8G}V=UTjYSm=r(ILyuCM> zKpqulI?%uhdpF-caCR0NNkKxw51W&D8})29>=n5_<-A=AM3e3mjg+g4Adoql79xFo z<5WT0CG!+}#8fNaJw!a44kspS);7C7YmI@;fr`|SN!I_jfAxp?c=qcRC$dY78Z%H2 z%>U}W9N+m59Pp9MPs5ia+)?n_3e^nCL9q%DORMKY6bc2p{VtTOz^wpIqa=n%6ohgkC#k8CfFH?seP<`y>}?W6 z7d`$liI!q3jJ+O?exV248dzFk$ z95T5jx@9Hx=MM6@tMDpYF)b_%g`YG7BxDh=Wz>&U`ERQj!7I{bE7KxZYj;VJ^~2P8 zPSz}T`-G`~f{;)e=4aeE<_x4^w%of2))L=5G3F{r7+cPDs!IX%whLc2=@WHpDL-m~lUr%CQQA-u!jZfx>f+f@p!{$tOJZ%!iuWstE!eY2kd+)@Fv)FcaaFXKZ}r z8|9teZoMAgh&9Ka`*t8XN<~-V8{Skq^At|_j#$6QU^;*)|M9?3nM%@=Ibx3bgDZ@7 z;l-Q`&qeKL_4#V6tHJnK|=&*}f4<4h#Evy~R*Arilh zMC1g0S>YNlP8%3}Z^yY|E!-O>kJLCaN*;8@jXcx0%mgwsm@2h-nlqa3Npqf$vA8_I zz`DeYYOtslF4a&Q;3fGn8;v@~{uzu9`U-~FXk(ct4~Ayd+QPD0e5BEyGPkXvA9pv| zM&4Eg3 z!){_&sf@7o1IgWTAi3L1#PuV$3u$RspWPl(jUb&G`p}6>oqit!DUKb76yv?X4J-@F zGpUuimWzOr59u=7kLv@8Dl?BgkZJ<2|H=l^7Jz1KC)a!@5U^G3a`oqBRko&Lo5jp!#5Fxybw8w!2RSX`^*9eln>|Sp$Kr03R%n&iBB+z%>>rCB}fR ze6^>=N23)~M+jwSh{@DVUp;^P)`DXIKdPl%RFYnA7+Ry5!cxs=kd9oH+9X?5Kp!+H z`eZYhDm{0uF*eO5YY}!Ra_8H@n?$-A+}0C z`ZOjs*np03Z)qIhev#BsOteDIfmEGdm)|S}sq5&B-p}$L{gJO6DcZv%i)Y2d((FcJ z4fmZGmI0Gu-4?zTIBe?NprjKmb@ioI&$5yLG_7Ld-S(*CK>gUy!E zDLlQ`2g~zk3yWy=qdTVdHWadsf=oWTG_Yb-7eG?d`SbC|zngg9_g+18B~bowygm>DUKC|W3kD>!8$+)Z!nw&JlUc9l zhJN7f#?-q};lr~@`2RdBjr*bNTaW9hYH~)MzI$=<@&^^2 z`Cx>r*Z8AKfts~hQ>%SYOI`evN|XAMLYql7zlQ7%r(zSkoM&%8747Y(qP>HJwP$-l z;@Yn0AZLlwwOj|3_ZZ)>Dgpb>TtD*Cup9MkM`mLTa23)BGb`@KZfz>sHpl=Te=6Ev zWh(wMtA56_%I&y~v&s#(o>gwR^{jGtk-6?Vc4pa-CXMWnIJgYSz)l}@(3zi5%nIYR z&mGROW5-6UBXd2k2kL0sio+Ulb?AZT8oFqYNL=lU&b9Y-XH^5q^jDr1f0BU5lu3hq z1Tv_yGZ6#--&8cD=)IS)J26IgJi1P3LyIF`Bl&GZf_zQtHr{{vz2V1` zgjnTDDzZ>1WnIleDXVYZoWrMt$cNkhjxB6!7S6~{ z_i6D{5Xw(MSpM|pe)PHZg#YDlZ|R;Um*11O#~1toFF-BJpD)bs#GkjGgAVY?tLF>< z79KMGdbIHKp$UQD!l9I#T}5DFL_48Noea#rbv=Aj*W+(Q5L}ovh$m+ke^>wQ3@s}01YA7h#g`N$dOk;!~onK1xqrZU~o!lvbqe6&uY>~>a{N>Vk zR>1$qYLvfM>siw0>5I6-X!3_a(D9W=D*Vb;qWUXaSe0$}4Z6w5zX?L`iD6(bHQ}zb zYSSE~H)ZSUBJq^0sgO3Nuj%kGditO~(nW3D;7~%h5nB*R+>WW{$r6h+*1P1Tri>68tPh?YdK@-;!Fk8Tx zUa1W4-AMbEfxxvy@awZ>%*x!94O73oEfVM=->rJqLAkwb;h9iLuAcD;~!Txs6m%}t|?<9bx?_Qls>^p1XAIpZs2RyJQ5VHD#?L%uw z&#;lLkvKuO57!LjN?eGS*sML9Bao}0@MET;fr_H< z!$H}N<0uM|b%sCPKEebc;`MM-*b&6OAiG`TE(9xRXgj+xgSu-cjwccM?w@Mn{4zKG zq+T7IH##7Y{ypesNND~k%2{vJvD0|D=G%d$IjBP#;RQ$m?ulUBD&Gza$zdS?(WL#+ z$`b}FQdNcQ!+5fk7kD@VC&8G66AYUdQTZ|v@Qr*<`QA=7jpCbC85Gyq3sCl_FETLB zA82dr7s3>sErS1x$S{9ZRpjV2lMXb-!A%If zUD4PuTmvt10{Z)g<5fL+zU}GiKKdMiXWq0@F=VzlUafp``FvGB=(roxA{^%t#hu;8 zoCv4sWYaj4U}ZIIF6$oIKK&Wa_}?XV3i8M~4H~@-kW0`qANT8XaLB4*0Vl9gPP%YxmJ% z?LIoTKhr_C%!V8^dLA;gz=HyW8TVs&N8HHval%6!JV>$t=~eKcxDN2fDI)21m_-Qq z1zz~486Eb|c66*a&4`|a0Xd!$RyxIBkmH%ES+?@ha<~o564qM8r_+G=gqIkeVgy7W zr8Gv;L_EI{qvrAW2^&)CwN9QU1G#m$gi>^^l#^ zq;2wDu|Ky34LBLK6&rY$2|`eSrOa)i@0s3l0&!dKCRfpjj(vR*D~O_AoxGfy6miMS zRvj+ZdukXEruULQo<}_}8<@rdjTKsHg52CE3iNKgf5&(QLIV|eWzj^yHBK<9+Xi(x z7#fX}kJ;^PNwQ7e6ZM(4bjqwMfuvrbsPfelay!k272n{Tjahx z+A__hjm)i*ql}Dfs*N7LH~&Un&Wm$xCM4%XVBmuIpcw)ew^U$}BpwNl@ke>X`FvD~ zSt8acZ3r^9eM2yix1_M7%_T?aSFKac9II74@1{}q&9v;vk!C?j3XNvb=M5`d75Ntp zQ;~?O6e9e;#O^XIVV*WvQ>ny>QA5f>t%j30AIbR$5dmE3228bf5k|QfMYkYEK-Rf5 z28b_iTgRV;ilW^*A<3`kTa7GE%kP=bG6s>qFbn17Ax} zk%4(eugf0JlbQxAYQVEgTf^JvqcbxC_yC$w->B+z99ue%nR-5se=jmfMg_8ZV|i1a zzPC{ES7(1mCB5-P*&3^qbrN}tros`NrnvK>C_VM@9aNw3Q#iExl1b2OI5 zNwo07;*-Cr=vq}j@Og$oK43gg)7qG_Dd9YPg5e4f9%{}s?1T88y$fBJ^gY)~wI`jd zxNa`QQz~qclf|4cQFe8OBKQCUq{8Zu3?_u-OPSw|2B2MPee+wUTxutOlapNJy`7UL zt}I#GlA~`n#ukn{q+HCBybokx2wWo4F_ibfRK78h$7nRCt zB5;dq zL`qbh;F=CR$%%pbfO_1wzB{3!nc5RYOj(*)HSwVafv;b-(PMA`T?i&AG3jS zuDr*SlBmo^X){-Lz-ImCQdns4y4T8j@=dugOvEf1*Y-5b#gF)Oec$2Y3^k0XrE(uu z^64Pad-JJyQOxHtxu$Q6`Dmu&WX|m4+SJ!uwV-@G(mgJw(pu4ETz;8A!vRl-QU>4= zp&-WanP#MCCXG}6k+NWzJxdk>8k#HTFq@}H3^ttf!!*Yo+$!a|m{zILZIzUfajx@N zb^7I;qh$Sj_whAcNUhwDH2uRI7b-a$hp9`NdiXt+FGjH;e?7meZ@R|JF$IoN}7tBpI zBE3>JLw_#Z%J!Mcm7D0+++A33Z&W^U)n^E;&opvyB+;9%hD#1coK~*0ec=UPyFmb|k|a+=KJ==WK_e~_P+ zw(v&QAKDSF_Lt%AOw|s*rQVwg7iq~$vhXbVrjNUUr1A25Rj6fR(t2T*y-V0 zM7^|_U4mqV4v}Im8ia}Tm+wzr9GhDhu&(6>=ufEVMN{mJ8MDt%-kly_o}V1azh2_; z4V}Jc?gaNIR)1X9p6f73x(b7x=UQg zU%XJ*;V%otFAK$=??TbYX<_?2E)@Qb3x&Uf+OE@$$diH$TWAeJ4KxOxGld3>*X0zX z@(bda-Ch`FAXEVjbmaMI;v*NS>)YXYM|n{(P(Ko_mL zvcy}0D3;yGn&%6*Vgs;fI)&G1S`Sq*-$>j%b9Jil<-}`EbBUWt&fGOE)4|~A(tM-* z?iG4-^$ zxU^QJB>Zb1ONGtwsKd#5$Z&EVGMu(YQYP?4#z`OlXQjO~LjntQmr@tLOJJZ}9y92o zj3F`!Y^Co*aRwmEI7~t7?56#oKAhmgaKc}P)AG{vr~}D<$Uw3mGLW_hU4s7!4CH`P z0y*bHorsx-eu``kKlH*bQVYRG6F5jijC)toD7r#4CY93s$=O%mwA`F;2}BJj$m zfbL?)V{V=m^s#<{g^anb2|ODRxLq7jVD#i5*ogt(-XL;CC}Q_dGoT*P?AbgT+=q+? z_aUQUd$Y&xTbbqB3B-$fFmkeP0wX6wLw8~QAnCFXTsKlNkxb?Suxo`5loFV^*@b9| z6URvnwcE1;Z%Kjf&tNocG+Z`&HNe204nCsKz(`NQ8YKBIEYX{u>sMwNgh zvMWgi=vhUzYs*Gg8X7}IG66!8RJjIC3WSo-$*k@JC8H4{ld0)AZRvvD#PlYy zx<)pcdQN!hn~O8O{hdJU7VjhSXeb&B0;U9sN}=RTvcxTV=D28gA z;U?fwDQOtZft03x-Fvf|6g8UWvTuW;9Ywo6pRtK>3IQuOt|H=eEuG2z(sLpkx zQLWW^NV2H%X*Kw9YZteLUP;>QbJ+~RcQe~R&Gsw1))pSybp+%>du`0 zCY}GG11*!ncU$AzA++u`!VWN$Y8Z8{e+LXHrqKr#YWx6WAaq}!9{%?H;`I39cW>9Q z@oXgHAu&PCvumcakdOZ@C-C1KEc~>R`=F6|XD#!=N_*wQTJD3UN46L5a$tG_%Vojp zbF*&RcQV9Vp*awPdl6E}8K7KHx5(m@Xb(7Dk|?qR7=>VF%;4IqorR%Yy+p(6+Ful? zRr{+-e$elST?d-M7Qkv@&x$knp|im5MwZ+4{8brdjZO8_;>@rb-6^)U`?#@oA2-{l zCSp7UkV%d~q7C`x$b}=9YR!I#^Q_fN9mu_P9a8*4V!QA6L+s*R9FpP0^*!J6`?YZs z1aR&>?zr))atnX%FqpfwpR*_agqh2terUESLq|e8Gc)wIYF3$-$RJ7 zgUTcVH&F=9=wd*8Nxa)V}jE|Ctj!G;5v z1D$w?O&JLQw=s?aFl7pjybU+ij>qDPwAYD{yZd$nXDsv2o}=Jrt=Aag$e>F=_c0$W zE0~m*XS^)OmO?JuR1N76$+d*uO%?-=sl$B{iTgB|&T%xOzy-56mX6P9t6G=m{N{R% zOkf-g9RvOgCr**>zAo-kl1WYPq!d{sgs|5GF%yeAibQ&Go~qZ-WDrW$MYcfpI9Q^Y zrG2_@{*Ww>5zh}oz><^CPBI)IV07tmSY&u}y|`PIQ~!j_AR48Tn01bYg!0lxX7`>s zl##GY4n}k4R!MT5%C_*O(eVDqEYAip+Ih@9p_vT{Yn3yEO->_qc70Fg0B8%*X6rb5|&e$UH3cx)|HJMzjn|HvX-C(6r#!t+a*75zM9MDxaI{nTYVp zf)3#y|Ng%Sk7=$=C;g~-KB@qim7gXzpXSM&&EMU6^25nnB<7x-Y74yn;raOqXma^@ zm25RC8=xFkMN^n2kf#pQ--8@g#Q?ExXG|B8*XJK5cYR+SQ*64J-VKgKDl<-^;%}j~ z;YHI$@^9Xt zb?lr`6O$e~%_!byE*SH~i)Iu*(_oYg=RCKX)V9`>n<#mA(I&mp{Bs+bW8fpcn?6EME~gQp}KJCp>Ev75O;c?+@hA zm|bBcO_$hjPfspaAHYw!8X#|s9t#XwCXu%<5+w)MUE(;M>z6m2(pQww1h?V=x6)>t z5m)*(2P-%3Ee`U{k2fCU^^r#P?Aj4s|Gdc#^2b)6#of+kVqBMTm(4&cQ`Blb1}&n2 z(`Bt4^g*6&BzM{3+3ms75FYZtj{-VQ*9I4E;=8ux^t#;?P%LnDMsC!{xjREBrtbkT z?PLi=1k(h%l9A)pIDmYdxFE|{4;O`Bbd*vXuVNK2=hA}- zwo*QOfpB(11AYbGAGgz8GfcW)Fig5%Fig5T3=`K&)7Xvx$U|%~g-W?)rH-BUGVmuu zWF4~WP8MgN3hKpy%cx`Eh7hQNf5+|P#L}$~6Wa^@M;|8LpZzfL8;42YJaCKz&I1QX zust{maxds-C+xYY19CawF~M32wkP2AoCMk~LDWrMKLux00?bP{M6QKp;|@E+VcQRC zIh>x2g6y3}$Ht>|?`JnUHUyxrJIUn!b_TYV88>l6Sim-8qR%U@)y~$oxEmTdJB!OJ z&(JD1gQptMmYVw+p$y+~>tPG=M>j2Zlml3^+|{SH5aWTY4H9=m$F?BPTEn#1zNL^g+NLf?3 z;`(3q-i5cqOLQUhDjMsZGD9aCYS?^~uM26;);l1pmnj3y9S>BPiqunh*A0*k+)4Pi zR11Yu*r*jBw{I`F0fuJ4IDIFI5x)u`L=zw)WI0ivoW3AX z!6FaPxE&#l7$GjK@_+&dX_EMf)fd3{0ZH`os49IMt zZF}7`ih6$N+g%7e`1ouD7i}~c0K0aK8v-=Wa`ym}9qiUU8)Y4w>Nfmn-Q%q7p6+hl z)7`Cmwx<$-@HTuEy%^>X_=+J!+RM7Y4j>~8XH*viB8a*s{Wt;xuCfWv# zVi1MwascDhTr+*^(}fcglE;zM2i|#X%Rw9iSps5p?BYmc?nW#8O_%3~Z!Zuk`fzgo zKra!ScQ}{~|Cb{m}eQIS=tH zH>71h&7DIG&(HEeso@Z1pqi|R8l1i(W?7CJ`-Y7(ZG(T&?v(;A&%oaegjLdRJ%_6B z80_UoWF94691^iZC<_{VQ?pwI`u1+^@H zp-}DoYA(N>&oMsu|OBo{nq(l|INVAo8FN`wg|T4y^dvd5AdIW=hhcMmXGJ3_b;*u8hHVdBB$!p zs1I+)YA2qvQHbxAgj*0#dPw#M+OCJNSJbzFNVXC3gq61#?}VGL3sSlz)9(vZ zEs909vUlKUmt_%b(KNIAUfQ+bYP5VLox3RrF6`X&`x@|0cBa3kGySbQ)8D!?w`cmo z=8t{Vj*#UV`9WaO=gJ=K!Z(ZKZ;DPs>U{)*+KxQW&#=+EA-dhhwi|~`yj$o@NQMNx z;1N61qaol}Kc&v})^w(~b!U28cjoqa%r+V%NoW~GDb8X@TF!vR?Ikeln6&D3!H%+@l?@ZWqmi?%mi9?y={0nvFW`BGX1H1`u%x6BL^i$XI%t4)On9~DUyO{O5z?W#8|Wge6`OOnv!RmD5YgtekQ zDyeEkrs#AG(^bwq@FtJMsZWU;fznBCB%IQR&rcSBhKq_cNLw3F-yX0>*V#u9>i{DP z|MRq6+^1ux{Fb&+G|{Vl5&DRJ5A&7CU(nMNL->S(0V*0I33Eu1>xFUu#ii}MHlAD{ABlnUBaj9+*j5HuF_wG^r?QnpP_`;O0G@%HAu%#4SK%@izBg#6ajaC+Zri)V9PXM)+lL?zb%Pd=x1rFRUj;^Du z$gZ4~*v|{hO{j~UecL)HF5?GWxdXr%LyUI*w@9KLj8@h}ba=qp{|rZ#n_I!3=Y5I> z?VE3(=2{#w@B1^8;W@u8^GVBs4|JIA%7p(lQYTjqv#kyXE1%pkdb7HWl~EjXrZ_a~ zbUoqn$LnYukAFmBE)Cq@T=B;R*1S3izQ0+}vm4m!B|R@?G-c^Fnx68;jt@CZ%Wl;6 zls7iIuUG6*A7qbec40R?vbDuzLBtadFZ|bn)*a3;ew;axjjTxrr~)qr9HH9>i-tqb zc49-650pYl2y=-ydW)P?a4v&4*WZarDA)wb>(;Gbq;+>ZRoRNFXK%{tqN& zLE<{fR=B}-l!^Y`cgN>Hd^mjb=EK?H(YL}xyCCFTvZB5^hHLFDh-crt0VM12?Chd_ zGX|{ddhp}?N@l zmUWTL4fi}`K)e7EIFRe5ZrtzLUTr`jMKkO@>VR4XhW>dEsEs+CS7R8W@B-gL>v0H) z$RcmEexY`Jlw5hseF&8ikTNU~u4z_eFl`Wl-&g1og~x`;_?iJhwPT(sj0#rs^TVSS zb-->!UGwb4OW41}?MzpM6lyP3zw>SWDtfcqCK<9S+b$NQCPx<4U}Dhx#wfUHm5@Y) zyrgV+JJl!x-T?c*O}jYD{ou5Z^+*Zy5090aY8UC9UZ?ePz-m&b>f-<=;{ z>}j?cPJFzZbL8utb2D;D?~p<J=d9aOce}3WL*MkJ25@E5rggp9o}3#zq0` zddAd3BX?HSwglR2PLWie`=zyzBF79*efkLw3O9nOyA{1w?f9qU?q&p-eHZLg4(=|? zKGvRk+Af&AKjVcrtr3I?DSO#3J3gi29VO=Ye8*&0@@#xXMNxPQfGPW!Ww$bz5a>oq zT$$vm%u$-eIAoaS(2Pqq0$M;3bFC+s98Zhc@r;8lGmPi)IAzsEwNCiPXCx(;I2mEk zpZNrfC(~t&#F}B38CO^J))0|qHQX= zy0@Ge<>X~HAjpAC7NbnI;Y^)Hnvz?-2i2uqrQ}3Xtd%pqq9Aw;ciU`Qo`g9NwXQFi z56PF|4xC8u(LAbCsq(#Zp3iX$$4+D@-c?ff=;C{$V@dJgT-y_k#(BgSKf%h`5$Yiv zsIzzdJ`oQ`7(o~p{2cTkH7HdG__r7D-Wsu5furA0a8YtKV@y!;isj@}QfuUzj?mmx z$z;;5eD<>(Ynkf=(J3D^cx_I~9`O$I17#ABP*TZZXzsv#{;_Y{> z)mVtuC%5&41_C4APnAu~7&tsPu=Etlx?E6{{|5vJvXQzJ;j$cS zcK)Prd_F63qgg(-zboog9vGYQAh#(GnsaQ{hUYJ2dofGHYwU@$>n2a!nmgCJ<<{1_ z*(bODbGLBFtuFv)uLJmLF+1Q30}|lq^&!Yl-@JR(xk*=f=uRW%t+x(4tyfx9-Bo7} zA-~D3eR8y|bkbR!a5JMBkye`%e!^zPvfi4k$NGWfLvKx{w@0-T*GIxwA6GKa!c)``;Bjagh9s-9;>hYj@zkyjBGA5D z!J`i%>tFdG3c|)g6!vx)L}71-K@@IJVFo&I$N?l)n51Cs{{Pu~v))LuEKTfL{1sl+ z5Hh06-D&%hGc`)a9+k0Gkdawg5I|fryGDA_>CW;c7|e%31A+ud0%m~eei0y{wamll zMS>nA=wY6w|4IJ=^-s`u&b_v{xtX~coe`PU#qLT5>27vyJ9h6`zVm%ZP{0(K<#MjLo=cQG;Bb9*x*>KG#ihfuoPNNLg-SG-|J9j9wRG3!#ypOctz=TdpCR{9v^Dd$Q(mO)7G5b#hF0 z#wW|0Un!TiI8_8Y#CA8Gi>!kYUoM%)D8IVW{_1;mpWyLv*5SeTGNMa-NS^ylV=kss z^#?n-Jb;RULD@A)Z*+D+H=o1x9c^u_fbVR>qAAts{5OclSY&>gb3X$yo83FbM1~sp<;IP~b5_vf z8v+hbRzJxF0+d8u_0gIlWtiZJo_c$~v!~Q;9apQqcXOcZyOtH*6Ss;PO*v3tLl5_b5xL z4gT=^HOW@0l_=ZkTwkrqzA>}Mk`yDv0(ssX;e`%C=p9G}gPTq+ikGZ(bNFicMCR1y zzEbwDiYB78I$o+rLaGd~%>QCQv*5-#vDs4YlU9N$+cn=FvE)=Y{mV?48JdskAAsE{ zYE^Q#-Y>vRGoR>cGS-!Y#dIfaYuC7GWLTgFPQK+82}0#%xgn=?3XE|@At z-@=%YhgfLNmeG2FB;fPs&j*i?+yV(77~L^VbW6y%?%q{Mk?Z%WK>P>Tuz_psKdzvnVgB>LfS4AGM4zKg^uXgU>lnm(nyc zxZ?aPIN|g6D`O8V@e>xV5f~F9@I*}LwqMBE_cp}n<&`F3ES>@_{t=Y{ zVBNFjlsj{fh@EGBMB~8=SSgjbh|g+HP)mHC!L z?nq3N%9IKGr)qvYdGcbO-zGfs+cbo-C)wBry7_GlyW++Zug@#0KXG5qhtf zIQMrLPjKETDfl8yC~26cTw~i%E6OXcYPy3!g*hQuw4mD3V(>>n>+-f@0&Vn7?&LRS z1+6ih{$0?$%}pB8f_kKSpiB9yt!R8Z6glNkq%k6I*r9uV5Hc))+2Qvv^aZ;2q30J9 zJPJZ=XhV(Alo~^>AhE%+U?swpsSlMlFrR@50376->}S3O^qx@_^fR5?v!CT*Vp(wu zXch?Qf>ad>#F$QE2^JNV-7r z@&G8Z=I$-#)Zw%zM~@j5A?I!~LX8}+q`+j#Y{ex5*Z%Qu{&wpuII`uyoUvaCVVe?4 zAtD_$r1bk#-C!J)FINx?1&|q(6YSCs2vb8r>ab*LMHir=1}qx?a+OO;GKuWgjgip2eeDrC@T-!RXI26zL_9$WG5~Yp$u3*T>6I3g zS)vV)*`BSm1?W*UF8gaDPJH@@m(L*evJwich8xbQXC1j}iC4j3*ZjB-l-_ENGGpLX zF8%S4A7z%H7bGT9z`|ftkP?-YN}_~SX_CyYkZtB`(0*{znv_CVVNw%k!s;)m1(R@( z)GVrmY}wHC0J-=|VRZ$*i_VPcpC%FE3bQmr4m8d7`IR2*k4TGu%|CSsM#JbBBdt!U z1h!jFsiii%5G1k}hflD4;6`hjP(W4D^El76b~xF+$dJ1#FRkCYh1yb&^(=q+H2vn_ zEemMqafSoHQ-{dA5ev5>(~6HdU1yXC4*Udja4pNe?aN{`5HaUx~QUr zarJ0V^$V{D%-;We%zQ#94V!<~e#2f~QX#K2Gtg~YHBA@#-O0smvWrEwiLkWCO4&0* z-`d#}n5qp14}SOV>2q`%yulBY4&Oc01H6|EIVFSK8l)%>>A@Do#(MW-ny@nTK6+cB zCc4yc-Yf3tWuCM0M%j*+3`8XGFHPTEm9fm%kM!4Nxt*_-%<6__5!qCZ9xfHxlu=LU z-7C*8^a`N(Qkq`4OO$JS3_w)0s09pgs8jE`a13qr9`)KwarO1yQ*F1+^O+cn-i&2? zmD-Df3L}Sm#(F$Tfm+YH*@RtLEExJ*UM$oF-}Xv09I)pFWqTFZbvq2DayN!N?_H8+ zS3}w}b9;LBGH;)Fr$*++>+RabTr)sz3v)G(Y3!twH9fma8q;XxljQ*#+x52n*Hi4! z(yUj%LwA5$6aqhFi-5#l`~+$Z7J-U_BnsmsO(G8|7|TZco(1_2aK#~u=_AJiH5eo| zojBQ~U$H>b5^gcd?$fK6`{Si>FhqZ$8wEI%1j3#kbR!ZpSVgAohfoGh{jTBQnxhX1kk}4u) z!q~ur_-}CkzTLlXmG>=6Zhp2m&K!r$Z=BQDopk!TlTLSc66$Z33<5KCQWvipm>mMh z=75#Y0Ak&A%_wyPP;OeWFTouG;u+Hd5rl7=R^(!>+kE4E-+{APZJM#n+mrd(#7$Fa znz_Xbx8vQ~`Qmf{q<6KYqV@kyY^krer&ITVWZ z1__O%AzJysZxnzaHUwXmWk5;NBAaco?-FVE zT_WusJ<9N#JQuCv5FH|w*-xI&$Y!k|(e| zU(MtSsFp%>rg@u+L}0ksL2^HPnu3Cnq&5l>Nz&5}7#h|vVq*;hbP zTxL6M_O-U3_Lyk!w`7Tn(KMsn{E8`>!0jAMuyUbLJ)$EMz0g*21ToW{A}(0@E_2nqy%&X+;PGxe0&E6(_RZ+bS~WbJm7* zL;`gS6jx1XKBj(*e1?FGXA;0;!{XRMZjl~Wqg1wG-}Z6Yw|!jQBPZKNvfYSN6U-0- zWYxYATM%MIuEtHH0J4T)tw11UMhTW+#|t3SD4hULFSHOrRcp;~RxQ)N+sCz)jlY}6 zWi>u7>$Z=}y6xlY5nM=9rY3PfLq{ieJ@hT%f0pME$r0Kypr3@7$rAyf*uapRfDD8o zgj|FH41q6#>f?e$N^p0NOFInSevhl&$(hW@T85Et;VGlIKSDH~Aeyv*DnDoEW)%ps z%LE@m3j`UGd@T^08*Ig&Z| zfo_rK_8OJX@jIMjX+YgIoH?7+CmSsofd+r{>w`k7=soy@d2;L%%Yod^C{dR?evkCW4Ld`A=q+^CnY+P(oyJM zlhj8huii2Ov_>U4T@0Kh6dhY+>FH!ml}#ggu#PNI))wOZf(Q9JM9pC#ClORyU`8Xxm29e&JAmOx1GDEYtk4AYiT3~*6-K_ z=X~=tV8d@nR%D{gK8)ynA)@16Z`bE!xM5_OG3fLh*mM)R6Pj=soqOZ_EXG-%0l&R@n){fixsQ4FK$e+=h(;CAPkazO2oOOy zjtD{{0+fZ6LE1kVn-XueOsm;4 zt$kK?YoAr!>anW3AOUq;BX&dZ65DPPfgIFD`?d^yz#1g5kRbwONI1owl~@23qZpXl zNXh_a?4bx!!5CX1q@L`%RdX^%wz02e{&QczTkX)=#}2K1?6CU+4*na{@?tcQVCi;% zih#MP*U#b)Szxm4r@Low9842g9anPzC(foBGDsC1Beh9GrN zU8_y>LpjF%jQC}jt3{5;EykD00utRGfJGAz(!|uzn<%7#d*lB{& zDbUi;UIf_>5<5{Me6)0er_N0NbR$_EQ9~&P6L;}Ogp^A zvfOHk=028a?qi8&k3dCGm_pse5&%Y^5nanP0189zmF1!T%@SI1kR7wZxr~`G_&4MQ*uNcntj3`*uV9X6n#iL`zj#Szo z&1H$M7eZ%g4uYi<#S@PI^5PdU#uN31MtM!Bo=V@zqCiL^@V;8b?>pkaeu4e8ieJ9d z4Kq|sr~pc27R*h7L2XS-PtuYT=0zcmHq?-)bZU*aH12m(;yz8*u@>>vx3RVo)wo%A zjV)~%BW)SFYNT%RNI#@5fUKS4^j6{8Jz`&3uHUN>2alefoV@+;x{3zV2L$cz$8YF^ zIhEIPFc`SbW2j#KE6hlLO48IhbEb>ckH9`n&yBN7ag(gq#`?$9{psc>Id%UOUt=cA z_Q9iv2nYT4`kiw*aegviT&&Ok;C{F=&z^nz{11=)`TP0n*WW+>asE3p2)%Ko=w_+!c+8GDb}zv+c&kc=(g_8bdnD9hgT>pk>4+J@IpJl);hVq8&piU8m-V-xMrgqKMKf@5R{ z0e%Df2WTH{y^8JP^6TuZm@Upqu*VY)_ISeG`7#qQHX~6L1%4daKpO&@78(Lgu3_X$ z;p0ElpHN*geDs{A z7ocg^53+M#gE96Rtis>JD*QdH!tV|}g@~1zMk4Z(g!pm5l|%nCBuG}h96^}9hy2)ewUX5Gz7S)&fpp4092tWKT;4Rs86ZyBK;sTnn)fS9yG`Q6; zB*6jyEc5u1vK}#f)GT74?cU4_4S1Fri2-4n%}Ydz3Moa~g1+wKL|zZR2ZaAh9Z!?P zO;dCWu-Z3dxxYVBB1bh;Tc48TPudyMC%xSWaMTU*^pe%$CE5V8`(Nv3W}7u?FzC~` zN=ERV#-DC}+R0P4eGwxvst#0TwR>(mm&$89pL|n3sajnBN17PzoxuJbSf)4g83ip} zpiJ2Tdm_rWU&0m?dHu%Sk>i*jQPgz7^8i*rslN^!QtbK2VIgIOguN3)eq^BS)(SwI zWTjCEXb8~AV6~0~)EEo{S^juqwgn2^grD4Rwl!L3+v0$&A)D#Zy%{4 zHz(T@tq)aFyM9GvtAp>>(=+Pn;=P#oRL@&lL1(-j$KQ6OTXr;Qye4p)pk3v)uaA^W z<772nE}$87_^*GQKu>Nt5t`pnKalENRhoRNaoaAALwaPUX&POEw5tX4SiT_2UiUO}6)qf>SBb)t#F^eJnPvN_Hi4YR0YhG03Nh6@BA;OeiLPU`R0 zZS#bEoVFh?AUTGb|6CoHvQmZTR`X?LI$`vZ(U$P@I5XQYolv{LV8mSmgJ*A#kHKn8 z4g*h&%jz`qggRwDYFcz~)>6YJsgz#fWnF1mG);}oV~LHiY8c%4u$%PlOul?AnSwc! z`C^V;(n(vQKXa*oRvC|kz>&`45Q`j2DB*=Zb!so3$GD0$h5-WY@BvJ z)iI5Cek;nSKi(kq#iljP;RpZtH~;J4m|94SljA!=-l`-i*V?1N6S1 z-x6amJ^Z>G@j_0oz(qWn5+||ZtwwXllI~mi9u^m<`ymFDy3Or_wIeh8pcC4@6xB0Q zwQ@2S^-x(lZNr?m+9pcP60cvLAgJMWa9n0c7pt%9B8GedILs%2t%Vjt)~~I{OxoVC zqE^1_%2Hf&LY;j7n*Dn66xWSVOH`b-Lfj!Hb4DL* zyoxQK)UU8jh{qT^5#Y0O4iaS#XjawBEmM{xq^5%slh zCWZk30Z=ZuTY#`vt!^Sx@na}&Awf-1Q-GXx;23Fu;wH3qSk&jX0O2;}o!@_W`u>O0 zqgSs^A-VajW}~c?-Fg2M+hYHhZ*ZXU_C=Yr;qm>9TJd!z-CXgotXrA1bt{v$`kA!r zf%MFDT{y*V>U+YBqz#_p5UES_2Ls`r`Y=?FV06?b6okl&hX~H0{R)B=UsOyQJN7Jf zcpa3ZjC+&*xlEc$qoT3dovf`HBh_Z3&ETjVdvzz~_EXg3>;y!lA`UV83LC{c0|cf* zdJ!m%A|H%_NHHaG6oPdEXep3Q1gRywB=)^cy+J#qVo-3`mTfrqmi^f+dpE{KpXCY` zc~t;f%AeK+v!zN*Z7^%K1hcDZ$V$G`aKja+@Z*YfnN}>-7ATW32a4s3IF8R{vWE8!t?e`VM@b8T7YV{J zq_0+-6?g@xTBE3q00COY*q%D;uZUrw1moee1QQp;vaZI1Cu=^B?4D=^tO3cc5jPWt zLY{(tELW<-{-c0KIwu;yIzTrb_j$_tP~{UZqTzq~`@j9~21mH=j}#G~=(11|43~C# zHP_-+`4dui6u;!xIb=K&pVd;GN3!PdynNqEW0ub2-~68xbDvRAGk85Gx#>mInS3`{ zuGv~LMI^l&CvWIl^_d}DMxF&WMpAN%Rrc6%J_yR=<<3+3+y8p-y*6-=CCp|`=cA38 zBmZ1IDSI`LMuJJL2(`0Nr9D1r%n&m==d(FD33i;xKmL#Z;s5)8|Lx#?#r_NHDw%fe z2_D5Hl(v;fP`dC$Looo0%WtX!BL2r&v%-Ao<)EuP$9*0BiJrlVB;GFPw1J)+)eAS=v-M&cTI{}o|K>kz3E$bIxyIsWA7$+&Je~bGr+V{pDy3o- zxl~e?>rMU+K5IEcLL!zrTM1=0$~WPI`ZGH~Z5h-hvd97Dt0b1p(uQx#Vv{&}ZPF-P zm)-)3m;GKE;|ZrM>JO#!8Rd63&rx+g)?qZAM;h1n_RY!rmybT2ynI8@zGB?lRf~>E zUJx`U;_~WWAOnaz-T6?@Kuu|`?LC@m;pO|VGY?jG-JKF)wKuF^Can3MzW~$bPAuh1 zarxv--;r#0F!kgQdcIf|X|hTVtj1QFb71z)>KXdT4YLdFp50hH(?c|3pvvy%DR0~Y zdfMK&b6ganV^|<|M*}f9w2}Z_NoFDe$0vq;ADAH#0C|s)Dex@Umc+?V^ulf`1GG9x zcQfKQ%@9eft-#1@3yk~@SuAT4qwgW7mV)IE;gCUXR>(d;&n$&>yqlP*xHU%q$G2P59@oW1)f@fGiq2d377*t4ur#BOr5Qae4f&%0a#yAihoJ-=0y0fb;)UQ?chDLv zKv<2ur~$%tJ4GuUXh0z7lO#@JIKqG^T~x6&%RqOhb+?x0mU|HHEv+FB)STJuV`)|& zOS5`enjPDQz%+;{4fKFc3Zf1hh6X1x@j>jL5>u!VrkKQn#1w{ssV#6okN|uyIB4Ru zf+fP2q3_+Tr8!mP=3l&}bW7i!RMHL?Ke(_Dhfrsg`DKubnuvP>A73KsEKK*b!crjmIO@KPunl(FIADrImJc<;_e+$Grvy5gWwXJ8) z_?qWP*3tQ_s2z0YE_4w;5-e(>8agqJ0-uF+G+I1ot5HfBlLYyp{Nm|chqUQAfq(RN zND(4qMQth=kv1KWM8GAp|5KK2_Z zad~h-r2Z>PO^ZcwgbfPU^XxDi!z^hWq$X5O4B79;dJ3m>_maMd2&6Vqe6V7P1%gR( z=UhW5{z|BaK%MiFPnsEAV{JCYu?gUmP^OI*Kw9Ed{GZ*!uSzK@y1{fy@hrpuOD1xI z**IB`1ouO5&o8EQJ}jjfbGlwy9F^fO)%DG3Sb==mjM{Y%GS*+dxh0fe{sPaSy}*n1 zxkL&R6&9StueO0tSVY;0u3bNQ#@Cnmj&9`eI9AuEKFd3Bhiy5c%9^`1XlpV_zjBlL zVK;hNwf&)fMGwiCNAq+g=wNNIuF|Yh1#=-c zybvlAkC*V9%}3CL>Z&}b_M8^Ae^|S6T6YHM(nqULMl{n_?3(RQTX=kS@7vjRF(L_# zZje%ViRmIg5rM?VOwCvVFd(2|fWAK)T9t-pfIKPyT?Bjv;UnUVLBNMjl}*wL$3@ZJ zcB;NhTkuG<@h!n)zD$>V7jOQhRzR&rh<)-9W{X6%wTF>edl*@FG>Qq+NxakozQjO&0S%+* z7!Hu}3PVFCW(-1RlAi?T3P=?jEvIN`LMwS-qv0q@+zM9|y3LJknfN`8Y{OOj%P_KD zp84f@v>Y>1cZF)4G61+%r(DB7MceRAt)ISow{5Q-BsFG>*RH;9_u8LkM^vutGx%dq z8Eo)~k65Klc_XipbC~hBYLDaf6}cPiIPGxNcVA*n0;J7xZ#{WJ7mznjNO?`>GOKmY zO0IegcaNQGR5~VPJENt(JaUd_>%~%INs@U?btP;!ooMVxeqq!?z&D95P@^>H8!DzJ zSs@ru7%Ftbo#b--6zgW8_m!WTKCk8A0v;qP^JgSTa50%qHQ0juVo8ErnnR}M4B>50 z=hugqlO&nS(PYN=5r6{aHN&^&JP}UlhW*~`PS6RBmf|CwEnf$XuC8mT@Hx?B!Qv1L zN*WC;2TnDPPMQ@fyhUO!k__v_KrrqT$)a zsOVp)z77Hyk%i)f`2n)qm}IgUcxHTJC^lZQvRXW)q*0nZM?7JDYU)*BP|6w)u?S-z z=rLXCTRV>^E)fO(f}l~$DD0sFUy2## zI2yV*%hGvaOk@W)FNZ}+D3iEY9*U`?Sflg~<;^V^pvyZ!C-yQe$DNz64DCRY8~Rn# zr-g?)q~MY6#S{>zSX_~P&^2sAD$c-3z>uo*8x_VkoYOVTlPdK*Dyp37eI zb6DI#P`5eY=LqO9i>xD#yGJ_Zu{@;7ox&jccO*fT(mRUlH0oZVTh}A}qT{u@$qx6< z5BFZnPt9%~bC;1i`_B+?w}KE{J+_sK&;v`08F+>TnO9Vp!QBJ!c4+u+VuQ-Z1b082 zXS4!?5iJZJ81n$(94T+bcNx92FkFZV+J1g6aCtE5g$gr^O)V}WAKgv!gSoa<%f5u5k5UVet{z2;}0jiE%0dL zjP6Cx_=|BQ;F(m9(PMM=;nnu@*$v#Oc!^)e=WtcZn>~8}_UOsuBNQti9X3`m|2p$Emnv&6XJ<$eFSA3OW7`}QtbAAX&GGf*imR=to8fBC zoB1UgOQ?^sXIi`LSHJBl1ok7au@ear)x+G7N6L^ptlv#Lw@7;xCU}C9^;QFGHsCjB z^Eoi~TTQI{7fbg@U-n!xw`Ye_a~DfaY17WD9=OJAB#L4;2_h4+Gyu4y9#lC&Xn{r{ z543bv1bw8$m%fWGAwxoMA%Ku4Xuzx_4J*vfu1Bc9UKBabE|&X^IglxGz$tBjRvN1v zB*r`tsX-MU%szl`0Zka1U8}14!|#Dzsnl6}<%@>f_y4w#7m9dCC5Em`@u|2gzx}lp z{CYqS^0&Xv*Tu@(Ts^m6AMQ73{T0)woikjBsDNKsiDaF~d_Cve#iF2cywsgBI4YR&zyu}IJeNR4v zgM)%gHv3}Z9w5O{YRoy}!esN9CBBN2<^xX@1q6Cm2(oj=gj-x#r5d(8Kvi>^TvL?3 zxqrrx!OdY5eWT<|c?Bi94l5fTs7aK5Me<;NrG7({Kcclj^Sha_aQca%bDY&c`Boa3&|db3c>lPDt!gT;_SPuDF1+7vB?=)&xp-y zGRqU34>wxh{_fvr>1tWh3@%y1gQt928;?PLV$)0L!9PJp60RG5Sp(rmcM*Mn>JyNq z=_ZvvQs<020(;=J6SI-=(mG^`ul(XKMl~a#2Ht{nyAz%>+i}e zgGrn%#he9i5_vOr^7LDD$j>Yl=L>3aqf!6e|1toP^-|rZBGsl|UrZyom2^aAsEKG+ zT3&Mkk3^R-8#T;f*{+~1zbta%c=;}`rYxaM&=7HOEpD{fE=wVEA8gTAnw>;8Xtv;t z`Mm$P!K3vVH7#;Zjf|Ab23m!fgPZI58dN-V*4KzhZ~&+P!_W`tf|Pwkgh#oibHG3h zj#Ni2Wir8b$Zrr|E<40|=PVMJHLWmgf6w$SuKcAOYQBJf``-*Q`k%>iBUwAx*oPC- zh`$i0P<|6Z7R6}vG(a@8%2%-%Nkx@sR_g}tn{Kd759s^H=VDF`HJ4Xw$|pab;%xZ@ zN}XHTl&jB4NsCW!4#Y=HZTwo|1fNeX^_uddoqb-=pL$KH(O!dej}JXWhky=5X>1{>h%9$3 z4Fc8zIRG6Wg0TwIbX-oDN>6}s$h9Dt2J%B0IO!&bDloT1SagT+VeZzK;zQTK!NJQI zJ2pu4Ep(%SmJ!#CW(^^rq}msCTi4{!kUTwp{Nm}853eX>I(mEp!i%H#uZQ2geDt2i zE2*+{epbKe<_Xk^*W`t#^R?!qD_=v=P0_wxrM<#sEK<-yd;yrXxhsw%uY4Oq{6wEf z{`F>*Ms5x?Ft}pAu{LmJmHF}H@?tf=LQakgre&IoY(Y~Og?NU%)(6Wqx0%yoIcEVF zHR40)!O2{|By7Dtk zbirCTw9q3P4<0-%kHrz2MkvUch^8CW{ye}KKRi&5kfz*(>r-WCVQHXlC?OMqS@5Bw7B9B|o)=;M z#(^`whr+STCXaC*OsK)>I2x$1S`ktL?M{q!SjewA{t|<8qyT=aM2s@Wfp)5!R)`^h zH>8lP%f=MH3B@~5V=Ckzv$v}qm-s8&F{ek5NSlFcTG)-i$Zz*0m@F62&A(H<-ff%S(>p zXjUcoet8-kA3OuA&p`D#EelqKyj`ovcO5eP9x0&07M3pqV6Qy?B*(4g?ai~}C z5&j)b=F4U@#MThRR@>~iw^(f}exdbbwUv$Vm?fQR38PSP&DEdPk(xYm%N++}f4BaC z*+JQ<7^~imHrwQ#+FgxP?A+3swtMI9LN6dH6vFet^af8^7?23I;{@CWN$fdsDjXT3 zJs3J+K)D)11YYDP3A|(UpF5xv-;^l~T-$QIt!}k;zuE23n7#=nh|v(|dJ_H0F=#_^ zwXO%S1Nw(WWO(&{bFVtd?pyq`$g}RWw_qQ83-+W8*;sUs=@TPc*#9M_T%@_`0An6ZTy zM^NAJOg94h0O-BIPJ;-{8&ImYVSC0VHZMT)lyBcpZyk-TpQU+?ZF;8Z?qg~0K9=To z@1l}u*++X6bflrmXh54WkgzX^^?@umG%N!|L(=hL35oLnO?$R5Womgcf;>|Q_)OJt zGdaadTTN&D_V*%zZ4aJCIp`JAu3~GXYN@6h;D5dRQ?L zWkBozKAJ6pG(j`Gk7k}Y!2IU~2qfYF40*_sM9~*;+^qYITQ_?%_OUl(AA9Q|Yh$>C zW{AMs;iu5^4ijiKxq>G;-;5K}c8JYGLaT{@^_~nO83-FVdaS0xb-#e)X5DApI$E5u zPffwtr>0fBr0=W*QHjBTHSg0W9Q!RTI4h^R&tg0;}5ZW|eg5h@1;X1FMZno(d{ z$jJH7$`;V2cEDj2CI&TOm@z6_C_`9Vj+<*5{=Kp7<+$zcGfn67Xa;6Yl<%QqpN^F{ zbhTBM2Pt? zYm6kg5_e}Rq|S(?eo8mvKnbpLMSmbi5su1OB^-4)c>3oiAEq z1rQ=6i~@pPbc6y-4l-X-4F{wN9U~*gq`h7*Co#bNzEY;pV2qbMo*%!&a)pHv5P+T{Nw4D{iEyL)0fi3jQgm4#?q z8RaTQ)L;XvpDzd+cU=hD_}NfRJj6nGasSvx!{h`=jspY|uwO0wR!fl0L0;J9*{a(nokdlEH)VPm`;M z8<$hw?)}NDqMvE-;N)CPF!f2iBG;G*ad8uXDkW$h=;6J*a*Hh7>Cq7L?kD}1H~7NnTPe=srhoD-XFCRlxEm`if1%)X>!HYzGM_B-d2)z z();89QEHPS@(o(H)I7u>FbuT`*aLws{GeGuowE=Jx6?Ly8WneO*bdz*Z`#UNl->WG zEjHRmI@Grz8@fY(P3@jtIEOY)@ERmtqsejvimAg|&7zMmOZp*a7h59U*+|N=b_;no zJhvvCUkG|^-`#=o^9|414f9h&jZ*d^8bT#CQX@Y?y*GPGd$mv{wCvSAtsVL@5}*kJ zm1_!Q3&cJW3-u9*z4{I+#Aul@d=&ElvT_lKNDET22Ji_9I%1Ig-sGYWN&nd2Rs-2j z`c?VpWiombL0T-G7!^eU9tRJYF=VNN)HIN!_me%OUuz^nUO#>E^62#4tD_$tLBX}? zrFnhy2BFk;Y*pK1Qx}^xblz+?*|e}V)SJLBNfL+N4$t28c9_O(-VW2a&D+r4pJZPP!b>j58O0@e;6;LdQxJtonpP@|fa-wL z>W(G9?P}5I{_XfPdpq16Ylj#5LI1ad_Uiw3&|ck@qyWhU=tDrWEOMoABb20qFb&-h z?9GWE1OQD66tFB4vfN=5q2QSSWn?5iN(^3_R^ARIfQ`_<+qc82^32`89lzYSV|P|i zyQtc?n~`g`URC}qa)e#Bu4D{t>cX|$ri`IYBu-|t3e5Xr*v*V#{sn~{qbxt7qA=vg z7L~^pCc?4oL=^+>#+hoa2`#gQd{?HYt@_p$r7KpSAO}cEz&wk%EMm^zzkl<5 zpct5u4awl3K+OutzIa3Kq=TLILsSeFG8nWyG{Ri2FV& zhF&n!DZY&SvWHsr3)#I`aXYB=-E*O6MFtn7o=jxeGdg~*L!&wZ=Nwg*?ja)H3CHSJ zXhvLR;`6!uHuFwEMhi55#M7e(9p8cnuYn@Bk*1-2>#nQ~n=j8{^|VlSYUN9VmuvEcq_AP&j6<72j@M@$>n)wBp-f zdw^^&0MK>>H$=k4kq)Tsr6+M2ru%a!GYq>vn%l0%JG4P=!{OM;lFWTgv4@T+a8ibF zZD?*7(r{&Lc@edYNzb(`3w2V*vIUTz7P+EeV?ckRCG5xy)7XavR1|YT=n?wX-J0Tt zCvk6z9sE1_3F{NMLWlqYBv#oe+!Q`t(-sx(4dRz>_T!4Ln<~Fssvn9-UO8-%QsY~a zd~Ihpc4{?Ev9qcSe>-1ICrnhOyf9)USZ2xmiw#CNCJ*E+K7D;IKFR~m?8C>q7DN~} z=S=_jfNAWQoi|VV>r^?P{@?-mCdArDr7ID~aO_rG*20hjrCzX7Ufv?BN06&EXJrs* zTTpRTRri$Afj$g*K&HY1 z{;l0t;%t*_f73Uyce4CCcjm4%>!mv_D@2p^pE@n8Li0|YR{|iftyqBfmq>d9V8`MR zs?}lai#VZFod@kPns6Z%1?<1!7zp=cBL3i_zJaN`R95Py%A<*k4Fbl^#Gc2W|P7%e@4UD5l!V7uz#zII@Bk zuV+4aamRv=z;sn`2|sndBPd z_~3+!mITgVV#S5bh~1fI(Su-WO-2%8fEB3E|)?ZeYjG} zDHxpQ!*^NX{C5*pHl@$082t`K{e*E4JUJe}BQ{iZW?7Y-*w$C2bIh4VZ*;kolyj={ z>8srn>SKKmD0ClYcV>u5g2g|N5PjeL)JV5p-nq4AsX#i-YSq;Mk+(n0AMQwVlXQkO(5l&Oh~~2Jd9+eGf|_GGXEw`)R%2#%`0(p1sN&mW&>vjNf z_(!B++g|D=NJ4`x0vr^OtVU@Qv4V}>Ymn)IA%0`(2R8}e#=U0#okD?X5-fARJ1$he z5tO)goC0Al(->u;7%jOn3Vg$B5jyD}+Ws=O&x@xlL9vFql+>CNI6A2ua^o7dY6tJ*(eDmj_ZM|CB$O{ zzea5OD3YQ&WCh-4zlcSYK=$1|IWuIP1^2T*x!sP{g%CgfsAZm(>tuehE_|!)@3smN z;9)SVX2~2X{07bLl^%1`zfzy(HG!jiQQfF55S82&G0D*{6Z(aR9I3sTXpTQsp=E=VI16&eWH3}X$mR;p9IX_NRB6L9;OK6{ENrG>98F z28}xnGg)R{D4bTlPm47HZ9h)t(~>MB``40m7-^4e5Owvag^<3yzH_x_a-5Y$bgqH1 zqPU<(QeO$u5erJa5a&N(B#GD_FfVbT#Q@-&QQ-zs0`S%8Gzp@+)`|+WHR)T{9~wh1 z3mQj44};iHazbQ1+Z>e2PBQ{3a@vJ5Eww|wrhRi^QGO6*8KlA{31A9lbV4PvK@zng z!ML?B6#ho+G2@C+R>xBP14^MvWs2mb1}9&lMpt`2CpgT4@u^${%vm1^caL{rG8<$F z?0U1-kYhDP4IU_jG7-=(6!?-rnzOTq7}lEdp?Z7!CxPCrfBI=k zCA!NdTJpLry4JYV+77W7t8|-s%8uv|8lqt+vY5 z_6m1uwH2yrx9>rA6WSo}lAy`9p(KdzX*-DhFm}O?&_7e8V8UmxiAZK-UkK{umb&p5oqBe z00JfPd7+)vBLHBW+A;w!FtCK1ritN2QKe%Q&|1)bIlb&_SFvf+TfC)x-L0+kAz;6v zq{i$ZQ`XI)%?jdT zeHJ90j(^5gkNU4O%42kVy?gc1{>^ysfgH}I#1A#ItBy2E==}=~8ktVV)NVCd>9ZMN zD(0=emr?dnCf7)%9@3W5RNsnu8V*KAmpfMdCQ^T+#3-{b$>q!XkS=MX>ca zMRl8S-xsa_=+{B6kcvUny+$J(lUsJF_R*bsO9@9qX<}xhF2|~$h2cpj-|?yGV1Rh1 zp;IzMeQBY&yQe~>aEYqB5RDpqcsVY5la^dznyHi}$#zq2^+s*~;bp0-hnjz|o9+H4 z7ycnr<#Sgu1?78DxfYmlNadQd?)`va$@n~=1S36zdC+09DH~;dp8`sIXe04cdzlwfBL^q z-i=k#7z680u?G@KVo7y5)t4&e;`y4Ch{WvSxagBwlDqfmMrqzCrz8V%v0fSMEo+&C zh3ji_e$-El-OTYVJ7Kc4c!R^X^>x?M-R7ilxH&0wsN%Kk-?O0CHc~X5M~5#^mOp&+ z^!wxSWm4zS{h)V+Mh%Tr%^7cwzq=!*+o}Ck)&f;zl%vTi272vZnHFBHR~IjW*&m%J z7thYt-#wE*ES|sk)9)w#)2C-27O(&1VYY3>{d78@tDrUwm(1DigE}qXseF(|9)gxt z<5ch6LPXGE@9qqFu9Qe@20;=yATu_Rw+7P%_@g|q_V~zAh0?V^LEwkToPlr~*-#Hl zIQT(9)aV)&CjzZD$eg*oAS*k))S${l0E~C)f`JG)?4m@UsG8G)#K|ReYNW_tzQKKZ^{UV!-(jGiNxapzTXa_29vG>u-kywP_bg!VH4E5# z%>vz73+*(;YYVb+NKPSt4pJ}l(n;47Xh{IQd6f8;K=wR_22}|DrO1jAz$ za+8-8(Qbg2jqrXJxSs|7Y-fQxoC2)9rT}ZNDWH3{%7yYA2(4VS6`E$`rZy!Bz+>tF zsY>m&AfG3`A2b{~EMgvk4akNPoQDFMkcn5B0zAtIeEWV1xSs<4Jg0#Eyo{Y#5l^W9KU0=OcJPk_yT0UN$iA4B}ayp)e6n~xA6Wg z{Ico*yWYjUVpMal7`2BSg)0-ww=5@012={k9dvcP5d1z?8i5M{sHzl^s^^%Ik2Yq! zvPs~h0|S`v1dtfN8lyTckowlWYkL1Kere?dyM@V~-U-;p9`7Ue-s645-aYv2AvpwH z7zmM~LL5pgk+F$4lH?-M((Bs7S(dKG&Fi--ZykrqSg9BUuw<9r`lhh7i>&S8*$3f~p^8uWFe z`_KpisgdJnC=-ox(UB19gc>H@ek z`U@eN2K`Y;yrhyO(IBIYoCjjC7cL?Fi3qr@?*oJ<_Vx1;I#Td4DBx$ zBrFh;i_j?01P$NOqC%Tg+Ph8S7ieuzF`0g# zsX;{zx!Iob4t*Hd5FI5rI?A;G<1;y{zCs1~X9Ak5tF0Cl9ucIaXsMloTvcA8`FDb5 z+!Zx@YPpa^l5){NEB+|PFm55%yJgZw;{mCFP>U*)r^KtJ+W(49FjjpLr0D^U)~~sp z7QpkbRxJI2&h144)tbeM4$CS!`KSj6|Md5N``-_;o^bRe5Sfn>Xb= zc=q=AxGL215j0c8yqet|)&8KatSl%~zPwBb3jIA|fxZPM-+3azK0B4vr>!)!$WlZ^ zglg0gUA~jUe7|Aw5YEx`j4lU~l3_}?V?$VmE;Y}{wTuYtsU}? zp(oriM7>e?ROvf$w-7VaEw4!M1szT30-_@q4^lsHmOmSSj2MSp-md6{ zx;Lk^V_!Eg-?2NaEsX>;D?!cbMP`ylrsG3MhIBIt;2sBPxOUL04VhqI;2luIq^J|% zR=@=efsBneJU|m+8#w#=>AQ9)8C2MPB#GC+3AKC>!h|?+G~hs_z<^8u#BrqA)dPHd z^!n-hqdF%q_v30={yXydw?3sWpUK-{q}FYX)Vi&ac262ui4&l=DwTNdF53N}bd7*T zQYAU@P;n-`b#!-2B+dn(m;#27IK(Fg$R^kYl8TYq#3$z88|jxY(*Cja4m&k(Yp3RI z?X-J19R)UW#VM$)QS1SIMQoxU&=Vvu1nPAM^QIXS(>f7IrtohDHPrFY=pWl~-CA#`o{cWJ3A@pEsfcd_iZ8tHA9ee1T% zeh;P~5j&{I0jUX500Tl52?$b>Ac}$KOj1xWgONXs30570r9T8ws0I5q<2Xbb)VC|` zmzYcehB3l>BmJc=`>l3*+hyOn?XqumU-mr#0&_oh?7)ws5ISm30*Y`@TS4pssuxZS zCUaP5l7M5=b0N6`u3L#TdIC)|FEZS!duoDO;NDJu!OOneVyVV$mwn^5%f8Vg+J{S; z#*q_85K-|E%Yz6uO+mc|LN>_%q^KVGM9d1ldwl5lk%xkg5n>%fDMo z4XP5d_m+CQWxv%(Z@cUpw_Wy)?#n*ls)@i4Nbs0eU`g~*8PrMT5`Kp`-4ZWOT*E*E zC1iw5P>2C25Zf^X8UWs@v|MA^H+sKw{fo6xw#^n6PP3aGOwIHAFRrvb;J?)q6YrnU?+b z8gnAYk(^(zr)hp-%uy#7s=(a*{dRSEqiwrVUADOR9JE!JdEEJ;T4JtzjcVem_#?`w z<2JUEb@bxQJH-?f||Z5ci4M2KThPK`aWl;&&Owj z63U?PG;um>GD|KzpKsjw@z$9w2h)tF6NPn{MdSHgUtf>UW@}co(fce9#o5)A3dUnO z8=tQ(r^P8AG3{q@YPk0ACl`~i4_>NU)OZ}4Kv42N;{!dC^T+u6u<{ljUSp`@STu0y zCfsOtb1h2?D*FY|XR@-dP<2u=-7}I}QgrG#IYn0r3;X)u)#e@9|7@}P9YXpJMtDes z=ZCK_!M{0vT3o=>52(9C0Au5J5yC?OJhYJsHCya;F2ylRuUP4@RX{*?X06h6!v?M0 zVaeL{cTXlP@gx>E{q*d5TSqwMcoERw0WUFbsJpJwq3gCMtuZG2C4EU2!YUC z1O__CjuWW!B;cNqp$k$9tc&Q6k7Hp%X_-&tSUf)UB57%~qCcv(@*^ z{g#sNDFj$D;2@*0kk}y#50p2H9O)xDW+t(FC!#!fgni|BTAqi~{}J{O=K*{j-P7cQ z7RHubhg zXzhav6UB?0TrehzXn|wj;Uk2=HlAYm*&pFbMHzT7X9Z?Y45S~0he>3HDLS2To*LpZ}$ia|btf8=Vu~DJ8MEh0bmWt4& z=|Dqu5Q0nMIPiTpakr6V;mK>UiU}Y}ZlGMSzcPj>FTy6dBrNU%yzPMhvJ_5s1sW1d z-0xaQ_*g@8l}|sNs_?Ib6&@dOw6_=>(iM=mOJR*tdL^$jKj-{&Jk~lF7 zKn*qhIxdXq%n^19!Un78ORR>u$GlVnMnvw;Ed4V%)tWwv^ZiNzt_e%b2U!k&T$5-K z!=1lcNELaoATkEm*IKzH#f8tW)YT&3!;krOuK!H9=#rcJRSg*O1ibt2CWw>|`R7*y z_5S|$t5Xo@&n~{g^PK+ntJxgv{TTTxIx=rqiW628c0;O^q@5KdJg#a$3aE1>9;7WN zHzvpkkj4}}t{doU!It>JFH4VMnGd9H4~Dp=z}F6p&CFqLhTSgJ z%eOpyG`m^Mn^Z`Fi*j~&&c1PXYSnzSdi@xqML?#yN}T#ei+)Va*4@P}LC_iqs7_mg z7;WOv4s75Gp-gS0uI+nR=K(&%T8YMCNEM~zk3j}gN=rC#=xnk|0jubP^tG3KjMuuP z=L=qGNe7L9r2K6{w3J2=V2OtY4!8u+uLTUR=$h2M0}992PoKOzI(_%*=!Zu~$m8B3 z8N4ZnRDF{B3*6M1WK+=Rg?&uY3;UR)*IhXmU_ui(MAT)WfC)rQijG;=k-%*GFwfKu zBIsf{F&Icg0l`%mqYG~nAQ>QJQb_225tH=qP4X^HvMGr5f?g&W^fJkyN8kfyTPFno z0Qm>-eEAU1vLXWr!3fH&P};CVhy32JV(`t`Al-{ zWu&W5ykXusSdHEEKuQ&t+8dQSHmn(-J$@k+%?ym?HGztnq*R#M2M&RF1!N@$4Q1|McP1~%p+CKHk%jY#G+*H+Ek z)^wzLkSVdOjc|EL#Z46{^>LUDfb*EADgBCJ5Lw9l2=v5b3asgg zX%W?9+?q>lh-%UFW~ob`%D_0OLGc-O#b)tEib5gc$T$;;)g?exWyKgu+8Ci3^?2eE zR_o*@Gdxzk!ws6^M$Q*1!$Gz(p)ieGP^I2M-+Byy&}K2yqsDV62Tv#*$#2~Jqb^yg zE%YH$N_ZsLhO;0@D&)5@S|9|D1;wiBPEJWS`p0?pa%?^uwR`qvoqWeB#A)Eq)SwuokG2Ius(Dn4y(G}pQ=4t09Xxs^iXF*(Bt}e|3ef{ zK%s|N97O5~`cokGYM_Eb+$A<@CXje_ptB zi7EM?hAHh3?x%>raufi56hbc!nDG#tooK7s$N#)D%vJTl{49{b-PUOJwnnSBHTIDR z3W&oh_5$gM&J}d^(_)b0gDF{R*X32th&5 z-xdV8H%9Acg8T0_N3*v%n!U}jhfqxl+Ct#^jnGX|KSOaDy;F#JO~*`vG_gsjBCyf; z2fzSe|0n_?c@&}16moW+T_NN)(QoY$r|`W!ehz!=3F?O_I*mltq%zubo#s7ynIiAe zy>H(`T^Ty;Xu=QCwih!gd?3;UGbBLK9;D3;6G};GXj$lxMpK`cTF4HeE!77#=VmV5 z1VJD&Hrs4}`g&$c4TK1sh7nz}1nHWtAaGTJu@2&9{v{3J`N3*v*n!W9@`$BEm z5Q;?#141>>koH1IM1?Mr)aVX@2!a)~FVt@Dh1%`Y7YdqPC?+LNgd!rk>_Myv#9By*LJMrY&{2|(?M4pz zLS4^E5YTuw*m&^)Dk*7TRTpX_w0+;ab5k_$P4U(Xb*nX6y{*yeZH?U*YEal3(gi^~ z+UOD&oyh{MB#AIE;`X>t%(q0TSGZ;!D(3iwyjnZ|5Q(dTS-!Ogu-Wq>V z3w5hKn!WAO>}`)dSdwKDqFC&@0gB#XMD}Ki&~_bEyuqCliU?>7NWUXgP5=oqjWk6) z9Q-6v=zYYuU$eE`SHPr>qO(th+O11waahkKqGVknc+i1JJ zGi)h#qeU10GwX}p+e24y%row+A|&kKVn!%blzvg=0$}%yipQ!dnsS*5B|`NjfoliY zLH%`wyD}bB%j4fu?QNBHZf$g>5ohk4#=;P^GqtJJA5MKihZ}P>mcXhgObob&f&wNN zv{8AaEp$b33DEM2>b0~D`ZdP~KV*{Xunl8Tq823|8KvD5=* z)VVwqYJlSdK&F{_7sYLY%2MecrR0@DH>0=)isLC2IOp?ac3HZRoSnM5XLLTeQ2n%U zsUx~e)F_EcbkTYD`0?P;52NFw!IP&0-I&(`ot;f@>T7fbTDGA^SF=GYZS6)6mo~B| z*l!jAeSBDFb)CA(I*hbkw%aUuSz|&^wj)u!J42$Xqtzo(z2-KPws-&3_nMnccI?$VTsy6r>mp_~e`rO>nllu_YTf<}ZYFwmZ| zLR7b*t^p>-6c5*k0ZGR*245&N#}T6=0)m#KlYZuhY*F)=8U8#Ex!qAq7vtIdOr+ux zj{r0))a#TnZ`6NRam#p?GTYrU`nt|7OR=_RgH|awitiXV^je%j4}e^V=Yr4}+#kN_ zYaKB(Z%Y}0PJIx!4+X#->jE%3iFddH8Tv{&Q{<>cGN3N_${PoE{m8KmQ>T_)ZX8xJDBaac3yF<28FtXgrHAs0j*c1vp1w4grz^mqzJi zXx}3iGaxTzL3^!=vnCMLAXzUI$palm%8g6W(hKBJE(s`iC1#Sx1lDkgZPPViU)e_N zMABiFxzkGYB=GnM&IQ}48F}2_B_ll4;DwxCHFhi2ZPDE+TVC9t(}~g>hjhZD+=M$h zVhwxuZtrgzrwuyw9&ubrIAE(qe}{KxcIwb?nChES2KDp7X;Z$SY4h%l`DoAZB7p@O zjJTkHMq&VDPo6*#Jw=uu+>Rm1?n0~{@`8y)RMCMK2op{m3ih7ZnAM?bgwBtkSE|6? z?VnYU@e-hYkDvmO9CfT1q?L|u2T1dZ(8bxsdRFh#?~CUc%ju8n#pdkp9&p?$$JM4>tF4{+;50)71;(X>ggPo6VI6yH!tiilW$Gt2^0kx*-^ zOo$oi=u))-r;MCkYGs*2YW$>mU^RHKl<29Y&RC%~#egxM<^|95G<%Nt&xZ#GkJqg5 zMALnapvJVUd5d^^#ay&CrM`Z7@)>5*Y}P72m%R7$u=M;7cOp|?^ANwIAr3bTrvp5- zVc+ffR?muR`vLDz#h3C$sOzZ_Sjm-9UOY*@P3-mjQ}XdRxO(*Mx8Htxdhz4@cYpe@ zel&Ud#cG~}-Ing8h8O76bX%H~ns@3vBby-Eh@!+4zMYbwx{p;Z zg5T|ijx;3vSYpOcp9Rt&vq8efg11+iO^h0UR+?tWM7s|#*WZdQv8Mk>v-J5Q02+)8j z(e*6EIs&Ee6l^8}QBLd;mMu&ZP$`-z8pslqBoTL{iDw}YacsoAmQg8%6M-_NDZ2A$ zSgniZ)-rUfN7P}_OeTNA413LYF+t}@X}PR^wM~<;k#^al5i|{^f~;RCpJ2ej2pm^J zx!M658ZL=d3`O z27e8yPils>j`~)Zsf+rOvQCTh`D#8cFH~6+ppMQA8N5HOa_A3Hv>K!Zhi4FT{|nOE zyc4rV;AmvXEN7MrJ>F+ax;c>bV{Pr zd6FVt3Smjbm;ss)-G;@jLb&NOZYTRXJMH|GXMD=&p82TGpBriD5{I~_bRI;M!5R20 z{8Ng40@3LzYi7!ZBZE=hWSHomnvrcALV-33cI}DCeocIjr5Jt7hLHknfoN zZX(!e8$IDEwS&92y<4NF$D0m*M}?XCh}ari)aSj!AM50hS&a?jJV5h;UtL$=Shc!i zb=!Ad=0o30(cgeDIE{@EZ4jm@eJIf3cSBfUIZM3241tD=6G(j_m2IUKnzmys`lcU? z&DsT8&t0RZPJ+EF<7re0lL2>@fs=x}3vy#NKK8;09LgcI93V(;w3N}QwObd?QSj#S zc&0i0`0dgAzL#d$Hl_VIiMaso0ov(ea`p2p_%G`8;adO&4(z5(Jq zOEUYKu}CqvrspzJGc0bnD~p&Wh3O7k7D*W|t?~ zy$}gOM9ToNFV^$KplY6n(jHnZ!6Pk+9RUy02a+tr?K7pBpgY(yQX>dm=zaoC?^mCs z0eAZDp5)e&`Ex$W-Kp6h<1z~VAmTA;5!BaI^RcL&b00*%LD|d)K)0yhxu>3u* z417lF_>_{RG}vrSDKm{9rsFg8SUWz;!FR-C^hhjq4wq8LIlb;J*XhS4&#hreuHB?- zzO1nC!-{=($eK0n+>^TZ2oRl{ShIyEJHoggWMKs`b4vc>Dwh=?e%P9%w^ zA`0L9J_AOTx^yW?pgvcp$h{?lh%bU+408hyX(>*JL{xj=!5XqK%x|>U4y9g;1RfSng)=5^bCp5Qv~e7C$Y9;TI*PjVFc$T<@1Lk`8*;_$9b~J? zQ~B@Rw|?Efxo&p2l=a==CzV(CcMyVRudh2z3E5;1HBW5aR*rM*)~?si_T2 z%OQ*}LNMasZ!d|G&;#|YXA7_-1Oa#vDwXG-V`Q zlG7vaIFE555-+M(pk!k5GbT6@U{D%>eyNL&=xUfy8B*VSMfH=Zqf%=+_da}rUyB$< z=CwQ=h5haWb$no#r~#fJLfRga0>qx_nrK;){DdYNTAzhX)W6A{6Sx{QQoI19AJ`-U z>>zN#$ha>O@zY~!sZ`+pY_Nml|DU~g+l?ei)5NZqPZ2f3sxx;Zf_^^aXw=OlnVIZL zX0nsa%4sbYgdfRh#;N0wnNe4Lu@L4?fV5a)1grq9BtVb=Jybt|9zoy#o0*57!-Hg2 zSJiN)tGnpoZfrRLSK(orT)A}xyrPuD}p?glc&H%I~DO%kg*^sYsrmqOjQ6u z$pkRGD~T1$2?l;GN-`*+iS7{TPnX1yy9`!K@h4Zx5HP^O@oc^sP_0U-mkJ;%7=H;i zvR@JI&D|qvgD9^@5U13ShQJO$M=tnIX>-c%-zEXUI!Pv`5rw${R)u^ai27`?eOB~Z zv>@LuKSXVDqRt4EN{dyrUR86lz;KJ(nhnoaAk85%A`$yV5WgQMH@)vs__P87LfJZ<;m6;V zhOLPKraZbx+1ygVsQO$)QvdwbMG~5K6Kw7_u?4EBb>M4XrayX%&CLp#>2Z8B{c&>B zSknte)uFz_685@;tF1;wuZeT8{SdrvA|KrKSa+Kk(6q$&0^lnEct>7nnV}7MZ{W-) zG3qf9!46#;Mg}Yo+YFKA97d+?QL(8W0%oVE_gNM|m>)S@?Uqygedv~v=h{@osu9#r z0-jC}e6lEQ4G~Olp=lxryfdv40{tsC$P_u zx<(ZqNvykOY`F$dWeuDT#~_*@4>YZ@^( zF0Gb0)*sAP(g0rO(pI9Q?4u=h|J1 zE+M>oF$%GA~#D;Znhlo|nfiX)SU)w86Yc~c*Dz(YdKiKbav z7XzxLR5fd*RzKQx7+$OA8x*C^&v(|CQe+Xi$Up>bI|`@Wkg!Dw6aq|Msc8l#+}9v5 zG(86l46fS^JCzs#;H=XdtQgAA4lPlcG1Dd)D72I*& zqO#%&vn<_v>=kBKd>6mG7521}8defqJIzTiI4K$_fjX=N@%;T)l6by5B0d~@0P#b> z7c^Dy)}*HG__1q&xGG8!UPbAu2J{#(RT+^3U^!6r#sSh3Xn8?wi(Wv>M>f7`KjKQV zIoUvaT1mf#m9)E3lsgM-f$7)b*Eqi07KoO9sMdwrc@)T!gCqO7!NGBXc42sZNG2m@ zAQ5Hvbah&Fd5F6>a=9eiOaP~iCS)Zc_YQfPOR@~PO%89u`U3_;Zvz`uMnDBNIaOPJ zSBI5b`qmoE3E_5;9Td^cSMM(`VIYpkcI?aO;c7jiY1k6od>IkoH;Oqq#wp+ys1bG$ z5WXFu&r$z5GDk>#xJq$eQQY~3zmGr@@DmWx|Do`I2X7FmrN}Y)ObB`7x8)oD`j`L0 zaOONL{2sw|1KOA+#a(HH{=7z7Eb>w5-cJh<1u$$naM4y{;bZ_-9EM;qV(ecLo@B&b zTn=L4en8)m4}0*huJFx&{mVa7hra85oa6Qkl92K3&;vr#3;J?6FgSn!Mtt$h766Zb zx&`b-Z=9qnVM|NT=u9yMC5?(h#WtqartyRV+X6Jr7h_6CH&cNjNK?RKT$JKIF$j?lXGQZw z;cxbGWpK-Y7eQ!wy~M4E<0qnK7^W)(V5R&Y{0;1{5$PLCa!x#hj4w8f8Nv`J03}36 z6z-p>EPmdX{6*OAreb1M(>!D}T0F;xlv}{@^N1HYca5d1bUUPVEVlzfYD^F>CANU? z4zNH*N}l?nIC?Sr6K4f1^YDjQ?2sBpETqhgu}EcL^Phn^7s>~;byq6!h-b7S4xVve ziBll=l0^bX;)sA5U-aq_{7z23Kg>4G=a7XnWN6G;dVYg>;o|R%{Yp4Yzq<#1A=Xb5 zZejkX&Ul4|Zg;~fEIhKOucq{Hn|p?CEl0{H$?sO{c!p%12zf(EOC~pLx}9DaqZ24~ zqjA_)_E|cruG@+|EBAKCpw`kv2dbBC`YsY)Q;WE$V*rJr9twdc%?WKl9BW7fG%O#1 z$pG;m`URQYU<5Xb0LQR2;d(K(y%3IDGh!?12h#$0pfZHB2Uv`N{?K8Ug2W&IJu&!U z{U~*%l6tR#o&7YbNjMuOll73`O!Ep={(2RW%V@#bAo?PXC)MdwXtsw=_b!fmrxOBr znpIS&quw7*&p!@AED1Q*vzzKPp?oKY0A8uBz@b;N67*->Dx0M)a3#wMyBfB?>^-a= zNF{oU8^Nr7{JQNnDV|7JUy~;xUU;{f&H2Kb4&mZ4LG2!Zo^M6PJPO=bIPY=J5V_jP zduVB&ut_52t&iB%coZ&1bWPp;`rSut9MkpJ1B6CaixG0E6m~Ms32~s}+Xjkw1}EbC z%jXQ3OT$31ssQH-)CiGF#yklr(H&5Q99(QJREY9t)WxMxTVIXU=}($6d+2TIXW+kZL`h8gj{$OXDQBq@$& z;cUEu*nut*eO!_KFyye{XMmoO`qtKhviIcw(ib2(k#+I{mBg#T3Ey{c@P1mBZ!r)g zHezvL?t0It{A%1g0^T;X8zDUm_r0F-?FZlgaL(hC6B&V@M_iH#tDrCtQ^0jo$n@ag zdr~XW5&FEooB)w{saB7eG%1n6R0uQmCBIPn2TRT|Wr|ByYQ`IhQbYV4^tEJypqJFS z1qY26A`~W9+5vwp)G@G7^8LAP)~O=KZWuRixCyJR_+u$e@bhS~0-Vi+Vb7sw4iOg& z0d7ND!cMh?2N9zt{-u-JQha0=Yovp7UmtL;cdH7)JMV5aBG+84Dj}O`Q77{I{k7Qd z+cb*m?cE9FJUf8@I7ZHj37;7pT8R!?Y@3)8z#m`^Cc^()hKrg52WfKP z^+`)rHn`VCImBM5qdSy}?2s)e6)F7PK@d82f;<}w9rBQS6+xp2V=cA2dcLY~p(3%f zTzDfc=5A}_Gv22xr^;yD33P0iw$#~$OM@bFs3D5%1O>sNQ#8S_T{G=kNnp44|dx)Uw4E$|83F@~H zR4%Y>5mZo|?IWmsdv`(Q+q;JnA{3ChU;;|LFi4XSk?#QIi->N=DLC8$GqFL%8d$)! zghAv-f$rNn5IImxMiN}wQElYw8M^_o7~8<=`ao}$qR?>Hd`G3*0sznY~8%Ov;GVlE;(BFXW`5DeqOy<|?z3Bj4F zr}XckPfup`n8eV+zI^#ITy4h50-T)40wZAJ;bMIt?m3%oF^ZYxL{h9n1C&2xc*W$t z4435k=B%>bhc#wvA7;2u+fS|4SgXI6uocG1*MK4+1Qxys00T0+S6GlwCdo8<@*S#%h|xEOk(4tF7(OX!YSPUDofCjnuT?`b~RnS+uYHrfs%*XLnRu zVA~r2Edz8x1YjUwnx#<$wn=CMA(9~+P_KD~UmK9)|j4_B-`E_nb z=!kt~?OEbeoolbNRCF)o!6YNYCH0IHwa;b*lz=Eq4EzQr4g=wMq?%b;jJy}b_#-z( zO-5t^9~hi5i5DSzw{-5hz$z{Yh1YiIwi03M4(Fb0_mPQ5pFVTXYMkn%xSgl3d(DlJ zL>^$m6H5n_1K9aMtq5+u5L_6Z9s&hS3tWQ;GQ5~5X2B5XT1XpAj5x7L*SAX3=UA5e zc++Py_l5D(PhSVm_Bsk?q_NI}AA@<$vt77e%@ojER1DTYjD}JO)nHM%I7Vn^O2|nl z9!Bl$5yD;sytJNIiXL8ZwJt*4h$YTsl{euk^|wzF-xvEhMHB41V&1xbByN!YVLM zAKxQ*>E4<6VOh8=!mS)xcGb5cemI&j=K})V_@2#gduO>%l$F(I?6;$Q335qeu0@zy z)C-6R>={QVmkQTPaN*JDsuu+uU<_8jA(}J>v=+g}t$Z+PqC}4l!GkeoXC-bYjyNu3 zj~N;Zg$4-&T|u{lc0+j@@b8MCFwM|Z&_qBB~2AY?kv<`^U5t3E`|Cod&6WRB4n917X7Tt>dFSp&>1O#JSplC%e zqX^1oV1}UwEhF^VT*)D z3dT013r1S#feMFmhmofNV6&1}8nwEpa@i{f{7~|;IfOTCG$nWF9+Lz$uw74&skx#8e7Rmg`CAgDF~ZWZ3^wNr z3oDdrHJuznPRp4c!Z9Y^DxqM=Pe=GK9E#3LnK;R(}5E5YsQL@{T z6k*}#nD&>7ta&aljmFRB#E+lJ#lz`+6dmn|x5G;{i_*oR~iWky=u>Q}KY?(DS?X42$zw>0^AT1~bqpWqe`5rn;W|VTCZ;w- zn7KH)rtLZv=TrOS+PknH{jxdoGn!m|e=3C9`O@~8-0tyMBZNjD#GwrW6ewg!L&dxy zEx-h{EK0sG%LcDqWDtaDfUM6DEO9XaW1Z0S6E`RUx0pzqwI6qKy>f!f&uDU+6Qcb+ z=GNb1YP-jQY(VYU2)C!PP6^InCQN|O>SJ#Ovy=~g7HF=Pjz16q06#Hm@(dV-vE{(y zWyWqX4upWW?mCaTY|9$v-*RT#)o4^bAVeFFfm}kfkl>No@>*!Ui))26C^YMC_m=$4 z8s!``XHxUebRO0!+w_X}mlCRD(c8YFluixDF(V@?ILOMQwIOe+ddVtn(1d`F;mlk9 zyS+)O{1C2Z(pixP91BI6a6U7l!H=@%)Erla5h*e|Q%6v;smzLBB9-h6=25A%q-C;7 zs~|>O-aPY>`2QILExMm9@Dmo)C zJePUX83d9dE7dpBuk-FAV- zLYS_PcTc)Po)jv+lhBQIAosZ-GSH0xKz%M~UvWjl77NHVAd+~J+hBAGfZp%{fAx}hA|W{AXX%ikvJ*9ILV+2^uz_yrsb1T79!0vMn0MeZ!LH! zJhL=yj%Gggw3T2M_nx-)gLm?ExEy_5Qql1+TC8|Xm*SkQ{i$@UI>Zd5`7N1Z;^+Ef z^%8Z><4Tqo#$l9h1hFo8i*Q^mlA94xnv23zF7%o5K`})*^(&5x-tmX49#?*kE8*S~ z1#N~RP32X|HJE=-p>aAZC`6j8?WgJ<)ooPDpkxCh8Bv8O>ZJ+RZQ1j*e@pmlqljXd zxn6DiI_&+CE0rW%8KB@6oB66z>dJ?mh`!=oDIMR2g&I$kyAP@QI4gufoRpSYpW>vb zT;>1e?VPBR_6Ut8(R$HLyIc9W>sbaF8n6&iZHIRVNxGJa2%XNb=Eob)u1h>}Nsly> z<61jop;o6*=sRLf+7SwE!=JSpw#nMHcWpK7iVb(~VW36~gaw*h&yT$ro1X#ic%+ll z0dA)Nc}$3bA|?ggEEqd*Fo>;EGq7zYC1yU@t-8C{>9r$HRo-N1gR?>%%qyvzMlier z^gT>`IJIDB!L-p6s|{JScDR1Fa2$7#@js2|w7a`bHpwt$;TKzU{coi`+F+P5uce68 zVOodk9?f;>Xt%sWGd$>eA?xM^4*{}pQ{GR}|MCv3+L7p@swhsPB2W$ow}vn%fl*TH z!vm3vLiK55+TvEdtPzGONCSAX6=T8hfSv9u*imSYdl&R10D!3aUnZP!!hsQN*35*E za@q?vQq=kP32wj7Pf}#!mc0L*&_b;Y57>gpI>3gLC`Dv82449xA4a4wb8Q}H{xE#Q zoclEo2opoxJv_)*8N|yJh-V3|A*+F<4-mFduIr3DFro|`RQHdW$e{O3yqCt4Oh&q- z!XnRyz3#_>B8BU>+BX&IxKh=f6wo`$kXpCb+QFFP8^j}rN~T*6iH2tm2aRWgxU(lZ zR8FT6KmKmp+lE{keJ8cL(FtjBPm`y9wKtkLO}BS1_XnahVNoR^D)`~Mf+Gv*GPVVj z>A=SUkm?rV55Rl#kkmlfZKfZjrmtHG@a$O=&O=h!p6`_pg|>u9LI!9r%X7+fL?KY9 z4G#e6Rs_69)VF~0ol+#V*h9i?RF~rnSST?1ST*Kus78(>tyeXYU^Ug{<6|)YejFD?toQshi|sEZ$2-gTjs; z^!~6$WScx*qDG%j=G?uEh%k-n5FQF&I5ga94gktxLaK_{_FA0gQP#Ua_Kt2%ChA`C zZmy~k7js<}D8$@sMuPKa`3>S+6xjkXN`Io&c#|1uyIvu`G=XvvsN`LF7EafFH9qtM zP9x=6oG{Uhc)=E^`MZN3BqW5ums$$oE`pGnu&Y);IPC7z7Wq3j0^c3v@x^=izb%F{ z&!$j2XfW`iJZN+A-j@Wc0HT;J_igL#Cp@4ET45xJX&b~PX>R6<9IzQhR`Y$A65gAn zLji06faYdRnICvhG+qmcBqT)>R0V+@%z>jdANPAd-u8QIWO9@+tBWsqw>5cL?YSK- zh%RZd+6>4%ChUdghmR#L0_2O(+kjw2;B)Z&el~kjHEKf3=G&n82B$RBqB|M_*-N zfRbkz+K7^@@c`fP-N89$l8|;v;2BIJMs5on6SKP{^Y<%E5{byda*o`bJP`i@EzjSO zmMbW#z9i#_2-VOmUJG>??vJH}HP7<8g}0vcF!iPk)QUVM7#E?!@r}~tKlqg7UZc>( zsAB|7k2#($89heipVO*lXck=ft|tdaspp8H1X=`(7w8d;b7jSWECK;fKE~LR{S}u56!e1y4z;OQ%0-?^6x|lIB?*LL)_wVhC zISHas?E8n=9zMYM4Du&W2an9IMjCbRlK^oo01qaMFj!MCtlm3gbO1Cr+%8yJ%|OdW zTMByNEW9?_GSJJncFzq?P~aT-@U~cRm;gvMh*7}fz|ZWVn#eJrlL6@ho@oPBVgVso z0l>;ZfEe(vNbD&>X*p2Lpy!o!GxtFigibjpz#;}B!$%l9HUsn+7+OGMkL}p7kjHF3 zphUY3(6y?ZJU$x`xPXR!lcYG^CB^A3DcxydQAP@y;RqQ6h|lYA%K52pLt#fQL5S=S zWR8O*O4E?=6M`TrMPvy8DiI3*QE3R7o z?vm2ojhlJ_P#qlDQM!)=BrODR zyHS!9nDmbSxRL@ecJ0?GDZ5R}V6w=VSoqC2Pq!9k-LF+Yu9FYqRt-(Nv1D?3_lDXl zC5&&;UCDVHF-9?)8^RJIz~L=sG1+DrWHDzc6I`9J&!BmZNVzGxeMwOsDSHU-XT^CW z?6c)Ie|qQpv!Sf*hj@3vc%y_C(J`3k*q08@?4arIh_nucsvZi2@N89%hMCJn2g>w)8cV^8AX2 z09{~YMmca{UKja{S}jBg}3O`S*mXM}hAQd0Tw| z&kwsvV$ld5I)PHmQ<<@A;o?hC#7xhANEla{=a?eU`1O-W&7#IGgp`QJGLH;NJ|T_M zf*lckHqWmz9)+OL2lu%RK$ZyXbG3N*;($4^K=wQf@!t`Hwlm22n6AQ5A4R|;fsNlw zR#EpTMa6P@Y>Vk=vQSiSi^FUm?O-LF^R#@^T67>e$%ktSH;57 zv>a@&;N<$c{nmf@JH!7wJ^=pV?-aU4E?uX-i+^|cZRlu>mUKAF*;{U%z2&yMO&L&T zVv?GGqR~-&sXJg~3S$@lMXBjV95^Y=h`_ZY2U+T{Bs|m==}8nLQPBd1O;K(g&oZl! zo8|*8g%XnCliYqgxz!rIAC|qh+}eA~t-S{Ty=Q;|DM>+ujNBvSKmwS-L|PJx>N%Kc zX(^bIbchJ>Ss=z{in<0Mz@y|j2>qgkYa#-wRm!9uEVtT|+{zX2lt(;)s|2sLU6|T0{8k z=>V`qr0g#@(`Z;5r!9WU(5~X}Uj{_dqTV7*I%+R(y((T@8K~+j_;ztObYcbyVryh0 z0zWD&8%j-@alR1(5@cfG*#((P8)=CJgn%!H4*?q_WeK_> zO7_|@D!^5jpO*h1N$8|_mm&s(%5tUfkTN-8@MO>D9wk&mU?OHcUMfpV{@Is(W@kmP z+_2?~uRWZI2(TK^t?8GM9djIS9H6tPwR!e>LBsuaL@t0!w0Xlxb9B%0x#-H#`TMu0 zClw>^4Nw;a7x$LqYHM<`>)^9MXqr(fGg)0?6r?2ZS|PI-b~&h%<>&KamQBFk91!XA z4G)f>4LD>=Q<|p~Tb^^VK3TS8C709Ho=JgvE?SJY!wFmV0ts+q(KD7N8HT6En6g_q zK>G6g4h569%#)t>et-G?9k=@C>in%Nx>xf~J(svtXpI5z3$7X=|3S2vEtf?s{;HgD zO_9i{?merqiKNsb72}Xv9f(_>lflkucpDW7DvL*15t9C#IQn0|ra2VmVoRf(z zG8s9md&(Q{G8Z#n1}tRMp7^949g}&HgRHgZKOc)a*UfDI>|jqLRKOTQHA|3+9(f3LA)W!_ z*+xkfk;szT7YAMu&Op~eI;&M{ZytuB>XxGwrmgu}ipZ>IfHE3g@xjT7`mO-C2fC3y zF5%_n#p&@G)F`k~?x4KvUY@%Xmbzc3WPBS{(llCB$=Z+lRu&Mi7QGSKeV^5$?Xz0C zLkPgN=gwFVB8>e|GV_Cwo$i z>q@32FR)8oe5kC&GMvXuTH3|1`-bIg9#T@g;XjW~eP+9Q+%2 zC~j_hj6OFKgvQK+M?KGd3GS2pc(W<<>tX=JCvq25YV(}qnIojdujmo*BJ*oxS^$}s zBApw*W&M#nwadLfzxNDj5o8lmiP?HtLi~G4}X|JbYzOPLM|caiDIf#EY&?G_oWZzBo0B3C_n;5>O>?F z1GnYa`PmxfN%m$U@=VM)NWKk6V^u3H7zisobo?vJ=yFR2IZq-QrA!7y@^k!|65eCQ zNKC7n2ogE(n5}WjhzC3#gswV%{ocvd`76Y*QD{dP<3yMUF%+%}+|cJLzU-S6)9)8S zGV-EFyd`QcAnw6~gLku)i1KIdU73rrn57ACX2>FWu^pyt1%(UZ4H5Ysk#hKO@%$vA zgnjk(jB`HZ+ynxQd6}T)|A>^Mo`CMNa@hge*ftmKX=(c zBi5vKV_dF|j;}78v~65||KUWm*2jylUwuRk0Ps{mfIsX!t^Svls4XtfKYu(Rht^4_`bw8hhwwgp%Y9J@i zCq@R~dTBVFwE#6VR*<0D9T1*2Y7cx=UxlV^xR#zcz}y9iQw(yY^7*V$s@~%+XnjEb z*rx^kw4i?u=X3jV?tVUN-OuMe7{gJt1Re`Luv{+%(o_%=y$VRr94u}E*9H6{SXA5) z%i05gb_yz7-2n}Yi$b~-k;U@)Y@u%MX*oYF=U?yntotns8USxy7c_3|x}b4ucj8Rn zPXir1)h@E*QM!moOd3CkEQSJcHE>ZgGQjk7>KBE}g zGFKqb%%+Jv;-{5)TiPC zggGP7swP0Mb!-P|*>+$9H4IDEGIfyQ>Uza~_FT<*TFy_)`PXnhw+Dzvv!RR+Aj0+I zdXE24|5mpuoxSCTT4-kbe$dRKy<(8~J)*t?5!C>#Z6U`U;XB|D1sjA2DGVeoP)HY* z*bG*Tt5H*0d&?&cb!5Qe8!2wD^-M z$5nnX<=y6!&jrjfbMaEMOECr<(3OedBZ?0q+&NhuaAI`+aq%MAh~%(c_I{7p$_6oK ze*7KbY_|f<`nhO4FfrCp;+k#8BG=;)Z9p| zrx`Z24B@RdGADpPOpz<#CPaa$-`uY1X8$1TBUKpmR6mDXAS@z=TaA#$k7RDOh68e^(0*+aX*(RpBr6B1bwm9SoWZ<&F@=ifu zO?uBhjRuqD4Ec@5Ok;6+Nkh2hLc%GM&r1TUKLQp63Lqlumeg`$cYTg z0@w9G#1LG^l+x_Xxr#v*xrovh;w-_35E#XgfEj#+n(Ktg!Jk1+keeUS%q`_j+q2|3 z`bu3KiP@Ft#wd=MQsv3fI9yMpxE(>nma_VRn&QF<8lB5bTv*1l{UFlo(bZDQ<-pK`IEKee zNvv2;BJV=oCECntdALReRXK8=RKf_?cgROn*2ri|Z~enMS<`2IocG=#->A z#-D}`SsK(m!eO`(96i)A2Fa5j%bbjb)E#~&j+nc_5*r0AotNWv0?_Bxty=tFfZ`Yr zZXwZ2k>|8p%*IRlYRYiAU1Nx7G_WTnlAuz4^?0+u*2VKAk4pxdP`HtO$^0YsXkPNi zwQ1D@+Ekp=;UnsBt3LKV?+_! zw3hws@c)#ACr3YEg5DinVoOSHM|tp-T=5z$C!FK!uUU>0?_bExS~2PdjYjqyDV!my zT8d|XjGl-+E-oCvfdJwG&xD{WJ-YZ_wD)?nBA}NKvP6cCaCshgQUmU_;@f5Ez1|NWG=g8a3H{GRH=dlw@?XZY{dbNuHpQ+hk(?NpxH zvlOZNd|M?_cK1VAn4}{8FfOL7ky`gciynWi^!R#jLx(53`x0Yh;D8ua_W-L%HAS|Q zgkZ4nOveGyU5L^l2X2NCEEb3qq!CCYB0r20A4NrRU__0^GD_!3e*bpzyT6Nfw+QR^7GeF~BHZ2o3&10g;X#6dWSauy zHX=5=*wK>|<(xK^bcXoZgK-4p$HXUGq{t##i7;{y$}Neo=IM@KEmnIH;h$85_YONU zIp9FJf2?MDW}tm)_fshN$$LpNKiM5~27yP<3_Kf=M%!{6j0EV_bx=B|b_lY-D0Q9C zNrBI-ft=kMAQMZT7Wpaj_OHZg{8ikR5T02tJ1tZ z3G&Zfv*;+x`n_dYzqc%RS22j`4@BmMYXHaEMCvs#=u!|Cxkidi?Etu1j^T$MD$f!? zfdxck7$cwyrYhW=cxhF|0A%BnEdQ)kjNJfWz+|42OH|eTj>%_6aV}g4QLZ>5q@pMX z4A$|1DiF5^yo1)rs@Vh>1}EOJcmZ5`97!n0Sv}x55SrE+&Mih;>i0~o2bUh4ms>xW z^oWPV$WC>c0w|bB05QM>Xaw-7Wi5nQ$B#ypf0YGFMfp%rit5)DN1T?u4A+^uzJ+uy zeMEMbnZM_L-_^@F@3C|IYZVOWGr2waD5js6XFz&_|hfN-N(JlXfc}0Z@ikUzj8p}W=-(Zr(ab^==LAZ&P8ue zV`3JPXE?znx0V1q$Z7U8}kh`KF9K&b3kWk(ffZ<@K{6H@4 z76hh*^F3Yl>`Q)oqi+T;k|d^*7ZQR+qsU``(EO5z>H=>A zs2aiKXV*{=JDL{WCbt_zFjfJL<1xUuDO`d$;Ej6SheU9!NvWk8Udj;^9AU|A=p5*X2?W39mrH^eFI{GKwh+UGj{<+JD5$5KKdh%y5*zhK1vVaL0W@k6 z5yf~t79pag0`kKIPAf5pqdA2io5K(?)YQL>1V5V)<5T1w@c1;uAIc<6rMlw$zAA&p z?F^_YJOEyX*%V*5M#=r^kk1S`huMkIAxN>al|6$NyW4ApQ=_MQ>hTJk8vXh^8l+@0 zWc-Ql(By@FH36$xz!cwwtEM|0NTp;$^@_98#wykE*uAD>w;2^@k5;<->rCWiItK8_ zg8=+1u=-Gf>_@Q{C13(|!3PRNsRYo^cItqKC^B@giNfU^M9`X3Ei5Y2rU4>x+uI8+ z$K9<=n~3o&j3XU2dZrhma>uknBL&K{4bHPfOClibqcAV@5va2w zD>h0JM}-28{kQ;4pxV*@1xs9`bHK45MdIv7k+|;2;jZNeaLIZ)Sf;cDaQ3M4)STGx z0uu~-a3z3Zn98~x7X(_GXT~lt!z08l>;x=)6ag+s9Dw#T&wFf%)17}A64!3YBX5fO z{AD(ahe%kVEST|TI3?)g@kZG0t+!OIb+LVL$O4O;){6|QWW4z&kLjM*FNiua2nO@0Y6|ixH0Vvz)tsgklJ+ES!Z#&7{TBY`7 zW*g@N5-;kK)2w=&zdh__L_e?`DE%oGa9s-STHr8q3aMl}!b~xqlg!$s+*^8U#)CV{ zA&Y-v>qfwRufNJf>8T>Q9g-oHBO*fw)+3Uc7rj${16hvrNs2+15wJ0&^j69R%>c33 z#92{xSiA_nFvi$1tD)kn*#ad#D(0e+xySTPL$-(nL=vSKzfh0y7mBQ0MUJfCl5S^Y zzzL#gepIkd^%ZYefT4uprjmPUK>23&givcDbC%%xIc=H$Rpd33&w@e75>9~Zlf6XT zXq>f`n?IQ}oGe9h8>cEt#&61SqXahvlKlb(IZt73Hksl|B{*{10`I%uyEu8pH(vhm zdOO0)L?+thnYfAeD;vU3YU1;P@uHVY3*Q2G(d)X(M<$%aQ8rp-G+Yulj^(JQ*M5%n2vmi^7bm z)hmD8zKj-eKiiSEf-BY`2|nBV0M&2s^Qku%CvLggAUSsX(Mz>*#18_ha7GE5vJ9ly zBa!(`oyrwllp5#Nz<<*wy8YZQ*|kiF#RAUBB5yo2Ch3g6dzle9@cuGnzYvBO!oQj= zw@t6M7qbmVF%#Ckh{aUOp?qwcFMbnH=Ie%DMouuXC)RXPbWU!3JTAoT&3JgJ+y#=c zBymhvf=QyshcHP)zGu7y&}ArV^Cb6KL3XX_L*(jJ>%wU$u?p8^+`^edA{ks z`{3)7+5qOb!%QdWim+dKaDR;bY7>k!C&2)Bg3ZYmD*SaZG^^h!jR=ip9-=Y_9qMBT z5Qkv5yT|LqMVD`{-XijvAdg#>xNB14>TRH%&vmwXka(D&q7~^YiEEmv6MAt7kB*0^ zD3T1F*b9IX9wbJDJQd#siBbami~!~i$iPx~go;*=VH+^iE$7iu_KQ5@lpzxvi2)1% z(+q*H1wLS0fWZ|dU<;1!&j~C`VY7H=Bf9anlC%NIxLrU0M$}8*H=|yv*lG$D^}kiG zLLeaK09k|{AmR(%J zAh3;oV9&be01u?A6St1@x;~K;8X|DGJ4#U5oi&OI}5jbAa>~uOgUt z{Wh=SE2>d%a41T$I(WMTayr?96!#WhE&_y?sTyou37GR5%>M0r*e6KVe8CCH4Y6%a zuP8LB@?pxBR-;En*iN@IVYrFHXOvQlj27_#zeN~X;#I|rROKo? z{OQ{`5;ICe;X%Y`A@OMXnap^JCZSBk+@~=iyYvmh2}o%1S!MVn=dA1Qs}oh+W9jeP zP8jUqOPO}U{g&=Oi4d@TyZR{UL920G5O?2woOpq9;(bw*GyZux@%oJ}Jp3F7pP%;_ zWSRsAT&b^k9K# z2v3%vuE@PRb6j!b@XgUKU641mbqn;DlEAm`XZ*@=v{>E71c_7nWPt%vKs8`!k`W`# z@YGPnKiq^NA+=OymD0M3Ln|`9_8Qs3*JPoXY#%7MC^ppqyFn0hCA#_$@fm~}vHgU2 zon%f~=}pY{R>VxQP)+H$5Q7(RRvUa^<-?>hZ$jP{vm45Oq}Nu?))zI$Ej2^P2tsz$ zK}E_%GG>GyisTSO3)8sP3>V`sRQr}nv;r`HK*tfA!5PvxMUJNGPkuB;rC$q!{syeY zxjTra1Z#HrTOK3j%Pz}S-IOS)q85fiBM@x8{It+?*rUGnWXCLf_i*GMs|6twVsGfL z{?@&F1=;FfQHQRC|Fpr;Tieby~Cc`M;sw>ucrr6;<(b0efiMW{y9?I^ZgO29)_iwP)nWV5))&x>Nuj8K{AA#VlP z<$9=7wYW@E?a_zx3;C?#Dn*!W+VxXeR3M^!3rKkys?bJwpD+Z3IsmGBUxkzS;!Q>0@Pt=GP#S)oK zo~u8ySnB0^?csCp0 zt&)g&M!&Lsu&w&5{#J7CF~N@JwK(^zX6N45OVMJ9VUtZ1kzZKy>&w)RWu?aQ6bbNWh`9-g+*u)TW|q$Sh%{UG{dh1y*eRQe_x$aTp)~sZE$z zoO;swpm@FXo{1i04wY@E`5!K zZn7*(45e^2Y}mIYiw+z62j7@L>Lk|D(M^`VlItAVSNz!mhIR=Yw=#tuAbBnrzMOA!cPlKxTaBW3Z(c;mE&JH8UFm@Y3XeC!lQasiTqb1|Nx|d!X zMoVsg?fu;~O#)&`Eo83*q+segV1#sE^CN)FBY{8gfFj_7a?u6Kg#pNlBz6o9_yE9t z^MGJf%HlVeNurX@Vm?UIw8{n$3Z?*p6d)%6)Nqjz#c>Q(!t=pG4!&`_uI#_<^sXPo zpHumWKD3)8N#9eF^gShM4~{=SvRp0nfbDNuiIe)N<;PIHe9uR1gr|XC#6@}s5E^VK zhH<2uCaU3({}hKBU{ZpDB%%07w{?3jL9%x<#B2I5N03_O!D^BRZBKd7_LK*$JEv5X z>exkmfZ_lq5_FNaAHtxu+#u1BF_##qRfFYfxPfDPChP|nD68PA@KGfMpgy-O51QfX zk0=jijr12J54&~sugP>VTH>8hQz*c{oGrJ?dd-hTZC$rYn{G*4x5&(*rBtV_Q=`34 zXtnqLvaGUie`T}+s8JOCs)pIOZE%!55=HxTzsUF&00Fp}Ftlfa!5v!4O@@PTkkhIK zm@sJul9_s)0`Zy2oU43Hoe#Qlm;cN{+_N%m=FQ^{R5JLy;!!JAFnEBW!=g$cX^mQDOZ-+L2KibY}x435cRGyuwKT^#KSLJJ=B82#&kN4!{ExYJ^|pVxzI3fki}z ziySVCxO~zgbgtn6RmrMmIy!Bepw+qwW-B?j3A!OvKo7J+wg;Tl)PbKn3QeG#5$#Zr z>dg+5TuxTDEjA3|Pp(}xr#^9QiU5S~m^=vSYxE=Q`%0g_uk>}-$aF7`LAM%(h8-eY z;n}(yd#J^YEuw*oQ#T^kRnROumKM5Z5~T)-B|va}EsZR{RG|x(o>x}wi7Bp8_=r@i zS@>|u-~=v_Mm?(V?Zn3FH)3QxvbZ#3&+aQO+P>n_U139>HMSzt)*~cmj|c>b5n>UDh=Eej0px&>IkO`G!Ij0uux>H~D(wa(j+%QRu&lz_>~4u841kNV#OEZTX`F^Od+Eqg;`lnaNgVO2s5gwj$M3`O~UI4rf>O8sIs(K$*a# zLH(<_p{wrqnrGl$awujX)7&~~eoYVW=aXw{r48=rJowrZ&y~{~lt}&VhW?}%G=Uvq zvQSWpHF>C>k>Ey6AV)wA*N5INg_T6OqO%5olgrloyV>*=wg5oN^{*sA9=;-fN8PbO z?;gl8Y1B9P-HqgwrR8=lmRmDJW$pc)PY~eA*p3G9@c=~$I3UcxqUtwrMEii|bU`r< z7?8vRRi$M*k>-b#Pys|-9atg=Ar|p)T_BO7GQrr3rqb|OOD{ry%&6#E?u8@fS{wsTH)$0cnD zViuVl%iH(2sfjR+mYRtA(eGLlvA^pTtI+MR3f(<&H#Qs>b3!T_bGmg9K_ z7E%cF(+Vx=Jn?Nai4$WLc)Klvq9o z2S*`{S^g|leo(M?<<~J3_@1Z?L^=7UGlZ?NoQDDMSce+nKzg z?+&n(8f?g7X>xYAEu3qkF$ZDSW8ITl93N@Xi4SlI*V2(n4SzTISOU#*P!eyapt6W` z&rFjT8ULDT1`b@AD3=FhstZiWV%WuomaOT) zJE&rSZV`7)Q+;&0ik(Mc>n`~-N}|b$wBIPv+WjP2yPrgN_sydc#Rq(YnWnntyKbmQ z#8(&qU>drmK}j}NVtPO$N)63Q0XZE**io+%rjZj`npKi$WO-TMqf4~?B+-8>iS`=v z!~A+aqIJK!mm&bu765%v8bB$At_EuZiDOjxq9>u|g>D4X!$<*K0&w2c2gg>5 zLIWd+bs%iH5V5KaPUQNL(0lOpk+XIZzWy&*F-9g?l^f zorqdhsvxEwfzbni2k&_L@neoN9h4)m3ZU)xq_U5s{hR^S!~Zce(9Tbnw@N!m*g%ZE zx#NMCY?z1w2O77Gn4XOA_D7E@HSgj+Wym2qm^CLKVO*Nt1S>$jUhw-!vKt23cqXte zD1nTDF!(E^WFp6ZK>*VLJH=q-<`7i~KS^M*Xg%qQx@#y*Ao`-PPSD2 zqkD!O8ye|Q5}*ye-F)y}Lx4LYfAVehPR^Euk=%B2-Ii8VxUYMDH*`zz3mdTs?!U;1 zVKgB>)&-3=YDH-2mP-VI<>EiR?ZhT; zXUmCQ-uvk+4pR^O^ihPugv54j!wH-SoXENbO#{bx@PHG$Q`X2gI28qHAW%l+G_GbC z5M(Qe9#|RN3(qg&fHQrxSo#Hi1B2$E@^1x&&LQ1wyz5>T)%;0Gf;EU7%C0RTR zg~69EK|(myXKwq#UN*G?-9Zruaj zaX^r44ba)hj1VM;7X=0&B62{?w17rynUHRJvqp{9v#q>e)2W3jJO{wjfXM}k6R;rAPY{jSwi$wZEDfxhv#t9vwvUa5 zT*rlO85WN|MJE0CCd3*!mXwt_I#vN2 zMOzM^G6WOH{F1jL021;8{L-uUC+|^sjALU+tdtpJ$qiA6E`Yzcyq(SG#IZWuo+^cMEEw+w=Foi z@p@NoU9I@dO{a>v-?W(fO-pyI6kz|Ov?sD`e5D1(0A$qTvpqBMC>Ja6fq@*N@HO^f zB3ZyC1Zz2RC3PG%egv+_^xIF1RrYT_#h?sZ)1_N}Bj)0s3fvM{bSgzdJrM{f6_r)EstH0vp9xl^rEHI# zlXTA(l0_hg@)H88x`2EMoXyWt{xB@*R{|(#kmLRM7kB33J&OYi7Yw*<(CaC~WS&axKTftfqmz^;8#V0G2;|y;F z+J^A4XZGf_Gz^&&Ldsk$oxF@cTf=YEsDkYPVyXOG_S7zUsVr}I>SUl8#+`>1sqLhh zH9fqiceb&awyF^EP`M%mSAVCzycX&5a~Jcr1JdQ^Hv6^->9V|oT{i(-R&VT15rKFq zRTU?I3WT2Fr$B5op#(bMga~6KvckxP3J4+=K)E0l6?m6OiP3=Xg7|CV7wLdOuL7i( z@-at|W=YV!I*uv18kbeA9LVJGHzBwIZIb1AE5NTfi6YX}nSrCk&;zu-~qcFc{=2?z-GvN^LaBEuE`!EuEY;C&SzI2CiyS(mQT0!6EQ{vf*8( z>Uz!Yh93>tdXjr>wuspVt!ZLdMPAsy4Zw}j!6rb_fNvFu;FNg2M`Kg}*qLyRmVXcg zs(rP=l3Q*}mM?<}oO1xwW zy#o{w)7}X@CesSs@bd0=1PUF~k3&A6>dWzReOb=2a%YR>;e;A*N^=BI=FrjYT?Kii zTN?K6ZDaM`$^1{%VuCI^!8NtfQee=zjqXNg2vQ)__~@SR#;|k}u)T-2=Ao23wUP8l zX<~YaOd4#pFe7Yi{Q-Ej0EcOLA&@GHNi{eXeBIj1y4i)fBR9L;whurIP;%=r_Igww zfuP8b6Fp8nL>o;DG(k1ne%G=42FOd;&~bPgP4G+pLnX$4sJQHrfMxhGvRzZigKLAM zoqXSx$LX?we{)eU=A-fpg)5p7c+e6^VxTV|OG`_Ar&O%z1DV7vXB4)fGqvK9R1VEO z1f_lQG^*hlEN@5Ys$mKryyzdi;=3<*4L*N(^x!&5Ex_`Hh|5D`GPT6lli0HHw~P2U zxeIhdw~#EVff&rjL_)KI`y(z*Atu=Pc}?N&-~)Dfoc9x$jfDHO917Pzh5ew5fJZkU zjM#l|B{%^H1;cC6-GE48H0W+CL=YG%^E7L`6sM4$0ez_bTLv*uaSIMwiY%+e;kIi< zT@6tVfF1~wIeSRm+%I`K2tfK&T4BNLAt8!RJx(g@e$v3M4bphR_tC z3@$e8e4vsLn7Q=t-~_r8k!K3f3J(c|@dK_>IygW;S6cteZFof)_e-J@ipFb>3Nx*Q zpoA8oV@??8i>C(sk&L3&?WQ-f@{~U+oeWAOzX`o1q-B6XsPwn| z92^|TEIGIVL~@#?^}c_J+bZ>HWC3`N+b&6@dd~>?Onf|Dd>K$`8xOq%IK!Ovo*l1OeV(qqz~Ak| z!$AVxm!P-wD31?aq{rc`z@>M5%lRx8G$Tai(HHk!V&g#{QEoKoy%}M}O~rMDbHP51 zizf?8f*|Fck(VLa@Rcd}4nb)Y2%pjtF?E6nMphjL7LRfqeIVeEmE6hx9i|`x!|Y*! z-17H8>gWkV6C@B|tr(LqgeeLO$05y?xUJFHd^s!Zj0aj&qA zW~@C}6RcjV|COr`qpwQ4I87xOe<2c}FUc57IQcH;+gpbdW@q7t1;06Y4Qa{HBQI#y zDKXChSZu&PRJi54-izn=NSI(j z5b@bX|5CE^HKP;DJDcpT`>w8?W?7ek>|~qmF(Y05Yn_^GMPPYNScA*5xz{KI4Zb&P zsP8i13~jghCYsqF1a-F(&<={)>G|$Sl@YvM2>PX9`LGfwXDIWw6R2}$6lh4MgqD=z zf87K*DJII*Yz>7!sB{RBK53vHqL_E$X;xW*YqQo2f0wl;)SJ8pN4cJG5c*bxE}@DD z0CQ1FE=2?_Z7->@*3OUKfwQ!ZkhPws^$#CUQQ&a)4w;v4-(Fv!_5s%~8z@`5TW*k- zCJ<%aeVDxH?!)9o?~d)kg)(8BYHkEe9+68sv;z2Yb?Eq-76k#&&`cd9*(i2QJ>&%i zt`W!J=ZFCH5je)TloyLM{!&Q!7cDPMR=(~$P+pt|%1igWwiEzAMWnqfR5yDrHSwC1|XHZ=?w@Ly02U-eM#2_TX@TTzIf zY&!b>rN}}I1Ys;`=#+5|TSNt<*|wrSehCEn1AkP;(E{mWCSMT>p@+~Nm43X9!!zmQ z>GV&tjfhyY{Csynw2AmVSP61lH8EePI}$S+tz?$1bSGqWPhvKJv$w0q*x`v3;X^go14!VWnr>4TzQ1i{t zgu<+rMmjoukc~zLy8#Q8jnjcxb@>W4C3H3|3w&8Z!i+_@y@~9!Nxj{BAtrGx45np3wjv@+gwQe-tiflorJo4A5wj-(9xl6hnFw1H|ABtI?7xE%Cxn8H*F| z65tr3V#IpHY8X)=On$p&w)31&enevt#e(Vk8-$&WhzvT&IxLpxh7EVZv1WC)6yD#MO60z|< zz7_yK>_eN)g~N{hVi+JT@a6ShNY1uGa)G*b0W_ZRqKmDbzjK$Z^d%RB$8R~ z;o0elxUeLur)t;5lztt@Qv5}k6Faq*@Mz&k;!>_;!h0hj7r}YZd%FfkDxGagj5A(# zI<~S?T1eNO*!%gg7soh{?NT1~XRB?sGfk1KIf9zJ6oQebY*TmgB0Lwndfo+?F@NDp znO~VT3!)2~Br8P=hu{u-uDR?e#IAcwi)TGm^w7EXep_ngHi&s#c&3QbEAs{}Xo%QF zPB&AH6ZVcVxpRMu`Cnxt^c`&!oPkF6Lcog57ZPBqfS~VZ%Ij?pG8te>>k;j6%OFi; z&4k*Fq%7eji+I2)Kti?9XFS}Bp92#%+XfVbv=h1+sTD${%{4%oi=ZgyiU|CU`BJ;z zSt@j)+`b`pp<)zc8)=Z@FTp)6;_7BMi|A%=~Mj+jD!QO8n&LSzDLlNKYAFy~kBOT$KWlxA%e<$`r^_hnUF!J+r-|$7T zjr&J0YGmbs!HYtRKSAsQ^({N;i|j5%WqKohk-dj+vEnDX*6lr9i*=88&pn9|kw66v zXhOUMHB3Gz(hOipdjYWYOb5kJ1~NMk($JA{2JfAPhqn1gm>JBk&k~u!|=tWeI6!1WrEPbYj45!GWECOAw%8F=h}| zjE>{+@xWT;F@J9=`fpXnQ_W^9N8QJ{1Up8JR07Qme z@fKFvDbj>M<@gL;5U?(}4%`P`DDg`4fDTI3eam`W z>Tj{q^AMayYMVNe)$Ig(BBgWNM(V}^;@r?Xy%s>PA{D{Qw;xZhfR_1Y`)}7rC+BDH z3V)+y;Ogx1GrUYs7EVWa(c*}g3|&+fP47|?ce3>3zX z7zQ5FW;T3}){vOJZ^u7kH7;v!|7Gp%zpT3#1z>#yb`nr=4;1ATY#tso8dUoj$oByF zgztih1c(zMumeEo2%bh{RwN)%bahgF($ca9B!yGS5_<5mE@MSME$gRc{kK@wX5+GU z_g~iT{>yp~R<8uKdU2FmsOiJ9#R2RA>_^8ns`UwNKMH-Y_2?SFE+Y-KK4E}r@5GDz zB-X$w^wTVB>uFg(E$g3nS$9CFy9KHwCT1NbIcU1=#gYZeiO;&(Vu3nLtXpe=a&zU3 zI!fjUrc`7F%Zf^?dS4dkD6A5Sc_CbL#gA30@o`mF)^}+Dksqqu3IFSx)!(bM6}oW9 zSHRtq{dyz_kFsE7{aqCQmdE}n*+e?$ba3@ zz#4T#xLfA=DH`5n(aCz``Qsnoyg5{o+rj8AJySKeMXZAs#AXvO;UwD`2>A?$;LzGt#-Uq}hKa3S*uZkp z3dXlx=wPU5#o;U7**z;iB6hq$!+$ITrO-Hvb)fNCvE?MX?|2E7QiJ3V$8?Z_Vbihz zL6!t5QkyNqjf*a8#|Abw9kzRLSvb47EVN=cBnH)c0s|E**$~rKMgAEJfcrZ%@rKPC(p6fmOQZ%2I;?q)m zbfc57UY3YPkpK_FHDqw{Wxj=xtNc`@!!0OmMFUj#>1Vy34wnp`m)vUa6H-9u!Ms}G z^%}{d2uEhf<}=ENpK&!(_DmkKlCr8f2U2ubCOM9N{4pqlv(|nba#D;6M(YohNIIpM z>GqXE{pW;mN`dA#@Vi7yL-k?V>%pY~TM{{;2LT(ELzK%P9URYM9+1@dHVE&Lx|bWb zTHCGqdsj+^WiY{b1xo}jsTN3lPOAym`eetrX?q*1;HHiF2*=A#piJE`xKwXnSHG?eG-rv1q7BuU?O-lkCNMYb;;DG>fdlI3RI!esMk5Ou!*r@?fEC6T4 zrk#KhFi3#7rnzBahDATChZ<|A%ml%hYK=v+t*6#@?dMJ{6ni7}&h(Hm6=^_Mf-(!1 zib(gp(1a}-)|^`PN5(^IrfH5F(}Wt?f}7?{-*b+;Q(5Ve6@>v2XCP-9#zGue2@u$U zJsNmOvPn%JW%iK)Ex?X^7c31?>^S6M2S{odl+aOuzeOVQ*mJD?Z0A_plbWnyzV@5l z?(5f%Ves}nWbSXge!_iHofmyK$hYS$m@l+g^!k>tc zx|;EbbL-XkVmi+4ULNm+^8Bgp%0wy~G`r7$^ODCFiil>EPM33qceuNnr3ENQtHnny zer`}vwuk-++V8#Q7nEsS(lwPClLMLny$D|l1v9;4!Gp-pE!I<_G^nxE_*hB`xNPCP5t9yJ*uRm`bYNZtK_lbhzxs7 zg2piHsvM8{wG7sa40oJcZ3Iw|DwtgseCl! zN2sH@rK-zi{_PpKTLc-SYM#k#I+G-s%Q=y9Qf*s=600!-%Z+F2SXLCt5Jc9Y;f@ZS zY#^F=gTVSaof45wzAKkSD6+Q#=iq{??+7GJy!B|j;=i~oa>?bT;_nqVn-yFKQosVL zJ|t$AiRON(Q1@r6?9#69xtwt7;Ss-pcKrOuI9l%&DBM`W#Gnz(-P@$mJ;6#ni2>Mg(bW9J2{vDlQ@Y!J=H0 zv*fEQagT0HXPHE2DC(39L|Y+Ctqy#n?+7}x4tQw27_71UfFkIHVzB1eDm33M}B zjF+O|9>t@gsS26EB^|`F@3L-rDQj1mtdpbh;R^l@j^gLaG5wZC8SCm(MdZ;I{%$0 zv?Yk242gmOK^T1j>e|hM3QCX8vN~vJbp+l@vHmW ze-AxCpt5X_epzMBQ`#f2fW`1$C&@QBKYo8n0%?Fa`LG~B9gd6oM|aWp=^Z$?%`SU- zGnsZN6+6UEa8ff9bLxvPRO+yF*q;cTb2E}%w0SeNw z5WO^=2-QJ>?V-BTcAdh04P;2q!hiPKubmx8kj1@*S;;gXhk^?tGCM_($92Gr8lqCb za`3-yR2M2aJ~}`BaD;Hh@%s-avLvm3?C!f`w>kC;4*tn#zU2RMtVU;lW&6io6Z7^T zp^J-P7!Xs%{#!p#4DOagXr(vB<7y7$eYZEPnb9A+2Vol|NZBG{vYtqf5t!mEB^Vv zdij6(zv*A_&;Npd{+TZPC;aojt3Uo({__9G|NY{vwColWyH^R~=y85;^98sscsI3t{b z0fF}rz!_uV{f1B4isT5cyHUXvQj#VisOu(#+CVKiGD_-1=e(0H}98?CwAK-olou1ju7%OO+Ym} zaN+uJ*v5VuTB&b>Sq(+(h!zB>rURs^sZq8=s(WDs#?Zt^37uZ7pF={v190EH$abvV z@Tz*z(~MmFIPzfwg8mfg6b49X4c7oW2)YzIZuEfshAcrr9@*C~k?4&K{PT;qr#Q27 zX$Z}O{|yLxn!^>w{^DfpFHYS9k6>y_LDvk69a@#=xR!>}bFlCrh8SpOh(tA>D=@vF z+#^aHNR_GUmKlQr#ta;#kDnhtGW3dJDF%!L&LxO5NLIG}n7ZZ(u0J=M|1X`h|fW>CU;Jkd1M= zuK1%~;B=e-dJs@`1TjfI)nFmW=+Rj^>IrZUCk#Qd{TY^IIlQv#Q-``%iL1z7mnd&X zjIp*TbqDVyr2y&tOI9=w0_xa6bZo{zUI;~ywf^I5HbI#H;rK~nS1J?0`TzTi)7OZW zkY73j&PDM7X+YvCs_wr)73d!XF~bT9c(Z6lWKG0OD?xZ?Q$&4e+yv%e3jKog4UEMB zJDAD8P0)>``jF}*UpZogyHAWJC-4vj?i(D+jENZqa}K&-$RjmH{3sAm;e3K0fyV!U z052uFfI3Q_Gr;0Ta*j~^APNY_jOK~p@>nW>pF$T8_)e5vB)s?0xbe6!BByXyEVpR2 z81?yjrTVU{72cy@7h%(DNF=NHdg z)V{@ly)gBv-)ofMK!;uKM)p#7?ydh}IUT;0WBL2}%`xv69hASC=k`QdsB57OW8scc zUzGY-B6>^*VnaY|Uotyx86HD~&SoPV)b`p7jxN5p{`7nMo%dz_>G$@{dUdqfByU#V z|D!!3whwi0#Z!d28bZl3b~20TS{IK|zPb8ThbD%RT|3~l-o!1^;mPj)q|lB*XRlj8 zP;xxivK<{Y;D$zZpowMKAp8p{zcq+$xNsc&6{09bL&9s~nx<8(N3l>e?o_dE{D%Pl z%QGyeO1KsPT0o`0SWR@045lH)Ac7c48h)Hc&@F=0aNWnvZ}~RG#0LF8_yu$P5Zw?! zcFw(jU!m!a29hFC4I^zk7C0UQU>;#=V7b?8Q%pOn^&{wTc3rzx(h{@o^ zW|07eO;1wLjo>d0{$M{b0>^af%`>zWfkRRWM%#@2^z+lV+4C~U|s%(UTE^)K0G zRb>rJxY%iqVFNiclGatV&!)vvz9yHPRpB4+rgY}Yjp@4x(D^bFnJJ>A`ZQh(0cp@h zYn0V8x-5PCpdclLHn&4njYr9VvS^lY?ylC;8?4M92h(KMNndSxcvp}Rkoh}Z0=%O?mLyFs%HSO9R*5I{jrFjeArRP-z-Rwg>h( z1NrW7HxZ$*A9`SF0;RAOhq0{#XT&S!zMGbV3=}G`dQARsvD@S}bR>vqE>iOyBk%+Hcet|}inO{-`QM^;>!+=FG+H2gNZz$aS$o=}tUYa? zwTB7>n@GE%A_dS!JRH3t(X)rpO6)ZBd_)^818`mmiV+eZ^5NfI$3v+iQoIsM&nm1v z@M~+P_1J5#RFv?v_Wp&fJ)>pq8IQ8|j7M2}#(rxLWm{(I`;i0QRg;JW7mVD#Wt*x9pl@64QjvxVlPm_y|VH9wDx|@Yp(;n zWCEtUF-lM3G?EPu(y#_|c=`lgPa{{0-2@ESaVkKlADVYid$Rri z?7hj799edzx%a0qN{KK5iVr#u!K!L{UB62CIzV1BcD_IqI&`Cq8iY?gRQhBk zNHqjiU598mtPsbba1C>LdcPLA^y&PiEb>30`Rz|TeiAZcP{r0rx;iKU|CZSIg6e>|HHy6oQAA7+Ce-oBXGj!1DD-b_YQft7B+ z#O$8=alG!~EX+Z^`yzCy>Vu#kY-X7rljDJfx6G{k9Ze}8RentybLEc*t-@c!_hKO~ zf|wEDNvmgQor%Q@i~FYRFQd@`a2nA`%Sg*!&!k7jh#GRy<~~YU6cjg&fTN1qWp?74 zG|vVf5LEqdCSTut@vU!je8~c0z9;5aO|SD8&6j7|>Z#Wsjg0+NWA&2cE)~nG?YtuT z$A2F1m!t2i+vCCOMsV<~x{~(TnMmuc-JtYjo3caGDAcT!%-}$wBvUJ6+<6=B`1tbT z;e7Hde_y-}k)3VKhP%I-6qC`y@bVBXj-cl@e*~;@3Jyr+TSa;E^48GdV8iWd$h`}u zM^!1Sp9Q*8jY>U^yZX)42;3Qb_vAn~-S7aR0A5Z+gz{MYPR5eb#dv7-zNML6l>-|& z#O-S_Ukd%Fm_}nLM)OtGH|Gm;+{zYiA6MR_IE_!1-)4Q}`?}BHSGI4!PgK%!3&<0lTe*c zBM>G&exJu|QlajIszvx)(r|+()p$tzpu)Rx;o)cPZ1`C_+h2YWs;Q!)@CKlZ{3M+J zwG)9DPyH%SDAy%1iv@Tb)HB?ldpMHLod9HN{M)=^O){lNBB>{sN*PD}@=8^jWn zBi-V_Vq52EZmxcPNxHhirS{8J}>;cZh zX0;w8m|txtpUj{hjtTihI=Q*R!~kC!=S{CZzP?ZdOoOJ*gfIt}aQ30%+6MSn( zMf@PH6Mm1LmtBZ+b%?ilD?~;h&e=9|d#XY%%InfBuTXSp*Ejq?y0cvSQ-k#n)`(j2&ywMEzY zkzI^xKJ}#=GWWq0mTrR+&x~FGSoBOeWQakorxH{js`kKqxEJq!5980v=Vnp_$S?tY z#A%i^t|(&v?s_O$@pe-zS=3mRLtqwS3_1r8pf+SQj9wKW-l3Kfc?O}Y;ffcuLrib2 z9y`z@A~|T%J`Dvkbv?GsH83nxni&?H|Iy%;upj7BS*P~U_`fOy&*2!GY=CW-JUG}l zb^NHkqfX9&wFCO5k1y;~aMC_=eVo^ollJvkS5M!nDs5i!gYV5b-P(fAZVpX(!24j+ zoo*IQGsMQP+lF__d-G1&{5|VDzCWHguc8dUqb^W=PRT7N(DIcPGDdNY=wJjQ)D1y= z!oOIQW#mC3nw|_B#A7yqaMcgrzFBAPb(GoR-kc$^`Jt?k*b&I=W>NFY|f|tvV^5Di> z+fX2UXQ*B@CqNcMCexW#Fe4pLXA8Nf_HPTR1d|Mtd26~srq7oKCDkhsrPul`*LJR( zYQ$8l69Sw?#i97M6}?G>2EyI4xowPs%gCBc=%ZV)0phq4^X8U!Y^48;^+ z;;L_nqr>T6t~N9^hP%_R*FwoyL)$pIZQ&y4vy0Kh95*Lp-*Mo|Ycr6p`i0)5mB1lT zgz9F_6&1<)?(F5@g&i7r{OMq%e=eXgtx&GV#dp>w$kpNWt$?BG` zMwf$Trjkn*S*)p^j7wSC@DQPPp;^G5?m>$=dw(4EWNY zsh(sqQQ8XhzvmWkW{>)p!K1U^YZog-W)Ow)s#m)gRx$0t9UmE{m3FR{D$KO|ioZW> zMdI4}&!)8+$I*$9`j1AlBV7UM)?NPhzX)yW@KT~4*&{yZ5@H$a9F9f&+)UmAW+sw1 zzmcd;z77&#OeO?iO*WtU)Kt2-WkOBAuPtEdO6%7@zcu7+8*G_I=JpF~xZiAdI1|r> z##9HBN!Uu#?AW4a8p{+CPbMwcnbsaxEv>mQ)8{BKX7c&9&Do|yIvYr2Mr*Z(nWt~o z=X$(%^z@48?R;Ca#)bp>iVX+@9$#*K?HuuN9erz-qt|D=>*SN8Jw*q$4Ai3lVB;w> z$SO~$`D+hu&~T?xT^#h2cJC6Pi-RrP=R4l2@8e(#75eUzd;1)jZCAu0Aa`-5_VaZ@+}j|_bPDYf4n?h=Bx2MGLPP>N}U_uy~DHpbT0TD zIRaiOE)gs{Fqdk7sv9B45ZBPP(^S~)a|le;0x4P<*^Sz=3-F4KZGJlds{3T7!%pWk z_CLpd{LitU{yr<6k}>kbk-SZNvWIMqy4x-P_v>d{?}$doz+P1bL_|a9Q)~?5ki|9RxCfw5IBI{(kqeb zlfpzm6HDcLm=LNPNgY-d?;vt79O7nmC>_J(fAVtm{X-z)+^hWvD?#+vVn5p>BxsX| z*B5AWAFK0iJtY$qZT@YgfZ|Dn=%icvf{7vB?l|bxi#LPTAz5W9TJ-@Xv{(puBoCu5 z|99477ujV}oOY01ZSb#Xzq8Bj4+ci8h-$8k=#67sIrW!FO!H)C!8#KE>M{^NiDe}4M+|9kNJ>%|icgh)lcY;+(+l$o7aF}=p) z{^7aa2flwjJg4TOQ`HRIg;Zo@+fRg+-~P9({ow+(N(1T8%|N-U7C>^%ZORG?PbbsM(b6u3pSlUf z)ONYQTCGrdus;|YD0w=}U?tMo0#s`<3m4S*Bm;Y}4EmdIdpqz0!JdsqhKO^D00qxC zLS&=r+>mqwR1+$1X7rz&P1!xx zi5kXr{T zBQalE`!1B(!VG;WB<@zXUG8n+wP}so6tV}7(C;KI{FDwOwp5to>;q|v?1+AFawUeFpu95{hOwc!tekF7Y#(yNh-P26a_;<5 zKR$S4fdP#8T+d#pQ~ahg*>&>W#q1F#)7B?N$nWHntN|2LF3=0-H!{BY!@P)bD3o-% zT*@)g-rRhC?V`%sTiY|pf#yq=)mr|L*SB=Z~VeBXF7Hvhmg z&?^hl2{@x7yqh@g`!MJTC?P-u9eAA+6&6}ZSRD@b}v;ZBXi!UZ!Y=>~`RHLj6xq_N(w z-DS-a5L5J+tVS2f-CKBDhj>x)q)uq(jSq zPj&{hI0kYekYj~$>U(hlXG-iQ6{yT0h~tFZn^)DTvut!IT+?TMzeeY-@}+z= zx~)dnm7xpVJ&n%Y)9CsuT+)1*q+SK5BV2uug@ld5OVK$2QuzxE8HNb9LiL5-yiV`R$SX-@)v5EEBoBWCTC(W!lg%%-;hnz`&+_0ePNH9jQX~3JBX&9?)=2Qa_NfnsJ zq`A}|LCLp7R95urx7C$w0{!s(o59PW$WY4q&zJMD?%=>T>sDI%O{0_9u91xM z`=KgYotD{U!(slsU3A*&B)yl(k4-#uS|*|%WmHXh&G z3-a-Jk|0MDjZhEY+s!Lty1c4z!NJVMMU7Zm1{0EB6fgv2RBzl0d5V&nb`nJ^8t!mk zCw>&(qh?zBtzB?~q8hMVRFp{_q{wzu1?^gC5C`xsLe-t7UzD?{i_IgV))oC#@D|7PbGj8M zQMBrF!cR)@VFEytdVJ=To=f*94ATJ=$IHOcRNAv5_kOK@P2T>i)&IJ!-r19_zQc&q z{cOHJGku7(79=k)ydXvF7o<;$7=G~>ivlK{(Y0KJNE-*VIPx9HoGgz7N>CwzRG?4f zteQMln%=L;hpTD^UrqkkV)EMq?w@b}d|X@<(BurIS67x{F5J}d<9f)w#7Vp{lywOMcrHtPUlzy)sZ@ zd9#=q*0RVqlpB9DUrQ={QqFE>thLpuQv9)2roldQfCEEMVykN)~vTE&AEowto3}^|psad;dcCpNY z$cuxMf7Je8tEX*lKc`GAWoWgE|M=JcDe<*sc4?b*7ej4Zk+cLsrt5XyJGx;IaGxEBZ7UF*rTP!gJGVluzb%$ZDAN6BGx#IJJ+094%^7<12mr*c+UwFqkYW zy|1#>{5SFQP|*GJ$IDR9a1~=N?IM|eaRrz(o!W?6-C8_7AE{w|+bnH^9QZWkRBm@U zXZ13>d@;=V`MVc`-x`3Epc#a|@TD|on|=e@y;()>T(Suk#!vaaw4_LlFu}lv`5R#F zy9sbU1L?llT4CN<^Y<8UwaqC3`3|sh=c+lyj#n2a3-8;mjKH?DI$0+laopf#@mOcH z3R2fWm~E!N_5iE9JLxHd?Ho7nDg|wnefTN2@t|};+B-fZozxhe$M*-WBaM#S0MPWv zbE?pVni8rDoI(%gN_1!H64pI1$vJUOAlU%8_bQ5QX@;I==q<-QklO@d=-+#!zg%St zoFt1OE2LA7VzgUj!0Zz|Q@4iE9j%;h2XM8j(c(k9KHy(OGqp1VB5&sY3o78t9>V`n2C=xK~F`W6Kvtkt?kwJX# zk#PFgWfl8s>2~e1x2IL?#T|+eiCcTVXiY(HAD6|Cy{}ci*jEY$nCh_P^e@T@Z|^+`<-Fc z*#=zsBXOaRs4HAaH=vfglp#~u2=V{)Hl%e@Am3{k@^;vT%1h%b3B!QI3d+H00Hj*B z5kXjyJI%oqR~&9Jlg5Q!Bo8&g-VMOU27i341dM_%ydF18+ziv4&qS)mbz@>jSCgYk z=`4%s?WH)dax>4~y?b*ez}3O?^$OL3pZpKUw!DQ0`N$N>O!KMSu92|HKH;$#dD?MP zd8O|8`V4BCtdRfnBSDpP*vj3%7>=)h)*_g?tr~A-66yg8RWaTMt83xYL;yqOhWJ1j zXal8xkVRmn1^z9&q&PvvRU34tZB4zFWj0~&{scA?$k!$Ok_z!tu?Yz5$FsWXB(zyL zVXLdEHm(T09W|@===p#3Dq3wgZ_ldLrV-|FL?LbvGPA4M&JN9Ylm`coCxchn#a<;X zIgh|`vZbgM?{tX6q!*6SjI^2aFWJ_B*YU=B&skeKr?#Bc`)lUWX#^QWfVO(cUtCu{ z4OLi0UY&Yqbp;5L1TkWUUI6_YgxDDko_yo@FSmpzZn?-62Yd&esB|3CVwphTgKE=8Znf$hA%DdAu z<=yF->hB>UUI$&GP{c2=uRK0sQ*W*sG%bblOl1V z+NT?ggrJ|p?dd03$^oSa7FT$KQI!y5!Gj;bn4X}6m81@klX5kjf|E3GzCKf5pQ-!# zNOki|g~6RZQo)@*QhPLA))@*CRn7+rDbl>sDcuqmOx}%Qh^n^wd#PIUxH9kRRX|B(E)a8LkVyTVfZ7Tp1Ftt0jPjIeDxzmTX^b!eWt!XQ~Q6U zcDK}*RV~-IH!C2Gc}xBJZ<|``U2hA%Kh%!fD8$bwiW);K$+7)1jcUpG~eP>t8)oQ6~V<($U;7&oA7^pxo^EdPR^+^AF!2c)z94YbM*}_7~da*i2N_1&u==~MaR2|Zsw=&FW+1#kX z!;f0hdcoVUq799EvqfeDjYwDbumbZrHDJmoQB)ImaZP+rf z5FhYh3I3F{vNu-Z>9 zmSI-W5(YL#By9m=f&cvc%|IF&q`_j+h#!~sNt5}vEDcMSx*Mrk9{|zjWBN9@+ZWc! z;@hPMR%rdSPCUCkv`fuO80FGkeda+Qu@)U?bHi=0rT1|CmHplgwlp7ZJifoDgWw51 z4B#5KERm9rpcV@ybVxv@QFjXxM*Ib(={MSA(q)hSp7!YPX^(rr-YNbi^neSCSV?^%b&43_%>cfG5G~Lm z_t@m7SYxV~7O82wI{;k<* zPM1Yc)e&L?A>*~9%5Ca`Uq7Zaf9dnrn&kY z?XQ8Bh6=P}!dQ;}-W#Smcw)GyMYD867l2&Pb^%o54@%&5$k2DWpeXNFf_hh0uZB2% zZ5qdeZ*NUktkv^ci13uXniqj3w)!n7{l$1ox?+VqIvdZ)(&qbV$)WX*4Eb{_k7XQX zxiRsco4!`LF>UXaYiSgDk^LkzYwayx+6Ie-uttxQaA6)!ZU~s#aPNvsA@I8tT&yyp z9l;T{sK;db#Lx7WzZtx}9X zaA0&Ul}~Otun-Ai?LK*I#(QJQ(7qYiZUR~53l@Jix^3Ey?bKG+ZnAOh9=w4nYy{8Q z~}9j{`4keWnj&l zDL()K>u=9qziO!Xuo`Imna1zzLcW0%oZenG0p7SLnqaW~=#aa~Pg9L-*^7k-QYFr; zJn2HFLEj2WiKmNXV{!AkvJ;gVyi?LWGmqh)Eyv!w$@w`!Y}1fr=?))fA|!3asbTBa z*OQX=%}j5;v=qshZwwFK;77grXXfU=k(d~*YqM6mF3rtYGd0hrOj+Ku%>&WCHmX7z z?d`hG+3$rHf>wFucD%HFewx4db2U}f0nyq+d=a*`iwYHPNfIYD+tpN09TE$^n#)(&~R!VS@{a}s0=ribgH}TYw=|F|aO@hQFjzlT0fKNDbk!A8T zKQsA@6(6~uJAC%$$)o4w`F}WDD35Gk>MzjA?02KoZxTCh`dfQ zw%~3?6x_{-`e!oP3@?B!+|NC)_JNe=MV#dcw3s4_K+G;-JOx}JeG$1#a1t~ZvWbNk zqWG7s;vhl~2+}>jiHHJ|zhWcW9#)st@kc*zRrVTwC5U={Kdf$=z-U;=O#=+&DhI9_T%A99BJxH6Dm%$Z?DS(Ur$GaNfWJ{U;cdZ@xqooMXy*G zuU3i5cHN-p+pJQSy|YC#l?+;7!~X8gAJifJ{^bkfQ;Yn~!HsA=Wrj`b@Bef?x%x+L zVDQ7+=SZH3w`%@oSePc4*CWWmnCC&G=sb9EKAGVD9`Q$^*MbN@obBq0eRS*p?s zUD;eLTZpA#C9?}*LvhaCls(fsSwl?d=*klyi-Vq$5F(tkY+pl(<8%R~m@Nh7#kH~P z#sxP69|jLZ@>>>f@vQb1hU2AjydfHpS#p-%thL>vr&?%_yp35=KN4`TS^7$G|8$t)3&XcvL^5katakw*B*?4NNcxCfN z_hUQgp7@E4K{rtwa8@U!f%)h0(kno@!@BqkoMZnuImXqG9dB0+)#+11T^w#d?&4kV z*=`o&9%uF!Gl90)P0IvkRK%#Wf)+L>#FeeG(kp8!2I7S9u}8ZaCF&G!q!vXJKwCwW zl<{(#TNpaXi>zvyY%3sm(JoEccvVw42qj`m@W4~r!M&1s;`q!nLh@GIt*hw?ch6z2$7+H}e&|?#8|rlgJ~ zATN*>9)v8Z7kGr(fIJevK&(3Er3B<71%3*{MC_M7cvm_#>JaX+G+pk@0D~I)_iR<+ z-@?0U2i`M-_^i6ZldG$CvllU+oxiUHPLcLvcJexV8ds%T1EkQxuKM2v{Mya+1Bu8?ZpmOFJ|>*7RE0p)olEmmL9CB0+>=p!@qmj9;QB* z8GF@Pl@iVQRK#BAhvPv_j?8?9R&A#FX{M}dD$zrs3)gS>o&7KbvLpv-IG%DgG z*W%P=rl*ei=m+f zez=6!J$niBnXgQIzx}=Lj(S7XDh38 zlX0`G{BUGseNENl!^Bion$lLg2gVXk@(@D z-Jb&`alX8@+hWT^m-&Pe+425y$P&p~Yj*F9*FO0B?BentjS=33bZuwUarxGHGdpQ6 z+aEKz;k-Q~j!h%M=w9V zx_q3S{%-o_>gId@Mg8;7;raRL_kZ}WC(W^IylcVnt^g(NL%J4prb#<7Or3A&ZXDB= zQ~IYz(_FkkpZZF{R}#u*w|0EWH?bg_qF5wVT=XVl7zc0>X}XL;%4lmwdpD7tmGy88%DCfVf1(VSWHQi&aDOj`FJ%DRy>>-D&Z=`HC>A5NkH%;3`gO%)mkQ)!SiSDCyINk_ z=&^rX_0#2V28UIU*jw|>*@s&N4&gK5N*32wCR4n0(%`{Q;AU2wyN8-^pCkSTPfyR@ z*<&PAubE|K+1)Up0pkr-Lv(737o(3&!Q%4r;K5L&AH+ij^dlIXmcZ8*Jj6*Gh_r-i zbQoBS9cQ8LVg9oj7&mh$k~1vO!{dDkcDE*;SAOj4(HUbnF-FkClUq2x6Xx=gm7P$w zkR!XGF7~r^4;qWX{u+y2k3qM};+8Y^fF2Y%Tu31uymjW(Q0iwqOK@jKVGNR`h>=-L zARvSLg@i{`xnWK73kv8yJ`)L%<*#2HueHv9&bzODrWfCRF#ng3zte+L7g>csSj7w5 z3lb0JU4-)NyKn7f*0c~Fn+WTDR)U_php(CI?!BMx-ut-+2Uh7OUV=-5`;t{CXl79^ zAf$>8=v-t1b0_7$Aal&!(DQsz(f}C%>V$fGAx+wg`{@A!*k?cGJGEs$9R=*Wzk>br zJ9%R|3o6~aRjZDmx+cFExu z7;T{DQeMYdxtM&C)Y7Vo!Kyp)Vs|#_8QznMTd5G&;hD+hnJkj|t;5?`KCzp;Ulv{5 zh91&V`Q+!SyqaJGCwAL=X-lo1*6Ee}D80t{kZy=GYyYbWp_}H2<^kC$GUXgcJCi$o zycj#q{z}n$%iG1Fn?w5dMS3ZTnbJw==F0utrK*=^MIP0GN6eE~i67&T6%F~`Itr68pwLa)h#W%} z1gYN=l6;xw!l>I}advN=vwQ3Mf5KccDEJ&% zmvth}rGp+A(#Ija)=8Nz$Jf|Fl&%NZ;b(rtoI7pZV)(qP3U>Ov2HxNItB392T=Bv! zoGb06dTFd)3_-blZp)z91^plV|J5fk5Pq6}eOG?i6xKV**5*JdCvOVt&2TFpVp}>h zzYZEK-dkFZHhqFuCU@*6Yae5x`8jls!)EQEQwFt68&X&@AVt|aifr1@#3$kU{M>4d z7N*E?z<(O`M}tT^ef%vSQI4m!Lmi|Ziod9RJu5?K9mkw$xG)Z=DWG*ZK5w`-Y-vyQ zBx&los)`RHE;N$zS~a~IqU|W;Edo8X05=&+V2N_}fl=A42=|*R)U?XELtKm}+B=9; z=C=#;sfW|qRoi=IPC;eJV9hkWan^K13FXG%G?z7dBR6oM&^y3+B4$}y8BDStC}pBr zc_FHm-%Yg6c-SzRNOFVrIw~mKODisPQH`&4^5iH;r7Ta-9K`frQTYQ>SgFhr^D?cF zYb$KjjK-DG2G!bHv+ZY(zi;$MtMZ_^XAB4pU7WDC&rkns_DE`nGM_@PrQ352YIf4l z<78!iAOcYQg6GD+23G}`V(wX|uF6PvTc%OiXvwM53DmO8DPA1sNATpfcEP_5K6xrT1gfX+AOG@NO+ zYGEv9C8JBVYqo5ZA8}u;r5;G*90<=wAWP>jyh0VzZE|GI{7pO8{4tSa72UWxI3B4C za9|{?@Ueas`K#uoaw0DXX_Y)`UVS~1SzRZ0`jDsNE2*~H(^mb*Onz-&b32_!?>N}y zIwreaN3T1)PPTX-D=*{sZnDJ>SwFZx(uaN;P+xA~Ch*)<5t5@QSd=dO#b_uwSrR}3 zi8xb4xhG1a0GL@+;*@waP81M`FL@*=%LI>0&u_<9+r#-e=GIS9$Uh`<`|Kg>+SgQlN`q%Z;+YZjfKn0h(T>gQ0H9Z3@on zb!rYw{PlI<-NGn@Rv!gwbtQ~`x9xe_F5jT5tUD}Y9UJ1ne4SS?H?)HPfG;>wc$GAu zXt*Ga>dIKizobVUFGDkHXIkxRn|I{bR#VhC$mf!6d);EYaC^idG@KEwTBn?sU)RtB z%>M0v%dpL~_2St=5>XIvrUT|dWh0)kD8 zn=^x&lhcWEpgx!hw2_mVfMU(dd-?p`FX!IDUcwt@e3?zNpRPy46Jt+}QTFN1-!j0S z5VWmBAsvfR%V6v1v&ZJd!5e$#s^3^PLrUve54SPeSvd`QIgZ`3Gr>03aq)HYKG^0r zt{mAvjg=Iratay0v}L2zR0gphIb|4=sv^mV=!Fv^h>=iuasAqLb6(_Nhq5q2Fs)wh z^ar^e27!N%4n+IbTx_q6T%b#k##M+P0&0(f$Vng`h$?|K`RTp1*KQ!Z+K&+#gk&e3 z@#=>Cni$-?ElJiO?rp1aZ(Hr}?8b0T2mu9h;{bvXLx&vSr{-H{fE>#N$2J1HhX0GT zlIlcgtip~%>C?)^V!yntu7MrD+Ul3J)v)`$8uhl-sJE^5chZr$OWiz(QNqIw_o9Tv zKjArbY#MANFb@Ep#6=xIs!|6veL1uVdU+a!k&vjYIO%~Hb9&#l>V{u!^)Ew4C=;!n zpIt#toY#||=K;pzUsnhRed;-j!Ss57;dj>*p2~2mV8Qc8nRL#GS2fclsW#%6L~$;O zS)8X~eLYgYovbYX`FE>f#}Pg;$zmEvG|$Z6hUa7Gvi`@}hvA>EKVJXKgT}v`nLiv) zhaW#?$D-2?ADvV%Y!zH`p|;6I$z`AUh(WM;yRt;y4tn6I92!s}caFZ5u{ zUi@r2@hx~3Y@(#;g+uS;x38WLiYZT*4M2178Z{zJB|IrhY)+qDRV4nDS$baTp?9~X(I9?ppu2YwVQz2E4wiMiiagfxI zB7@pZS=U$*K_&WJA4TIA{*WQxibCa2G=RR^K|eC z|9jRcnI!+AGB0LuP0#DU4Wt(Td@5*K`qIfVZ|b!-Jl^U$KZRTON-4I804k_}Z^YxZ z4ft)T!1~+<6(Xr_trg7Wh?b_7Puu^J|5#$ao|h7qPc}+Lb8(#ZY?1AmRscAZms-|E z(Y(TbL8g3RJnNcUOoq;UKHY|)xFry_!bc4_V?jD+;~d5yca&z>uq4}>#p3NWzbr42 zB=sMi{aygXh8bDuz_57HldyPsBJ!AFJw8fI8c!V+fnuwXkwn-x-x8Vm7&; z@_a2$&z>}&nVuwv8u_8~_H-&8jfxW{utWj9w3Y@>mB|qVSwftxRTSkx0Eu(cK&H0e zjIdEt_kiDlfNF8V#nZI4DReumIE}HL}rKPF!^{|5{v7pX?@)B1T+NNQjmK+ zZiwL9$MMwK^5VSS7|wM=V?Vcc!%ZRgLfsI~MV%06De!yL+Gci!=0P!`jto3m_u0r8&W#3v)`DfCF(tew>fp0h^|&e+Nit2Iz+@y$3JDxfQ5f%)|< z4p;#TXjPf6)b`{qx@v8Bqj$0A7Y0|vp{|t<-m}Alq3<) z2Im*E>#+Kl^>R(qi_#izK*fn; zYLY0sj@m@;zMylyl2!>7^AmTZRkZczv5Y%gqbYncmgZnuX+)X^NisVNC{ISCtQb~n zxfNQ6FQx7&_0H_-N>G;PnRLV$`jW||xTZ51N;Q=xQ(%RfKT(2`h59JlM;pmiRgKia z2GaPcvaw5Eoeve~sEbh=2mC?^!&|YY5kcCuWPz>)QrS#FvN1L)-#8Pl_^#=0A-F^u z>Za0v)YZ-5Kqvt1RE1l8Y29SLdh~d5W14D26;5>9m?v^nLlZ;O?<*HH!Gmf!9S;tE zm@Uw=a91kKEM$O_$;Ttwr!7QtdEMn&5?q^}9DwJUHpwh5_Hf!+vvc4N8aKU5=~NUlv+qH89RQ`Ty1bA2X$aPUl8h^XEUP1lv_^ZGQLUR{e` z9IQ3f)o)}^5!j-ld@Wbet|O^AOVmGGoD4D3#pPAgtkmwG!csN-W!)syX9ih*EiG!V zs!uBMr5usj$`Y}644GNl9P^*5IxcwKOR3PC6Kt9*n;GQnoN97JpSsZNlU=OFio2a= z7x_rrzYI5|D*x%8Ny^U>w;R1V>36F;HcpTJ0M8F;!cRgU)LH}P_#3rj+dws_=N!D_1C^p>kD4+XXXWeV&5o_n-_eK@U{E&&N#L` z<=I6SyAg}A6Qe71;JXn9PA8Vk>ylX7_2GN(b*U`%IJ3WB2AN|-J9=FOnGfwepaOb6 zVv1l0FyZ4<$fX@9t&1q889j6=n!cS3>SrW!P|sN|gF!zI#d8wgV@F8)L0?p~g_S9z zS1ICWffPqLWg#*?F||AV$!vpPwrk0LE#AAk1(oP|ceW(&&X&|ax8b80AH#J+CDeme z6z2InWjh={Dm~O1=rY5_her##aDcT{8Y0AmRiSa@m4pJzJWH~dxcGaqg>QB6O;`O2 zZDdbF+Oc0<1#Zv1^M1K^-mm_=Sg^Ev!W2@A6dz1jB&328j@JYpEDwPSd@h!yVi~0O zvkJTz>Y~Ug{5XXSGAP&gD~j)Xzg7aWz4vQ(P4`k{Hv`!pSxOPxnb&sLe_OBddOJ~- z9hB)h-T(yTgu>&gl#f?g8`?Uo5gIbigdRT7M2&kX`_m}KmsKm zoPMdd37if)kj)BFcZ1H#0ccFW-`YE3171ElPRz@NY6&WsrorH zWnutrK$5?iBktI5H0GFmwA7V{ZG~cL)RZg&a&Sw_n<=$O@S%>aX@YyYxM1HNY$mVKhVn9!eyj#N^r6yuWR>G?m*Y@O>^*D1pqJw^LKHiZS}Z0 zLKsf8h$jm%ywE6P0Utac*=iWSkT3_qYhtb|11+}7#imR((&$OU^7*L-5^!Qm?^5ah z#+15cNph>Ryu>o5PhKsu=T~cW^hSA@hxDn0tlwDGa6MP*W-=Rnx>#h1Z04fU^~%jJ z7{VRs@EhH?!kB!_7;6*U)DdAy=}6kZa?#g`Dei2Hkwu zy(qu#0Bw~=q^rX0@AruK`#mE4i!O92Qme?ag!Wt@q8TKCDMjX@Okqr|>A_8MXi4*e zz$_PW2gq+BeW-l=8{&d`MMW8Q+#vG3y&jQWMHlHLi@xk5a=%vprA(gUW%upp9!YgOC(w`LbyMlp5Hq^Og>mXLC4s^Y|&30hMb1ZAeaL9~W;z z1Mo$W=L>P7g}p?HODsV6Z~-O>v;Rj8U}QMLv!e^_#ti-m8850SZNWtcv;lZSnh0d| zy0|hSw?+ZVa;*Aka{E-ee*8d;q}o``KvDuTAn?gjI5=Mgjhaea;6|v7>J)cPYyo%U zr)*~L^Fwwi)lVt^2(O8h8uOV%WEIHNvcYs3cD)H8UF{qd_Qy}d*|pa9M2PKD598_l zxNrv+mo5PYSJfy4BA07oZLTgEkx3KV3E|AcL&J(_RL^8=sa=jg+Mr*U4ktp@LKb59 z^I@}#)zSpja%2yia_w+&MT7;%%;^G}WD_gf5L30nC6c?zr2hDPtkHDCdL!BtCaGd~ z@vyPX4_ZZJ1(Z;c_k|U9784y^pG&sHR;MnS0-dFP%vk@jnp?E6)YK}ir}xvKd5wPB zr7b?aoa948FVyq^Q`l;Gcn)o*Nq5_FR!#_Z=QYFD3hlpnsy~v2S?oMG#@H%V{TS$nvs__1$qe>fpS9N5jg49= z#f7Cci8T@G6NXW?_QRyX!P<*9=^ojk3%T{mZaM#5UN`v)`-Dm2O{Kl{h-S5q*NS$h zJw&gW=ixiv8i$g*=&s$#aa%aR<(tqarZ$J#|I;g+UOJ;sBwbF5+3|czC;x>{-gz|- zoHxbLcAH3j!0MD;tg-h%^|*MG7P2Z|BD211xC2;_uTPV8Quu{i_2-o+1o~egcG#G!>Tx+|wyZc7$CE2;ry{lt@&=Edc?L z)C0XZCr+L=JiM`t4(T z{O_NC`*sfY>Oyzfefu@VD#l>DxJ;9@pTQ<+KZ8xuJ#^KRpprgK$9E9?s_TN@dLK%4 zpMFVr@sq3+WA!{}Q=E`WcR)oegugU(DOtzWmp51#I93>bz8dT=Ww2ez%p~b)ut`sY z?cac$MFG5NQ0%%6e3mXr$|MMBmZy#xgZ$4N-*K~!Q8U<^y}+h_z(_YB=AD_6Sb z1EJh?g8Mbtf&V1GmBw^=FGeYUi+cdE;_bYGCaacu3cT2HM$J@4G2wuU}&xxouE8TFdn_~{!9#+9jN`Q4a6gnei2yM`|ttRxL z(f6jBii9zE7OlsKItjnPiXs3qpp z>YW{;I+Ea%p>iW;B%v#e`>!TQ1dx)Vn)+h!y=ab)+rPZw3KSBj$~J0)8>tCaCI%I3 zq!9owP4#~HdbT5W@Wa~|vJu+8y13tZZ5s0jAFEpv2#Qdq;d|9qW#zlKpkrNPQ9{>d zT-fV}vW-iNt6}qSd`+T)y7u5v`*+=p;Y@eP2s_&LzHuvm z$uB82BB$--ae?*^iF6Q_6khmfu$Fp}&Ucx!@tBp7^wr*n-ezAlunm^v^*6fy^&09O)HpC8p*;;MClBM=L!H?vB3hXW{I;S>(-i4a zh=zPS*lKVBfXt9mwtI^$!UTJR))(uAG_P4+O=u*M_CP`8w z5D<|nsy%v`33DhK)`=s9L1?3Egu!dy_dLW_U6JN=J?(z3C`B(zoFx(#-Lw5IImln_ z@3TXd;pEu*5u4c|JDt@=UvkqCt2uj}wY`Pm zWDo6=x^E9(cO=&lYCAnSS=>Hq;oaC8JH*D9+olbDZ#H!EE3W&5-Y+`3 zVE~gQDER_Sk75t0UxCzQ@F2#Ckj05UC++nif{uKI1usE&Gu-%yb*4_ToW^mJFbRUa zWH`5LInJM)#Vk%*RlIR4uW|uy0HH_Lk+*e;e}Do^>Gx@S=tiu_^11tNkh|{&?Vh9p!ug498J6Q3umS2u{}1MmJe zNZx!x754=;$l0<%&b}Mu?7KmGl$x^8t*a;xBJ%ffT{?6G--SRAIog4D9%s0P{xz#b^b*mp{8}2WX<{GNbN)X>_)Qo{ImmX}nc9 zk0SZe7-~k5Gb@V=krc$=N&bj#SA`KgA}t}@Y9I9?T>2GklnOvGP3bM(K>I%6DsgP` zZ+-zFEe&{N)(8&?9{!4Cm15*IIYuYLD{{t=+pQ;(DY+Rb*Gx*OHkk{6Hj7P1o_eiE@#1#84B3irN=#rhx$V zjrx%#8@RIICVN4ZE^4xwtwK4*W;MoU*nbB5u+tyCs1szS&)niR( zsV!*LPC;PvTy-4#i40Zk<5Inyd{SB?t^Uf3jw!085s!M@s*X? z>R6=Lb**5S+H|Qj&xhGcrKl(cl%xj%2&yV!jgTqnM9d4zyyUSrGrJw!&0jw& zOf#Ix>OKL@V*!bq+4y%H-8Th1y*e_l^^@i;+{@P+}lWr0tpz?#0*6IL_9 z_N#=j?d?v*tiX^(Z*OsNdw^UgyBYl3=TzrJXy(+L@p{Z{=Jj`FC!L23l^F% zuTCJa3HTq{w?hb#5;SA$JP*4e)sJ4EzCBYFcFlGi+CM*i{%ZY?%kb-OU;Oao9qiK2 z=KuBn^zqB*uNM9d$(|X+QIky zy^NpZ_cDHt?;kg(C3Na3qO%FQMrG>6LNkY406pD2L@*zgU~~wU(Lk!mQfH8X6@^P$ zI}0)=jFyQfXg>&&^uEdHRuY827UOS>{a0??JB+lmdCK$lG*WL*BkiBuDbl#0=P5-W z0E*5uBqbanb4Z5Afr8)l%A$$`Kglxozo@9!WrgLI zh)EbNn?$*Mh2z0lK||NbP%3C9#Rx<xnlLys-C3kl z43D`moi99*_CIxAN-=0I^YeTNSOje!lCSqLvpaJ`sQZrtX)e(_Gk=_DJBP&*L!Eb z&C)yh@XYMIiS?gV+34y+M_ORzUB0=f*L|Be!;iz=sIjGU`a#B)FS93JtZOSCJv)8% z-4hsW*^P}!174g$MfnGkiLx4941NA?hCUGgZXbl0-`!){BV zdDKz|c~`pd%iKW{(DTR-xJ4c2Io;I=4;Lv&G6%R=Qc?q{W8VwXm<$2c5ty+2WuFsC zH3}&4T3R5vds?9NSQd%7V#iOAdWCI(*p)egE6q#lZhlap1LtmVIyc}SI;5n|9zA>V z_=gt);)X{Vc*N=3mxo{}mj2zqz1Nrjt}fPJO`o?BE`nH&ECrWCD;oc+z2lDEZ_ zy!}kc+s~9dXP;bUK#nF5;>Nt@l_a(kXXuqWA)F9w7l75$2_Z@k(+Kqn%+w8IF|CL* zsui{!hn6dUtxCJyl(zGP1}jv2fBmMkyI!2(Vt(YiE77(A(9a9T>%XnUt%2kgym$*D zD^lu~&2j6uX2j)i)yOg*3J1fjjZ}$zW>Sortzxq_&+*uE)}UFBZ@CJwoQ^Xvhcyb& z=5NwhU`v8>WhJCj$6rO7C(1j1f$RCGDfhLlAnlS$jd;2+rUZmj<;vE;a7Z*^1hgJV zwQ*J**rU|_)hJU5#SP`!-#y$knPx!k`xiG`9ryA8GWIKJgMT7!y7`Aji*jMph;Qfw zkL>)~g_ZGTe*D<O!&}q!-clN&FzAFxrSY^t8~BJcGLZK+wI>g?V_?m zyQmlWu8+0}1(+DeG9t^34S<)+3M88mzJ<%VtRf0Cf?cfW4sd}d*8cLxJcvS9imJzRB+ zF0Schyq$Yk8-L!|r|WL*)0I}DO->f$t~yvRuSFhHzWb`mC`szdiq5=ja@QcL%W!MV^#MN#kd1pB8~qL@-T>-45LqlN;)x%wK`T`{hc|-OdqD93R}Tv^ zA~e_Idf#f!*ZtSdno zP?wE3%;r(itCsAu9na#&Og9D1)Verim*K+(*JwJc<#~JX!@H+PiLTQNS(YQWV?DkV z_Hqk8)f!cPeIftXETXX}L%{uy%M~tVWtM3xBlEU>ebXGID~xw+;DM)R@fqwb|BGdM zsgz&_4QarK^6+jR*~&n-*O#MT`af9x(%J+Rs;i_ae)9kc<<{omz9R)Iv4%mkW<(}} zgY8-qvmS?SK(b{8UTB%cjHI_Oo^Q)WZUU^_Zh=HKGUKtXYteD%!jNt9|7Ta3T~nn8 z(^nXgqW)u~N69~@&3ZS#D_5Agl@60&vX%_4k)@umvx$@zZc6#HjR0@=lQ9yOaWQtw zJ#eYYvT&%o;fe2@1mQ{KoTQEv8qLEEJv>G4BU|<}PweKgeWwG{R?hl_?zZDm&MzOkG{60TkEDr7j>+^9<`};#*{?$A9jCWAZAVk5`pSDyF+b zRZ;4#YDSF}StB}BD*HnVD{X|N&9mMGURC`xv-S;S*%H<5R^#fjz35hCMm< zjuzaxUF~j3&xW^^VY?c4lJ4fWPA~3#|8PL~^b0++&;$Yr z@f>aT?Y|K6%KHMe?^_4`rD@-jW4Gs%>fZ55b?^A3_OCIEk`t`(C_dBdUZ9?Uj<}N) ziBloS8%CjY;rmkMp@$zH$RekWzbpa}j1_JCak2cQBG%+M>3u(`E9?+opVY5U>fTSP z)ALDn?)aoScYIR!NX#R-7Nw&bCtPEEU~$ul%^cAu5+*b(N>8g%bI@&0{;&%5Q)?je%a=W35Qdg1sz zv7(D-ZtEx2kxO zkz=MwG^r~o+EvKyqRZq*DI{(Iy`&+%bV=nUV64K@uPMd5LKnDHo{tkJ#eW5A&YD{>U)TZ;q7@YSs&Of>qM}oZjqrQRZzx_B3DHE0x%{aV#6z9!YL+t zuMy=}1KqWy1WFmu{V-eh20W z62jU60)xPG;(K+NBvH7+(S;gzhs>--B?XB%_eqm8qkA(6 zyHJ*FA=Cq`zK0_!NB6f-KJ{M>^u8U@ zUDoOCWu4w$*4f_?B_c;14d%)^qq&nJc<%aWO#!r~Snfj7UU;PhP6d*>1=fdTa}*$! z8`fow+)lXah{8e=$6u}UKZzr{%SPS3Y}DP$M*BOWaZH$$Lt}$#GLp%V*3dN?gBOQc zwTjRZ2!aIXvjECL-Pjdriz*a`cFcr6yOsW9B*TNq{c5AXQb%-qEhhWZvEal;n(=2^ z*SLSi#f2m0|Ym2dLLvo~+0zYjd$ zYCYzua7hjp=p=sAq@=6yv2ZC|O4j_C)=XtqIFX^=KgfjS<$C&lnw7)J``M(vx&ayY z@Hdjumm}u95%#iH0M_bK35$&M3JL*=>JurC=fzMkdl~roffO3x#IcO4+A|1oY(`ZF zrrPVP%WL42H2E1NUBSCHw49 zm1#w>xO!v4(MsLGK%Q@Amp~e?WJ9=zmsTZs!L}8mC=1)@NezZVP9m(dt&Xr90t8)b zL!j=$kZX{U4{kmTr8>0u`y5*N@Ob}n%_Af6tfIah)tQZl^sUQ4*88%3z)s|OFpooz z63ymQ_pH`*46?0ovvkU8*X8_e@lV*C;8K@bFKLP{CPUu-FP>dq{PE?)`{#-OHv8v4 zz5DcR_Rb&9p8htRe)=i+`Ntpr>q&F$nk;YV@2CugejiQVu&akJ>`9BK#qYG3T@f;m8NWLZo}pny&QElYHW3a@sn2;#iYq^6@Be)jCe^Y5O$ z>-imjeERP6{n;O1J=*l+eAt~1K=%=EU;h>kyslB6KYIK6&9m39p6o!aEH<&V*p$r{ z^y^Yy4ZGSSLvPRL$lLQd>K~HjUIMX601gspm0*q&YOR46)_LtBZW>WrrI-cjMIcwRV4vf>8?CSVGOJ3eLqJlDy(0hqrbK1s4H#^-94WpchBdjf7pm-VFe_i zLJ7F?ArXMY+9SVK!yJ&L6{R_7tyZk?b^JnosHq_+ed>ZcF5&;_R+i% zRjz5^aJKieRX$ncaBYWMAZ;avDz*&vU?BHp|M5_U9ZH*5%cuIy!Qe<_KAP*o4+~G~ z$S8zJTbSa|8eAk7lPl|mTR7jWLH-#?pAMscfavdBhF|8%tVQxcF@_WA`K6~#(Skxb zDHNDjFe;k<*ZBon5>KOQEeKfK+s$L2yc|rfDR$ZK3oU&Lk9;@m>tiKV02Z^1tzs>VUBp3{e&LOOY#!flN|GA!b_DaT#oej zf9BrDLr8i5nf%koi8*3=b$xyhh0$lOJc7!;cv`s-I7I)g3i&&K{|cO(%9Xdp`OO>fuk4 znOe(M@D(WSPd#{|yyJLR;vw$C6s6;A_VnT{#ev07*84EtQW#h{vVX%EF^?cq$ScHU zipzvY7eb65g+W$1evOU;VAK-0N**C&?LsvZWYY6ddQR=Q(OOZ!3EjQ;q0(J#DCWz% z$o`i=WSNC^i0U?7rd*V6lqzz;faL+uh2AVOpUV;?+ZuIkYt(yNqu$#Z^>^Ask*EYM z9K{LQq!3|V^box&g8?icn?%Vc$?Kee7$4bZq06*L+z=L^sEW~}S>9HV9#MLq+p6V* zRZ_?Q6x$m1-qx`9wub!`6-$p)Mv*5J-`GPqo!B=hIcrd50UKA9F}ktJu~as*BZ3m^+Fq*^BoJfx^lU>(owA)+O7iox+W= z!n8PgyUv}NmFnM2Kir}UaJ-4bX=OsBvDb*3e*91=iHvpB#EolRYUx6{);1(@*F~64 zm`$di#LfLegy$&uWUdQavjbg6mX`4{-8bBP zAmZ*@P%d6o(sjf~OoVh2H!>>(TVH0#WG8i9R$;d3y+6-6ZF=eb;>bv(%8!#SwZVPN zBG|_)f;}pQxsy`h1C;Am@N@H34QPl^LioZ7qSyx|Q-PC#XECqxyoAI%PicgJ)FBLl zu>1wgVy%b#uiPxWeayn!$1M66zI>I_s|%ReQPr+N%|Zg=i%>dFqgPAoe(I$u46)Gt z@+Y9(C5iD8;dJ&U&R3w^qU9Gje|u)JKM-)MWw`rThP#ht^e^QUPDT42dh1X;!~4v8 zpo)Q`x-76y@g}FoFHeC@)3Q=SB16DWZ(o)aUL2>8#H>Vx^rxmlnEXvz#@>A(&X%Ck z-{)iE@AEP7_mJ%@Y4dP%C(KZ%O=txPl02dPC;}!AvcxOM2o>N#Gu|tbFhaq$K(`jF zq0U~2ddTv}#392F-?weJ$zS2eq|+?+`Iz|od`$fQACnY9X&+;N_=S3I5W*HtTTxtr z!6lL8#wC3~G_z4|MU66q-Z}74rQ|K+W|a_8t$j@BX%GJP%;IhzlTORn=VRjU^D*)F z!397!?v!o<5fFJ_Qb|r+)o~em=nN326c8Ik1+3$7;#V0j6ep@AHAQ3+1fT|1KPK=R zIMLsvWqcl%drDFM>gw1cl}#3F`ETpRZvV*hx3KC23QXFNz+%kl8m-QmRU2K<6jccK zsl`rLM^anq0CT^YPN-Y*|43yO)#X)l^n>5NJbm-9fnq-(F*8PZMe;=dr;+`8Arvjs znf=$pV+vg&aQUIiKHX~Bl`)$c{I~N z>;IaeZ&@h>y-=!KJ!eSym2}ea;8~;5DVYvap+$5?5{ubP^Id~-A{gqyRK#l_Djq6x zwHA`e1DdLT!@l!DVbzUlrJh}I+SRcXH(}v1hIciqM)lxkGG=mD@CpG*5MVdc7z=t^ za(j>)36ZV2OA)U;Ep#BkZd>YRDUlw}S#F2pPm-my<$TE#R^~Vn*{mhySi-M9-#S@< zTb&n23#~4zIlCnNhaTlXap^T0ug8P$k5bHoEhMA)G#gHz=zem7nt~Lh)eJNVDm$-o zpf8P{EZYkvpd{O=OZ{4~|2HBJdpr0@0UgOpDTYf!0NF-K91inoro_7nB7pM!+KkT1 zl+>JO!EZ)a&XfQLzFkIR z&=yssztEo<)8#GHOhWDec`8Ho{heM^b`wtg%i+|%D@E){&Sib&O)`~LyxPEryh}w% zt>^H$YClz19f@5%s7nfTWZ$Ekvb^&0hhY0$`&Wjq-u&7&Hv_nNM)t##ij)Pt7PHCT9t&X#w5YO`Guv>s< z?YRDKa*;X_u>8t{Vb#Y$u8EF`reJmA|5A*FbH|ltP`c=2=?BG$M)ouHV;q%gCFc@L zhAvr4x%i*eS{JN>jZwHyB0sMw&4n&Zjv;I*70rP{I#nSa^BJvmJ5Yw3RQKj?*77EN z0cA!pA%9)Lb65(I7Ok+XF{qZ4M8b(EB06!_B3UO1%_5t3!G zLjotsgy$E@SV+NEO$h9Jn(k#nJ9QY{BiH?ELX8P^Dn!LSO(^bZLjA!!Q4JM4={%2( zK{q%EGq0dA9S)BaCfO2YsLUxmkFJO`FVJC1(*$KtBs)`ByefCOAb=z>Xzk=4O~}{Q zq400ggto_U(jJMdNF_dG`yVEh+xXLpq-VH2qSHv&*&NYbQ`;Y|Ms&ZQ{9KKWa`gx= zgsIYEgU`j|;=ixNw_k%U>rm>q4L&Uluym3s(l3s=6v+TjmFO$~Nt-L%a4h?TGo?vb;}IrIBJjzFsw=lIGN5Y!WBS432&(ceD*hyrKzUE zT54ST#l;$lvo6lFI@JhyT$&zQS%r$|h%opYg zVFeRpi&_YHK@g}{I(_rpT#Z|!jDM@K;H-(~B@_iNtUt&NF>I+2<%tG;04%0g5{j%k z(JurT;lZdsnI4UVcXhKb{dO{{j)dP-X7U}A?{KHPk6UrA1xYYcOH>zlCz8S(dnCt> z?6c4VN3nKR9~4D7DQ1gvYmYct0#0mzMl76Uew};S0$6`E8I8;X^WU3$So!mcGaeqh zkx*7_M^3n8aUFNJOWusu(a2;++4xD^_;P!1&*}I)Zat^>r&5@NoFahDoD_B%rkRh7 z3CKvQU9lHK{ZhghUIQGEcXtE<=(-t_IP83sY?kZ^pd>9PYyDKm5^^K3Mfn66y0Xw z2Y|pQbD)|`;6$Zd8wUZY20rV-zV zFL^P{jAP&V^E%ZI{(acx+^d4NnCm9zp3w%UmzS&lz4-y36vcqH&1yUu-(F0vXHu{D zP*E)rFWF@DuNqpK-@(bFWHsunimxMWTdR=e&O1By^eI`zE{o4^rv$^yaMqFl;appv zdEw%=Jq{OjI%|9Eq(yRJ(hY5m!~@cZCPf!q@lV6jB&KhrKrz#nF-<|=Fyd??xob_& zt41HmVafZK%58(z$CMwx< zl-g+N6qQC<3W*%%EI!4=+W-D)a;9@UL#sqI92S4uj9#!8-uRLAwtQkcUmx$iEg#t(XZ82r zNSgvvARLJB1yzO=U3Ih>(!i-BH>#twaF_izFMu^9 zTBAEiw|!-uT`e}|b-(bHEvqWYLO=BCG{UR&vk0~WBACR@B5+vVFV;Ebz=XJkl6&v{ zb~|;rww}{_uYoT)f>i=L@8)Gx`yyyidi2eeVNho&B(Xl2YBchS9CeN&glirLrcMPW zm$>Q5UWX9Sy7#-+OJDxK}m%)Kob zmH5c(aC*wg;K3XFfqrT)@o?1zdT{pS(GO>@9}d2SqM+0qrKGn4E9xD!UG)#l$4iBX zq|51=ToyC|e7D6fdbo~fS-$W<=c8G}{PV74@E7M3KBG1*s$(&9KEcXHB zZ&RyRZHFAAO$BxFCp*s49N41M#eZ9W(I{eN>hlUIWMSMx@w^JbR2ZbhjAFecdf~>n z(%d#5=z$fPznJ9W2aEs3-EY3Xypk@v)^OGi!ru@W(geGu`HO=utJWzLA5>-O&F|*n z68BRW3TD%54tZ`pTXpN-RmF!fVq53HcVyjF)Jw$B;>p4P&)&Oi z$8lt7x^p~5RP{QYR5t+n{lcp1N)Uu3rU;Tnkg3X?u>A^z7>Gb30Fuxs-=TYQrakDj zdT^#^bY}G?`6TDt+sxzQ5gzb>K&3K;>aG$I?rv^ww#{r`|NnQBT;WG#RqeIEIUCmb z{)ge!KEVWFPEQ3l;xSLWeXx_@zY(OH?e(E+d9-zLxdnj2dB^rkL~V42R{h6_;@ z?@lIG4M3{HVq|8_3r9jFu@<%<68L}|A>?rlR`s%x9i@eMlL?GN_H z_M>jeiz^pul_U;)P`)aP6kt)w3s36avXsnYs+%++If)}M%%Y?&3XsaGAX!Pb@e{{w zr`ssB^&JD6x^0y~< z6Nlt(!q7i$Cd{R*4L4^VQ|VUafTxo206T=A0v=uhKj2ck02CZ(<^g07b?gDmAJu5| zRKq33mluVgo%8UwA_`r`dwe%hi0>u}{nKBt*Qkni1n>m#3rNoMl03W$tU~gtsx$<2 zg5eI5B!zZ?T_Q@!ts)Z-WE7V?y=8L_>z?E){~kplP|Wn`E~3!eDT%jvDfRnx@n zDcb7?O-7$SQMy)5FkQ&*_+)LC)5)$VpyFpF+W|iuxzj^&Sl{SFs>`B68ebdlCwDm? z;U2`%EqbWv?>Wy66+q3Ve{&07)M8Yr#gtwBMp7XD!PqaKDup9k>Z*K_yiPMp&Lwf) zyECy@WT`DT+_l&G{1&umx0Z&xmI*CKr1m6Y85K>xhJ zqDu1$rmlp7-z+KXgk2K;*r2RQ|Eq9ddUXtcr!Uy80W3B^d=?SOrY?IC2(L>UBlUkV zh{;<;4sXp1bQhgH8aYh)!;RU&9m)(-+_0SU;1)o55*1~T{6q-XS(LGBbnkkk&V1rc z=HMCTlLQR*n;^{o=>;-7_nmKKmWj92#yW*1*;cyxYhbm9(-dvHtYC?S7#7+>bnP&% zNFenhz=FdJY=O)vE2w62KhMG3PT-eIb4jde6#{+{>2mhG_(8f>r7P@^u4r57inf)m z{z!HOgl;Y&oD6<9C|-lK^qC_kOF=AjYcjXk$O;U;kSz-R{?E=FV{Ne)fBEzO7&MisbKJE%G{5tgKmRY|?aeM5@Z-|d&oyeL z-g2^7I610nla_4i4W%BC#iIAUoLosoruB_yT5_q_##O1;zaN&T!WFc7Wd{8GR^G#Z zAAQT~e-uye$mC9oue^{y$c8ql(H9fsnY+Kz7XD4Zp9j`CT{BYB3BB^1$rSOOfk5LY zH*Y$hb0hE#B|$3~E>2fA}MgYll+^HdEhM`IOq$ zCkqoZs|I8eXVO+ycY&C*Pw$>+N=6sf+)Ug%suGv`(Pkfg>m&(;w$Jbg|{&%x{0-#>r$ z4QHPIxtJCB{>Af?w?{8I2XAg8^4q}M*PMhIVaW6SdB65g zj^4gMcyaLNc#g^`Ad58f@Z(v_&t30_SOg9y=*_SjoZ%Ybm@z9`Mo99?=7+Y zF@^wu1w^JQAax8u5(0?KtCA9i$07qyC@(#@d2reW4p>uYPE@lMp#!2!R)+ZT!te`A ztgH2D$%DlH+!E_;kXWy`#Cp9Yb_*=M2nJM8^kUGD@W;kNe1q%6sZvOOsSK%uGITQX zh@id!87U_%C{IhY{V>K>3DYkwu}-o`!}%bw_b;)x14*Axh8SOxk3Pqcp8vKAA1&d* z&G6A3wvB17ZPknxNJtd6c<}Apx33?cP{w)^O4{@1n+E2EKd$hDqffs$k%Wn{af;)p zmaA_MU$2Fqo`1_e+)XQ{kf^j5qS4AfWRc3h&HZ~O??jc}=8CbF4u!AOR#-MTJBlMD zI=mTPt*cZQ`s6}<^$U!z2J~lLVcw4yqbb=3LrP4|P4Y_n_nhJNk%H4)lGM`K=u+nG zYjWcczxSfY=CkH4+rMWmf3>gw*5TNh4GeiYnxjR5rRfQ%)c^FK{Pdr&JtX;sC`B^{ zwmfrjD1ZO@<>R;SUM>&f1uVCthGr+T`8~5#1#nb$1$n2sG-yDIb7O{F<0=aX-ugmK z0Y?Hz-nlXv^4M|LGv*s(s5YE7nr!q@cuGiX%BJ(T8(bG_Vc_L{U~iu?cGdcbc*bD`?dE#=jm_pT!3N z+7r_L{B>()+Ydcn5DX(O?=HYesSm#=ka!z_9Ien&F zCPQ%=Bfv+L~DlR{ii?cGN=b^!n|e`=Dj z8Qf+um?0_dU-ooR?h~u%^uv;)TW^I?7?ht6e7;4AZSx3jO1<+<@m9_Nv zzXH(lCZY2C3YFhisQNplsHgGqI!Q?~^~q2sP*UUP6xcwP5DQQnP_a(zkPZQO8}&e3 zsPhCnze;eldTkRQehyN{9)#+yLUm6P<@J>)udhV)cN&p$7z5De)%Z69a+JwJP61Lc zNkOG@AG(Ery7(|Gd>=+~b}|R}Ht5&{Fdc;cBx!9^;$n6D2Z{RGfVVfvmD^XY+`e+P zMQLgTLj!Ctu1_j^jO^rookIs#<#;KB+y|N_15Jbth?IkX2#+u~lHi&HZ4EYi(b}mv zJ`*o|kgK1O1EM>j%Z>XgR&ig&D(#e625R!zJN}L{k^qgp`WG6y;H`tDq$ZKs~$qtSFwuwDpv6piWSVFWgvJ* zfI7lQlorAl6A_AVvI5DegD9(9kZPht7(|?+q#E0+ef|kApr5q2D`>4^|3RpJ7R9Pt zqWUUUabLwM_O<|R6N)=>8l1H9IQc>khGK{(;K0iPKLSUKDvAJTA*aeq2vR+ebSp0` zU`>r-c_>>FMb$spqYo1GGb&cya@AL{iu)>7vAcx{h|+}8o)$!P>#PJSm&x&xAQ7Sx zV6r?mEb0sMEXth>EUg%)f=iu|sP19mwB*W#T`i0s*;z+1KVTPLtRtjoZ)(O|=MT@!f%P3Y$ z(dY-os;5x(Rji`EidEEKu`03jiVR4yFob#uE3O7qFT>vn!DlL%@Q79cRw)Va8DxK; z24+=JlCX8x%w*+ z%@G>5G00hF-SxQz_ga6gmHHNHooBQm_08Y&J3zgpv3BB{q6L5_bnu*fhAF8cQWH)p z&1>yRyT1Hj`a#OQm77YU@XD&Dc?ytwT@kgX6z8}C(_UVtMdgDT0*tb3ExkrlTukPU zd7XLUN`n4G*@sHmrEFZ>r19F_0kQDiffRyMHeTASolBcBGn!nNGbXI7OU+D>#x*ey zXcP*IMkASti)uoe0DYBiLeMq3FtzS<>qasI%4>yurvM4e8LHfOZGMNPWBbdW|EK0# z2n449b!r|4P*y|5cU=TG#!>%d*jLJ`SdmQ&tQvs7_b-uNY@j<(H-Kzah6jcUk6Yh;j%0$2a(MakN8Vi z){c}qQ(EBr1+khcYOCEz1haA+`1dHEp%lclfEga-^QX#Zx>-E8+&+UXx6h#e_L%`r zl=yj^`x#j<9=vl&R7lhh(&q}7djgFGX;)BP!0!zIH&t6%Nhu6&&>-^f{lfj88 zn>QJvUrQdmebqW5>RruFzHDw6=BmA`$)2E~*0VAm+RDa}*y~K>!Svsik@bomMIZOj7bxl9~&~ek4R`I#kl4;M-zf zOeRy?li|Ya7RFa90@T`?TxzL(X_yPxZR|+B)r52N8Fr+WiQ$wp;;dTg_kvk8o*}Vb znXxlglvci&ym7r?K65Mt$%Y?8e*8FLEmVUK)y>q<*^%9Sg@N^>Sy>ZPgJIdKEs`_Y z;u9$c$z;^^Hv=$6g0}tCg^updzQPRElfll;p^oH}!S_m$tm6s{@)A;-8M^C>oo6;J z|A{uvv+~0XR`@SbCL?eOyRm!{hhc~YfT6EjjxoAJx=4G#s2aldo zW%EIni~wnX75tS!e^Z18y`&cE;!Ii31oQdMMW_WVxfwlM-gP>elxFeF3%l-MoX>aG z%|H^1JMfl?`cH>f$L7R%{9=A$^f1HMJ$M`Y1z#)0#kXPQF5S?P6F+ZieWCKEaL~F_ z^1ZhyF4XbF7IpUUQPd!E1&)Ln!Q~3FlKdKP36piX1h=-K>@*0te= zb#=z>W=>d{#C3|-I6E{gF6&o^QnEGH+9Ye#5PSOuGJrlv+MB(VZ0R#d;<$ApioqQWpWbeLr2g!o!$s+zj#2WqO8vQ zh=;eb4qG;oY7gPtqpy6Fg$>tj%AzX0{WOuHFws~{Ii9$7bsQ)SJH4cwRXUIj{-g*< z;zyQZO{j1yc$r4ZHEWA2>ub)ELSWv-O{|T-n0(qZH)`XC%hvK#d^%IA2;piFcvT%K z#?$c70$+MF6-1`d&CbqKW!p7Gvf}JEr*~m_Nl^;)U0>_#!5X9E)*($1^YGe|1&gjI zgVK-$oAId)3BtU`?=DU+A?-EaF&gHV$eV4zfIpd2cXXvS(Pji?o+`r$%!~ErhT)8@ zR3}^pRVlMF^E+QWTs-!*h<0G=u2CN@DPOBDwe>^Hk4%(v>5)_nGGKBrL%+a4nmIEA zaDuuvA7(k9&8QlI6UA~QF#+kUZFwrlRJJ^GflFC~1>X<%z8(%9jVH=rCQf!G6wv@f zR;b`L#2DpQ${BVov|u;m!oFi_B|uugV!~t-kid!_vR4b$#;Be48|{afRfB#vk**ib z0H7P4A|TA144f3Pa?W3DSi*$Y1pWsR)K5kmGDeI%XJL1*g;bfEOR0n3 zAH2G-W#=*{uq&erp3~8(HF0RtY;!ZbuM&2W(IgX!Hzo5hR3_;DdhL0X0sD(v#Y490T#cXCqqA$(X zR-)psQHj1Wo9E-%=DG7tGkX2*-;-NcYCI3vK+Y<-o1OJqqQ2x?;!X=lV$1sBx;y$R zh%24!r5&z3wPko__4!SK8GCAV1CaUgzH_V689V*LG6&OXB_*-9L}r?|cIc;ZC!fQH z)A+qPjn^xxw?5ZfT}2(mpNO-ubZSt_KqvNKRRh4i0(&^~BdYSEkfN!WI1L%&rC+9a zzd`jUBu`R#3l=JkoQO2@`%b^~mQi++I1D{N)*_tqiPYaDbyNa;n~*x1J4N2t&uD-A z9;_Yv%i+QC(VK%^!!ELTVdj#2d35mR=^xCcXGfAoaI(8er}_o?zmd|QlUD#@hE7tz z&C(Wh(gNQ4Tz|4SpETf%AYcS+HmLn+OzjqRqba%OWJ%?z6Gc(Zzhanr5fAuyQ-NYi z;d&%uexx~^`LJ#wn=1W1W!Il+1WYOg7$>a*1+lF9Z_z*5+v63(+@hIfj(j$@G z1rVIM*H+a0Pnr618x$97f-E>9v|U4eJhhs?m|s>671zQH61YLBTEqn2`7eW6l*h() ze13bD-8>!}DnJR-sIe$NPp$<&=PJ8+BKF#c$J0?&U2xYyYakUSY2eZG)RjTbNWC;h6_(I@oPKC}-@uDV| z+~<0ay?!)xmt&wx!DpH;&v?syB;UXv_0@3O(I zL$qo$& z+RqKne5=7U%+7U%tL-9Ge?2gmZ+W&8f%&)`ypj6_rDuwSuK?B9_SRt4n4z8B(QL58 z$zyZnpY4DD+dt#5E$|=wH$ORl^KUoNE0zy*M6!!Zv#L z&ySmXS1hnZFhw^SV1CjE|2gTzCiS~0IM(gA@BJ?RjgGtf=V*|Y;RT|LrVdnSZc+je zK&`g#R}oOp&>DM!0$q~9k58R2Kv={i4^fgNbiwIzW8|;HK+D z;I4XxM9cT)zp!t9p3lp_{G;#v<3L2|U;eS_*Fulhj?_OszC}|`C_d;i==^xAH4<;N zM*8!@5kPWc2(qbu3PDGA5phkJD!PZ342wK_*r$*rpX5f)4aM(-T z-A~sAt1Anqyp-ShoK-QOP&A^|FOE944QhU!9h{X(&@zRE+>`2k3E}=3|Q@dZ?>KPK3G_P zGOuG>u%)o=Qw`k`cBEB_hwfU8_g!%}*l^-LuO}4w);b@an7&|2k8V_yPM&>d8U}@Z33k2Xtn8_2C@yoDiVKVzww#GY!By|1p{4$v(dFV z2fO;VYS+4C#?y0wFdwVyVyeMo35o6xj^>FUQdc*fk+Yh<%R67m9%4eYW;UffP6({# zQCt0bJT$`sCsFnhJ9x&HC8Kjio}c9WY4u*EcYkXBX7$KRj;)96^?VzDGI*s40%z5N zcxc|@u#lVtWCH=q)Y)~pvb$+&rY>=vo{z(+E*^_GtkB7%V)9;7mv+0-^1Cl^u(6&;pJyH#fF4WFPmyU)|`9)2#2kL zs5Gh_pks+xgPTwa%!>;>j00U|=>fm7P8EIn;@#2PSFhfF3*qC-mq&+hdlriuW%Kom zgFie)wcLIE3SD!Nr{&~!P1O{rELE#C$&;(P%`ZvM{4&*8VP}{x>Aen;-s_;hTMs}N zfj|JnrA#>nV-k6NVs-(9Sg_iIIt4sJNMJCf>?%?`VxOw3F!O>e5A&$SsT;aU7=`y8 z#dDK~b@2DH4uZ~g5cggOaqo4|-*s1$n=T}}6l(_{`W0SY3yUsU>J{E>pFp?klVA}$ zF>;s{9*IF=Mx{0x7c7yg#r_+Fq2t~AI&jj5b?`U24th8&rs!tZW4`{gIwu!=7Og9* zP$%AImM_B^z0Pg%8T(0FT=rdjFH|-Z)2Y#^Weoo?P%aXEzb3V6>M)^A$r+d8!Mc{R zH*30FPB6eEtM<|s-K;t`THiz^{aUgw4OO2=HvKEv8YJc{mXdaC*8Rnx_FJ*uB$$3? zNP7m;@nrI&36_7#KG-V35u@<0^9C@ers~De)Z60fT5`(8Fd9h8yA+`rgcM^PN*ksk zOrp2sw+pwG<_znXEQH;^ohWgY<`6G+Qq0fE#rz>tcXcjhV)9m7$Edy|9Z;znu+e?V z(YK|Vrod!o-)s4zfmIQUSt`uo5d0MuDV%A6m4?Rg=h)4jf~_o?VnoH?>2mfnWu&B zsu)7cA%%UKa4p3Q=ILtuaY!1CCX~-)ao-S5Yek+~$R|P@#>04>p3Iyy<_(Q6z)rTQ zNW7Z+3dHC=C86Y;hSEd?6pwbpI*yvYK=dtnkob|o|9dZ+|p zHWg!5lRL}NcxH1n^u1DQ%ue=kcxuSfO|t&*vY`Bf)oGne6lu;xe!iA1lFwyzdM)d~ zjMiA@NL(bc#0JF;{jzfm%8yX`OfI!Zm4cH~*pQH*ZmV6w`&VLUuZ88&c{{{g@L~5b zWAWhYv-Q&lr}EeCiKz&-Bt2UB(uLhKZ)NiG(Sn^K6N>o7_KH!Um3N1LNOw~0}N#8gq4S-;4P<2d#EaY8npGIWRqSL->w(=E$-(U3^>{dKr%&z_$gJbiKW{=1_;eDm($&9ggVs;$+FRD*f{6s-%T>-e<VviD`Y5)E8JE0Ced~mTeX=0Hz#!BJKKpB%+uw|gXIQZ*d7n0%d7eHi?^D>aa$WZG=$AIWY~U3Ga!mb zBZxNGZ3g|QlFag{B`4M9G|6C1Bl|ngrD&S4aJzMdR^4rwTFJ_&Bu;DduBXP*Gezzi zKyE*NQmI2>x@YZL{T0Kg8ei{kUD-No)mS;tkB>S`oDDa&kV@M^HA{QG)f8%d>Ejj2 z=n?X~rH$@9?`lj;{77(+o{=rElINXGKm1lul1jO_uQ0C5Y2NEhyNii945p9IrSYx+ zXDCqC0EpjW#kFqfu;b>9tvyngF0G!b{r7|S($a<_cgvRg!@ShFgK`?yX_`5?m!qPF zFuu7ZWn1Lwgw%P34jJ8*?*}!dj)g;YEgtd!5Xn4nmkE|QwxeVfJ<{ED9*4KVyu>Sc zt!V{lV46cNR;I$_6OnA2)=;-rnM;Ig&3U}t2;TLm>wCH&?EAQZCLi}lpQih@9((2t zS$AdmqyRYAb-KauT&cm@^VR}jkG?-?p9ShR`c$d&==&;XE0pw~!6R<{s_orf*SCF2 zwAVnb%)ErA!fAhhf4Mw~AINiBc%jm>H+XJ%(WFYo+_uoe{lUTO?*<}Txa_7=v)2o( z^>=x5H~CXj1@M!9k)<{rDx0`wyYPrbpPuEm&DgR9E5WoNuG#%wyzA#5bu=C`R&(0* z1-ykh#rnV|dw{Ps7LX9@Pn&gmo;frH_wA}l)v~1E4Uc*G_)3#%bg<4f;Rv`BQj$hZ zI+rH#nCdI3KU2nZ%}~N`*}TkVdg*A3Csemo!A={zF<#NB1^=*Zimwt6Z6#h%MIsxE zceUKtnUg{(Ern92w_t#r%N(kceOS-lC6I)}>8!y;H}X^K4PA1gTg&fe)WRXNi^dW_XDh0R>WZa!__H$N?=QqCuEvB#QjHJr}b=^2h?>3&9X)moud2@qsxq;VBSO2bdW(pO0s%H3Qtz3yHn}`)c9`j7{@6G8 zg0fmzk4&~La6@=%iB4Gg85*srDrb;y6Ln-8L9$mxJYM6S-N!AhJ4cOp|{ zc9T4gv%1bei;>0w*>XT!K@dMF@}NTIX_cP{CaHi5^fS~vzD3Cp1Udo@nnq?yFNcy8 zr|m8kH!cn2;utSoka$Nq@0BzyzWfFGX)yjv#JdnQmR;0NliEr2rTV<1D`mNp7Ms6W zh(PGTl{24ovq&2>K(gYR;KYT@&dzXbkoRV9+#fte{md0wsG{QYA7qXs4kI9vO{OS` zBq9-64hD~oIT^HOdUh;w&FqtwTemou(`NIW)hCkROIu4T?xYe>e^PNjW$O4}9M?b0 zFd-!+jm*czVYaEF5*yM0%Z11_UD3faIlQL&RX%G>HRTRZ=!U0=Jti+Pv-p|iL@Qw5 zzkfO#k|zeDaB@u!>!ay4ryINWzkX$BLd0ZeY53VJ1@CYyy;2xV6FZvJtHN0VNfo2O^3Eq9ecl4ZGhOI%h^{&Z<|6q&`1&xvvf`XvBa z`#%bx=6(T$8X8Db1}<^XGnrW#b2b~>HQi>%lLmBmX`9s@b~*$4qUUG{^5DhjnjqWk zE9X-*e~T?e*YKVlG9`<=^5V&+=+Eo)xRD?#NO-zBa2&}wKBj^6!RcFea{ zG3#&-EIzPx2y{U;?PcRH+`nZ~^Wu{mQ<{e#&O{!c%H3^O0L%5aX5lWn0hWz-2Vg4T zQ*VRBQhVfftA5_v`g}Fh%38C2of-7rm_h5U_wEP!N1L$A;dgMN+Rwc-D5|m+sw=;Y zys)aE{i`5*s7P{)>mq<(8>e(a3R^0$S6&`21F}Fwm9&XPC92kGqKiAc+eDvRoC_v; zMy@p!aFs{xD@d$0-h?Pl5*+8m!2-AJF0EU@S6SmTzonXPJ!bxP4rw>p;E-Pbf?H}g zooJ)5McWEnw5_m3TL~MvkPzMY9^~3?4zqGXbS6yj`ot-j@G;Jp3h*>9DQXClQ>Hm6 zQ#T}b6IYZ)C(99<5aC=AwoP@M-p2Th^?oGxFKmCCk`->0tZ-Y&3b&Q4a4X4j35Uat zoa1gxyvh&ByDifgG8%E{<*sn7P(?)aCm{v~VUU!UJ-yKUFz z(OwQD)Zo%yTn=Q3PJ5x6?m`uBwfMxMBgl9!_5PjNlXGN$`<%fg66*{l@${@AqiC$O zLO21)uiMikQ-v0OGN^NJm;d7r9#QQ%5sw6P1$ZZ@_!IQq@!qpH$NY`CssHCQMTaGk zuFEU3I4{avke~D6;+n;2;3!4tBjz$hFYPZ-t!>N>C4m{&zYDQEY9+K?4zJX`vDMJ)d4qn>SQvQ4@ zQhR7TMz_CRMnvRXRO^nr-&kKquG}%kJB1;p)>Ah4>C4Kj zrQNwtPKYok+Bl6)PR9z|w!WFKN7ZLB@KhFOIpv1}@g)wN^$$Ir8Fyf@7gm(SMQi>g z;R#^>%LOB_W55*8Y-lKe8uQUi*PeAN2@Lo}CiEn_ZAjB5WHCJ()>rGBofVnOcUjyr zgHS)Y23^fsu|G;V_4)>W4{ZAYry%VZ_lt#_=SixX8{zno#Af;Tp13)bV?~`jx{8b~ zz1YP1H=WqvySTTNv!^l~JOHtsS+sZA4M)QZ6aF^&-!n|U`O(8Rcvb9gmb6R@xo6nk zCgNj6mhm_1P5gfBAoC1`4hfKI{5@tzXQc!D{jnh3H~8-z-#@gN0k%1Hk6O>XPMIL+ zH#|QI>mUGu>RPm8Z2U3p)*suw_-@JBNBULfq};Wm*Wr0sytTLI!JPN!kLPDT=iKAw z%-`sQ*!<|@z0M4SgHiQiN*Tf-f-_$ADiDsSJ|o({J(WM@6wcp;p! zg4xd@0hZiyw~pchg1@=~%sU{%4=ZvxgAb6PBu&zLO&PGd;({VirY_mKaA8JXg)fB+ zKB~W6#S2&1yLHSkcd4E`K6r_4y{hE*uBz)`{p&fCUnobngm5 zbrs&jx(c^gS4mR)Vjb6#3!B$joK#@ULFHOkPzmQ%QU@hq200lxz$uGkrHJbaID5Yc zquOm_gTvXBtmc&ev{W4GwcUqx_4m52g4@?sa1ZM$xQBHWY{Lvu7EuV9LtRH{8l+^h z1*IDyso33VPI*R+J3UBi2+wM&tK%Rq$a?e1QIE+gD}J7J<-`x`>hEA(-Qq{P+W&EQ z_2adqA?}Y9(Cocuy=Ln!wR~j0VtjVO*!hZbm#+1ZX@diA1zVl;mtA#u8B1oaHFk_cYgLmCs^# zS#H)zD-Qk-JFje|G1i*+15A>Fc5lw2IkmCr^JcsVpU(zC>$Sh3eG5f?NL>HQUbQ`H zxSCc%$%lt~PyetNbfuS@R=OaumM`{=7T0^DYC^TF`9`l-#JKg$mQmxCH?9VZ|0t_L z63yFY-E5iMYS+1>Sl-Ip8TaB(dRblVzO}qtx3VbN_pT@Y=z>C7y8*oSy6{lCZto9@ z0`Lz93h|60<%sdq+T)-E0VOP@3>Y9+(2Rp9EXq7~*l<#ktUZr-h)2a$9o4M@#w2x9 z(G8cwF&lZ={o4Rr!!}@R23HORUrCYj4@WAKiAI97jP;p<;^nP56&Hdpb99nB@M>&_ zgyVFUuSyVVoB5BnoBybPERJ&TBu>hrbc+n^_mUVFLQsP14tOsLP~`fKm*<%CVeHhU z3&B?4JE4?+yQPz~=byIR@V@82r4{`R&3`97Rk+>!huh8n7I@9z3OTOO_IkiEmwu9f zXzr(F9#K;f=YQw@ z_I{FyzYG;8$+Ez&^8ypviyUCyGCz${e(>FzlJg2bG;nZ{CubtLC{ehK(Lm{&6QwJR zkp54iY%r^IVTa4;?8#e$?K!T=a{>cfX@?B)>!#q z&%gP@dj+&_fahEyTzDq`(SNL@nmSGmUSpri+=mFIkN95+U#1aRRlRSv!7ZS@x2e==>^zJN}-kk;0 z>rZi0y8zk%1eF(2lo6TpGf*)}AEvw(8=wFXiWr5L)G;JcRNV%CLauQV)TmxT=(%md zjKbtWFu#akqLAK?K)QDpNcYYHxdlL3kTb7dL~e2D)NthbNq|V^S&mp%RD2_PB^WV9 zR0;-D9^_tD;E6~$;mLl_)*Rr@gFt>Ef!xkQcDu2H@2phDcUCIn{z_#jwlT3Il8@sE z>gL*uy%_kSH1fgO_T3nY=QJTqgHTog$0H0S*k0TZc2H7Q%V5GO+vk0s-f_Da<3Xvs z53#(nQW@V_sf_z8m3f%r&q+z8$6J%t?0zl~jj%G~u>q=%+;yrr*#ANYn0FU!$A}W4 z#4D-rK&33|wphaF>pY0%FG;EF6wEs-mGPaG%6JQcg>qLi{_5hWdQ z6ybL!$eo0^4>PPHV(zsQ#X(!Xyb{6R49rc`zc425bZ-mBAe8YJ5XwgC?o*B1Co_;)@T{Qf6HT;TK1oQhKE zs>+}fPjavxDv5uWVc?}MwfUs!FuyLY2m<1sTa=k2z~ltvi{)Z`;QW2?@{;?O0@wLv zo(#7~=3yxvo&p7VK0UW=9L(v(JSM;XdMg_5h#zo4Wn1Ox3A?a+k&4N<9fr3RWW=kb zR19!6UTJH`8qSat648{>cRYA;d}3jO+9CMYO#ok^7JuJv%iv*Yh1|e&WN@q;pB9r* zk&7f1(v;CHzIp*HP%;9RdSwufB(E~B7C@1jBunXT)AB01?(y@t>yrTH(f^L9|C$^6 zM*a__ha#hc=GnfSp~dNNFCUI&Y{=61 z*W$eVm-o}X&(viW9~6=JY$9Nj?+uREdlroM@803tXYaoC;@_t)^cFKX|8jjf>Vo1W z5z5)W4YSAX2iaqr>R^R%Ht|BNU&imm->CI{122rbK`+>?zkl`SJBxddUbHZ?RDW{yg8usY+gC4- zY=7nu_#K4}-E#4zY8jHd5ZnEfM(>711BZQA&THy;+pkR?+J0&BQ16u)QBN0E2~!_P2a7d4iJS*O~+L6}mm-i#?w(&17)C|y zdU-%rN}iVRw_+@Zd3$AY1mEYG846I;LK)V>%6wRvKOJLB25AZ%#t&IB$)`FvQX@4t zMb}(x*`aQ$kaX1!h1w`uYlr^F|DhXy`Sbr!L{&kXJrwrJRZy)riYG0guBH~sl9w3l zmKn0^BL266|6pWLayR~9lE?+-cDR}`ZaT5o?t;azUU;W6s2Wzz98?|wqH4b|Gg!TX zBUCl-6DV&1b`o-%VZ#%k1fA9Cr9njE-;)OPOXx`jw^}f2Yzt7ri1{^~3Ky*D?&bAZ zaa$H4ybS_Bo!OQ5OcoHvlqqLY8C-V3>K5!RDT17Ro7m+g+tSr=YJg|?EXA}1u2x^T z@c2l9cI0uzvj#TYfN$>`oZh?|SG9nGQyi&dsO65$ixx(Vf=GpN#$O;@t(K$Jvgruk z((pp?kQopKQZH_}_xZV$2UJrF2R?(`(OKsDNhTK=E#9Xu0pM)#7DV|gBLfzH{j0m7 z*_X=xlvGHfyVg>EC*0OMAcl^d=+!M0j+5r#G*l=klyL34XhjfILY-)^$pXBpXr;Njl8x5Ew|ml-LZ1KGC{5pqMVm!3T(zgT#*G2kv&pK_8a7XjujEsn{$X zuL>ZegwPd7vBx(gs7=u1)JX>`&YF?B&)ln@ic1m3of4C5EiuW~64ReYi8QOsi2}i9 zf&DX%3XjCTB*~E;KLoNFcYIKV83A3NG{DHIaO4yqJ5-71)PAy@R^So9Ro(oaB_{Ya zNKDizG4a+C6K^dsTU20!j#WFrCc~yhQBneNY^nvh1!jbsu;Uap^_h~bQr2NoGhJB_ z)-loHI4|q0B{A$U-aYRzemlGQ??PfW>*2zyb1H=MXG2cMYX3YN*?7>pD=ls=@R7X5 zeR;JmGe$>Wr-2{uAMC%_f4$#UxSQY6i;rJFLnjtZtgSh(5GE-Khvf6ahW|5-hj5gmNBzlH(I&^jm>qad>049+h~%adbQ^nmz4 zp!74S@TZjpn}uja$SD*~bZiyy8T;2KRcU1bLCpw`PQpE7Ke5&RRt@|or&yX+66j0PC#>bP3VF^JIh?zo4p;TWts_@I5 z`vdA~7w4q8U5Ga3q?#fsmc(Rd#}W;p*Zt;731sxL5`zd53Sf_FX^7>qEFAiiv1@jh z2TR&hc8*M+H+Ivj@&<-A`ts>)C@NsOC+pSX z8l0GSJ|28!Y##H`b6z)CJSPTW)6o8iv@_0PnQh~d13rbQ!&vo#}wWS~2h>}R;Z zDYwH5Cl(777}EUu+|GdQf?~iZ`-og)sINqHuCh}sp?Y2p{gp_IZAIXRhss@Nnw!Cz zT7+ML!+BKgW5|A^oGP^UE%qIs{{509%g3k*XJ(T=@!HeM;-u=2fSV4-Kh982+e-#g zR(v#-u9yYJB$`ocQ9e#rObtTonS7fP6sf%EcN*9m^BMbtot>{6cSLio0ai!gbjtY(YxHFzfVE;9UsZ;~PDA2Pb+~wqi0f?Xr}&$Vy88qk|y#On0rjywtqq3e+Z#n_01xMz{|Y z^j5B&%=o2bY6NTGk?h5~oD>fFPuC)qnM$FIuGwqN47|F&y12e-I%oyVP#oHQQaiRV zkGn6};P-1V(Z6OY;Jmx~C}Q4-vBf=r1yki^w^_RYJF^ApJI!;7J7ASF)^7Hme%Og3_?MgGBDEyK3O)ShMNfe#YliK5`*!sOZ3|CCdw37 z_C|wWr38F%o0e0M(kp$M&Fav+SU-WS8Cdu^aT0%CDeA+O(6BL++P03`f;&`uaF|`@ z{M)cLz5oPC4#KJ3wb`@_y{-#j)La!(AG1G;*A2W9m7SqTbydxN*d16$PeYO^BZN2T zqc5YPzM$p~!}~OyGi@#{OZhIVeDu{gcJ2tOyhQ4>fKLJ4C_w*%V?9e=>tT8amZv^_ zfBb3qVK{w!HaV|ug=SuPxECt(>MQ#>xw@avN0a>Vw|LIe;CGY znp3GAQWFbsWrcsLa;RG{ZYl!UVoFwkUz8MtEoX${lK}N1-U`REN9BUE?3AkMZUSCV zF2FrljG$WjWtHaMh}OC(`|0e^YlY9< zHcffGIJsL1pSwjJ7v6~)%2z*;>H;rt1Jclw8ai-*qC6t8@IQqhMLD(O*AA{O+@dgZ zCjsC9Wh*HAMTF09v-dm*UvJ^-%Bu^Ut%T3nO8B%6s8h8aVaWKAa=0TWTlf#q#pG{4I4GgI)g zI^~}u?UZr0ygE82Vm$N2v>3C+z8hZcF2;g)mF3qG-Iz}06<(&{#JHkt@Oigkj6u4v zgBqhp&6Uycd??+*^3e~IUh@6T*T_?I66ojRixS&J80Lw%ODukgH+mJE_uzK4x-Su$ zHqzeE*OM7u>@{NF*5Lcbo;#b|7=<=Zb*bW{XYa?i@w2XI;+Lk9K)rSUP3W}$`SH?E z>oVH(J?o2)H^S4e+dAII8`0?3Khj@k52GM10Soca#G@F_*4zzZ5GyItg3JNzIT|Ie zjhG>cV?kc#g%*2(`0j*7w5&F{Y+cX~t|YUiKQ%76OqP;Uc1lc=*iIsa(P8R?3Y9xm zB6;lebj{J#Jyu`(%#ip}c9xa$#_VdIE;avc^=yb2X)IRH26K7!Y&g#jrM2Z_fuA6u zI=!xp%azTL6_uN9)^M7OD_?h~OuagL)>||EG{ikqS5>)iTnyRtTir)HyO3lly=2Z4 z?T88TSn4o_ZmRCRV%8mHULQ%GlsE{_ByC9g^aD`|5{E7UEPsvPXTouFK~N>bkIUA1 zzS!^O4_fFuTkyEDx8s)E5x>W7^NpIkBu>%!2O4)6eYm{Z6W5w@rcd`5{lbKs4$4y7 zoNDXRu(I0C-QYt_bKw)u;pCH3KwVOD%nD7rxh~i;##462d)6vk8vF91CB+2p8)9_5l-b_y?qf*avX=47V zxKnRhoMKOuuf8aFpSX?r!ya|^T-2YM6tSJ{p<17hiS$aaujBk%z25f-Bz5nACBczp zS7DFrtkyH#JYqR|*g$UWb~4@OV)FZVn7aH+n@UjU8^QZc^`?ur^@qan3Exv#oN*9Q zWDJZlIZqMAh{2$A2Mm*w)|N{C8l~RTdQo=3oN>W z{~AaY_OfZD4-sM+5rvi5^jIP>-&dC;y^iW#bH=^7W}Jm{V^WYe&Q?}lYxvf@rO;TY znkdgi{U>brjBhR{gkjL=1q)+eW5as4ghNKsD5q)75MNWsfw6f?ku>Ft8~VpG<#0YILZ1^m;shIQwDY;g8gY3mK#YZVxPl z>y?C)tl=d6+%@Nt4a7^_q~bdTb6d=w(=m;b;4zUPXU#!1)Dh5wQqE4(kVMd`jQGp^ z>m}hpzB7}fZEQJk&qUjAzWL?w^z3R+e|s`0uQj4-;`!{0Q+pVX^`;L)B@W%Yn0%51 zUrAHY@RsyauGt)I^9@tm{Wh{9t*}>CHCD| zf8~Mc1(B1T`NxU9JRGybGxS%(k*HgSDV4+RY*>kXE!og;Z|-qxsSL8sXMb&H($3AU zi9KFVuaydibxf4Ad3(d%$%#6e$%Sp_`!iwtoTb{>i))*^lo^(`#>7`IRbyi9h?L9} z9@`sZvM;<{Qoh9)9+^HEsomL`Gg806aJ!aJpJ)W*QzFA>lHV;GY&Mqc>!l#xgVvruJ3RDOJ~OaI=Xg@82=D_FDImZ!!sNt<@#g z+|XN@$j;xeJ06?e@v+^{`e+nu9^5kNdud!*4?gPe^Y_= zhGXZwzp2K0^Mn1tQi=e8_Xxx7OCm5%feQ{n06d!E z%{&AnOTpF2Yw&Yi0i^_|G6ZEm@j%-r!?{Wv0yd-!W>ST{C=EUd*lBu?($H4Lf2q=N zd*<}{=zL#ujOHoRv!7b_M<}_{ZnD`A7p}KIw6$gX<498$HPSE=K$^?p=Oz{NoNNgT zVskh-mw-l-jh0=BhE5tD3Kmwz)mQs%U)<6c?7N=tu6W~?+gf@x$+tEWRIQkW#71F< zSKrL!Le%wheQFN@n$zq^>7r?EXE|Y2b1OSO<=yiolgl`hrXP5XB!L;`9tn?_lD(lQ z{(Be__|Y^sQB}{}k9Y^Mt7o)C;2dNzN0gU{}>=>C=hL$kuda+-p*Bd2xZaa&kQ?B_kTwCT5B}F;N|NT&Z#O1JgCnWRx*I zS2D`;o59H|Ot(9PhL+RCaJOZr-7H3ZGQ%zr%AD~>>)aobiMv0XJhn^i77ZAY-V>u~ z+T7nm`E5PZ=}tR-aja_jN}-~xBW>X7x*Sf{&^Pv|RGf99t57ij?%+1kM_I z^+%ny(3u%A+cDnjj?tYIv3z5HR5*};!pu)lXiG1E`@2Sgp&Bsx(E{Q)6zkLu2O;p^%Z@uzCZYuV`mH>7aSzYkGOiHlM+NB3kcMP_NkLp1fcy-@ zLzE}z=E(-noun<;oCb0eG*zKNN;rIV@aE)b<>#-Tzg+$CHmHTCFWw!!ef8??x3v25 z<OH0WXe{wW=& z-^Z_C9C4a2(cSF=@B9AiccAy49KJa^dih@7^78HO@1H+?vkc&{eA<062>uy&YJ;TE zq!H|YBz%*N_wB;^=f`uF)eSo7EeqA@yHK6J3$?!)5)}YSqw54%B1K(8OX2|V1)EbS zwZa7u{2+QkAOW~<4f_rVCOH6(1z4|ea#9euv{0oa5VWv|b^5SQ?|+@Hht~aBSE`rx zT&Z5#bESIyEB?XME!`~h{0N3Cs`%47O#m(R9Usggf?;@%Y8(hf4SRv(WMNvkaYj%q zOIfQ`8)%jD*~4~o9+v9EQoY}$x-N?N^RCjQ?$r7o&$Pa-^23{e|BM*jS4{opsuMZ?75i7R^itV%7LCbd^R_Vhkz4ul6#h0kt zxkTN*wyE3KHg)^krkNk2;pSMC0?b&iyY@_hE@X*rq>^ZMtEh_O(sjzP72mMNv&mh8OfALWvDa7eFo$ z6|e@w7?Kfyj3C;CU!*F)st&8ri3(`sAR`FyIYD`p6l-kL$az?&59{=A*Ea25sZL+p z)ah%RI{j@^7oJ-PM1g-w$#WnWorO7!%Yclz;6r%?vK>crIjJhhVzk~2yR`t1DIgKT zi=(z}8bp4uT7CJjR3Db=--KhW znv$P_UCe>68bHa4Gc?P~!VN-rFoRHz?H1L8wN0JqVU<3t(!Vv^G}({3muO$x)ah%R zZh9@Gw5N+AJD7zB+(Xc#j;qy8;|p|Ne7 zx?vio_r641tPgizqQA&>_+Uof|C;J+Mml}X$SoL-JSsy$KZYBngg`g*vJ%otLHsD7 zZa`7ZNm7U;*)qYDgwYa<5(Tw{-~pcJEDT#_q#s9)?>;Q4U-y#wnJm5yi^@%VT72Bt zZ&7h$f3%%C0_y|_5npmrQ@8dV=wE@%p+XP4kLo)o_sMLo@uwC!q@N{K<0KNKB()~g zZ?9Nalfud1KLINP|OlD-Qn>ARr%8)szQP>c;Y6&$G{6|d0n!Jte|b(;H8 zTvx&!#5u;I65mQGOscZ_YB-cVq1bI3XPy9eKWKix@&$FDtLgRvV3N21c|eB0f0&$& zKV8cw45uIFRln^ktHeP523T@MdKU-h+EjaOZaLTft#ZDelAV2=$<=mAuRLSnDXzgV zkOcQd7MvLds6td&z#mD|K4`$EEntuQ*5Lb`QFE#|NZAtE&i>BHL<(EEI+Ci?8M^lq z${bBkPToBkSSnn}Tfb^BfE2EHKFrhE1611yFw^vj{9{<(D94TFb!&CA7LHD(-q^C+ zDl?FIWY)%0Ui3t9N)>>bNxD!-2eQl!6#Mn~Vu8*Gm~{JB^KH5G)Zn$!=pS#S(I*rU zj4k5n*aD%?GW{36HgW( z`Dk6O7Ijo#sdq>TsKi4xAOS&m%M!G_IEGeb85cf%Ja9+ z59GZ2-K(c3yYF6eRv*poqc?BAef93Gx%c2ub%ymsciYkP`Thnbz11P=!_Aj|3Bteh zPK1A7g42fO9d$16Xxrr-ZM(dqEtYqfBvl4POOO%wsG+VYGLU4H3Mut4Qu90jQB3fO zv1fBanh~i4amLy&$qp)7=|3RR1Yr>0O zTOEDVVjIh=d6_An*MEzxxQbKR-A#(+S4-uai^e-JYgsXfp4+N;0z{d>15YvRrLG*v zDk&)!E|}pMEG3vF6v9hLM9v#NCc_hx;X5#O;uFCLlhStq%eqvOsZ46$1;2Ux!{o-| z0h|wu%gM9=xv{`1$F{QExe5)beWXfWNP?tXUVx59gC8azz=z5wmt*m3fa^HIK%NN1 zV{v5}a8Iw5#s;Sc{TH_2!SmmoH=s+VhyVFc*ORM%nvI9~Qye711D5f+D^tgAE|`J3 zv3FtnTTrL{K?5LW7^2R##61_(E@jREF{;#2{*V91jQV)q3xmC7u&5T_qLaRFUv*-r z%uXkK_$R=_CizGC)+ahu<;oNal=W|>LKZd}?eFg|iV*|*{kI2i2hZOQp1nFc+1dHa zpa0jue!o!C2yKobc9B_@O8>C}BxH&sPzaw&KKZ8VVWl1_6PN)w0AcG2S5$tZ@+D|#pU!}t27l=i**Fu#bA`G3%b)+Jfp^QU6l;tw z$Rt~FC@0!EMM(XiT+yTIbjG|Tk_e90uKllp6u2?~cSfqf11~;|45)#?6bRI50lV94 z;0k~z{SkNtykM0u_sBS%;Gxmal?w*q`>MK7d@hT=CXjCcyFGFlB9H$K_}}F7U;g~R zzcMfl6Ydu1P&)WYK2k8nwf7cHRZzVUU;|KAPMC@8!XlTB75(nofP>b%jYSYW1kWg#4Wn>36dzx|k8OZ?;R?#i=UctJP4$D2eLh{C|@eGa)_&1X-(qmv+ zbJlE04}`?(Y-mB}1PX0xrZlq)MB8eX#&hY5yhi5jxuNHq+Hd$;JEBm!^WIMlY_1{A z(%~8nFXr%rXVtlV`XyNE3iqnqO#=ZcE$zu!VFa=>rp>QM`m}n=MT-G+9_0^4$DJ6Rv2Xg?U(_4fZQ>v zYQ}k_VKJ>=b2yN*?h z6gL=6-AW6oFB@#9jnDPZ6Rj$UL5Rl#xa*NKlTv4q7pY%4K$gXEO*2qU5F`$&TcWdmBw5q z$!Meo+d5MW>gghKx3bt)`*(}G3vAlp+iG;tNf^5Bj=qZIhaciUjXpkJp41*XkFkiZajZC35jhc@n8Ix$ta5<)QGwM!ElQpwagHS*6o@4(iQxVDP#yAP9dORY1!0=FN`T3B$`+n& z3YTY;{K|x+qj2NSa^l`aPTaf5N&k8$2r?-n1SF>fWSF$qkT{&6sAs;)>#XF&U1aZT$A9}CnxKx5pJPW4Z9>IzRR%_-{she`yV@A z8G4cH)Kv&~YE@9c!(3N_A4-xm;^Ddc?y|Q~f&hXG2rq)DT^AuNz-anK>)2uE@lxl$ zCB^mszK)$uLUNa5C%((E6Zb!M>e#DD*TIa-Dl+s6vh_gk!I>ap5La;^*gmi;h>L;5 z`HIsBR7-F#3ozFGENUM+pr{AIuS`hpa_nr9le-){@m-Fcc#AUI+5@N_ACr^kv6q)s z1swxL7XjseNRJN^F*+@dgAB5PtS#bj#KYzR4^5@w@ zoDjQj2#I!s!rQyH&b>dLkiSsv9n3~{@A<;WJ~p$~BOSj-JKeDG`s`r->|pWPwzoh8 z^u3YL-$LYh7pVBno$X56hwnyM>NdI_JQSSO_!L3S;jFOdo!DW@B2|*CUDwKyU!5fn-0tCt&q@1UMi9 z0u4VYHn@H7R4g~k{d;+I@aE|s%%x{Xk_mURyJiONdQ#k$q~~qOep~l_UAb@lb69VY zVs}d^cDIz`{@O{JhHz~o4+X4Y&?pcD^K;?=aRx7xXtltvC#jF^?9@4+AU;eA$Ph5I z)S4t!(lT`j(R!6L`4iw%Um0{%u59?(Yv)UZAJq#{+4Doz~5O zouX*jmz}sOsGuUwM%=fi#tP~Wc3J$xwMzy;#h#q4(A}|1vpxIwE5a0<`VS)fSK5d- zNwK@76uVnWasQ1NjF~1N=TR^s#8HO8xLvC2RA)qKBup@Pyg$bEr0Oh6p0AeiAc>IrF`#C(ic!043(=bfL zLAA6IJ7I*8_#nl9a~ttyha$P?=acb-Qi-X2;>dUt+rO?rkGRgB=XJRfePKS8a=o># z#8KOoXfQ>p;`(ycvsdlO6P9?h?IdivIM91c2?qK4P2$dXd#PU8SHezHo?v{DnjKzg zkYwWsvc>l~&>($=nc^e)HM zRfoUy$OzGT_RN{smUu2Dr)j+#RTc@gMW_4V2R%OEWwQt0o7TJhrnfxU=_r+3_nuz< z$Nx7T{C)yH$utvx@UnX} zhI6)D%S=>@ES#`?>baXvJ$K`&H`^N*PCXn^L0$l220$6<+{9u%iU!;q?<-`VLHZL) znP(@QR@?&h^_HSxTb(TMU2~wW4pgp_!*7qCy?Y^bwLD3U^TC^AMdy0@{MGT%+c(b- z4OSOz9KCt{=J{HXuJ(L>0Sa@GNUsy6uD3+Gy(QA!qBO*jS#{hPoPQLX0{U;joKCFu7?dA#Zg=aE=d1Tjp|0ie;&6aGKQ5SxmStAGug`8e~`%gk;tu!OjhO0 z=`E4{3rQT<52FB^6>}iVLAwU&fh`iJH-t@5L_EL1b|`AD<-kB>0F?sUY`}Z!9PV^v=KTRTg)C^=7lj}-eH@?aip$A(R)`1g9s>UX80&jhlz;OIFTg4uH zQ%O;~lsrrL-qr$R5NL|O^nl{@iP!-O?zSr!eb<*60pDF}mq%!eQ;MS3AsMtjWZz#0 zUtszKVf^UAl!=eElA5lF@_$=zs2%uNQ6M^!!gMViV2BsG$g|_>Q#NY4I)6-b^6Lxf z`p(V?Z>lmW$(63CnIcuMn#u4HHT;nQ2K@IyncZkDe)RzpK&b1f3qBj&n2PY(w_NIg zny-N&d~#{4aLemz$wEPof7AZ7diWWmqF54KgOw=KcSeQ{b?33jd3mU1#<4eZK;E}K2EiZ88U-p}JrSkpi%z`Ayw-#rU z$+QyF#}srwqlnm+cFzGCPR5hTkIm1fW}55{1)5~m#K>uzLMLRQkRKu)I}=;bmY9DU zmV6@D|LvMWe@f)9t_(;6DGK}=n$m8q0>^OtuLCXDmTVF(KGn$|RG-B@96VO|1OD-- z869fzN7pPb`Idu^YzX@HIZA|n57`LVayvV(TbAP!DU!!;q1FA?6fu8T^K*Z2B=V#+ z(o)a7RX@4 z!sv_KxGWWW5-(zrIhsw_)(X}r0R1tF=g0NB!{rxLed=#uRPy32q|J-BkTx&wUu+6N zQi#vlkMr2~YeIrX}(iZkFt*XjW2x*Hd^hgKJlROfJJ&#aFN--WGzA`AQ7!cba z_Y$WJDiSb|J}?|&3h<(zMcP{Ul@HR^$BYsD_aADCwJ$FA1^h)^_2bEKYAc^RFSkq@ z&_mrl;nG-d(s&xH3f7x6AkHXU`AgI8vSssHJnNa&GIxy*{joBd*2Njx>Fyl1MjEl5 zTMl%b3`KdCNQD99nrQv9L`~G8|4D#!MEkv(To?Yy- zX7FV1tHJ)@8U2?b0D4PdlAaDvQNOvHKSb58E{0Q4%5|*h!HpR#wM|S(xP(Z|h%Yt& zui9LL?F;DaYSE!nY{ zNp=+Ya4ljl_r>~PGNwjItldK#v$K+{*&b`p`cO_`?mcbX}| z`kTzU{h_+aCvYDO9(_l*U77gMbn1l)D>wX_W9A9 zl_1!~#czS*2PLwaYy=bVq<-p_NLJV{l+{^?)v3I6o-2f^!VN?>ozHB6)hh`{Zc=+d ztOTeAj`(Il+^%y_wMoe*Ih1!qOpFXTmp0>#yHS(z zHfl1nlX6Luf$2Cv$;wdWL*RO2(xym5f&V6k^``ON-r`FSIwvW(!z2ZFn53Y8HoBWp zDFF-^HIkUrxY+$F4`BSE0K0-DS_`(p0)tyN_gr#Yyoe-7Ky?7EfxIaG@{{DXE41%t zlDdP!{v9UCzr!T;ua1rZTnK`KsBl?(W#Kt8_yy(CWd7mxh#lsPdIEmTD<7~nA`p=M z@=#t?zU!r}Ndl=MaqfGPR&0VTC+XJYpJ@TsCTE0>V+LmK%`5|G^-@b|CW9;4bK}i% z0JlqNJ~-4CTk3M-$P~~~PW6)i3Ox!Q*NkkJOIxdV_(spu*U!GT4%D{qe{J{o19n6= zSQ7!Ty;&$|+_MQtD;;Pm92>>hV2O@^=$u*|d{7QK9}xqzM2w+X6Ai>?4MZrKt9(S0 zdz5MFkZO}wAz#S_(u5>IBc0I>iA&zLC2*l*0lb}DmrY)fMj;jwh?e~Rf=FtRE6s;- z{Rux@sKPYwu)$Maa0~zC&;R$-0(oj1M>xQ_bYAE|FJJyU+FV^-&dWvh&97N78f^I}2&$KtO%INAkk%>9j8dw>qsowK(m^Bbs7Z>K?XHCR{ zx2l4EQyM*}^e< zi_vBtU3{R2q1Jw=59`{d$1kn$ucKTuH+M3wefsoi-?FZ$390{tf310JeezPwNiK2bUs#Er;{ z(+F!YrxdXEixmDfywfD^P{Zm06ctO7f7Zf&4FJCoe9pEDJN>EPukLa>zfXcrlt|x` zs*Zv>NvWd&+m3w8tnkV;E^G_L+2qHp*W-R}-rT}seD~zby+ySA?M2JqUbOrzM5|0f zH^BUfg3u!rhanTCL`OuX3OC|PO$CNW5Q0oF7=q#)$3k4@h!+`ISt~e_CQyQT&aYXt z{yK8yZ7)~e_Hxxfp&-H@7ECG1nN!ct!XP807Nl;R##&sLBornkB*j8GMQ}G|M!}!= z;X(&q*q0o^r7eqBdub5;YUQe) zvTw@QGAARepejL3uPFkcRzBgdEz`nvOYlx2$~)WK-w_BBiSuiguJy=N_b6Y^2KjQf zSGb(*6)q>(B1alh3^4Afq6FeBsdAD6%7koaa-E^R1f)0i$mmZqFBY&IQmHAcbZSWH zqM)SUeMR94D7cL8SH8%?4IdP)TSaSoh0EDq;d1&bT)@!~y~)DLD@d$Il>E|hsuG;v zBIO2V4D>v3TY~~D5Ws8PGND#Cj>ymhTQBxk6fS`D{OH##T7Mn6+Fs#uwpX~EEdXS) zGIBz|838HqsMjuC6!+B2IP}0lf^o*HVy~{rc%qboDsc8R=xF>fBIcCDX|bYkIixvx zzgoHa@(R~R>Dpf5a<*5voGpM|NYEq~EtT4!)FBhia}X$KpedXzE6`zSFDYoTB9GXw zD|n*I8Z{pHX|M;%dPU*Ff00JNX6gFE3Rk#6p%1tBaE04@xWa8bTvdSU3iP%#_Smej z8hs+>ZUlokKtgrqgzz4?==Fu~0U{Jrnp>6;{0LDM6;2RDD+-rS3UPYx^7Y{1x?QyV z?M2JqUbMFGa5<@;kyxHbn$nB&6C#BY<^+#;e_EAwnUyKIG$qHNM@4wX;a3Xu4-tQy zVyhmm5L+?%HH+3?N3Oi><;vS$uC`FPz?ICYesMy9i=<{dNTMvtVd`+IA}Gkxz!&Nl zq-&Qkkl2zkQIRL&gz%QvaUHi6E(g1EmDlB0BUfKq;kre--0h{y-Cnx-D_r36Q(@(1 zMJW_(lw?70Ps&)3)@4C$4B!C>8Q~w#2R^{TOHt)+hVMSl0Xr>(YoMiYVLJxNuUWe8 zQ{lS3y82Qu$N7TEysmoo(`rSvyNC|EHj~Ql$fTNdCQ%J9$#-cIl|~uKMv~lN`O{0y zTF_(+n3ONbRxsJznnw<__SMb)x{B)AC+r8&N+#7)O(`HGKfILmr2n72x9gEB%dW)U z`71&iXo{2~Bk%W(pseXmW+us0F-a;jSyah3i2Iot#mI>4_#i6_eh@tH_`&iRW61;C zfH7k~7zXCW@W_Ax1AdtK6V{v1f57t-yw=|5e#DKqkr8B8tGbI)cax0!b?!O)p0m&1 zYp(?qfx#<$J(&aK0qyJs@D#b?J|JH#&<;HI0~$ChkxJSQRc=d#j@=M6%?S`kVe{~M zaZR)k$`xSZ!yiNlZUh*^TyavYT^<|gPRT?MF0D^1X?y2GcK?PJ~mA6L6v(A}gjb{O8HSkMOY!f+E^ z$I?^>Ik!!09V_Qm@p`@OeGzLpgP=%FOETq4a-+KY1jGUJQQ_VP{*(n$y7yXTkIh63)Q07_N_I` z*D?>>@t^Zw2QR=I_v-B6+3^lO$R^^yFxX)Bw0?c4*3Xr1B!ps|By!r$LppFrIPKo6@5jL6w4-RC6 zITU_ob2OnWr6s*kzWiPZZw z)fDCD&NKYFDut-Wgx?%1Tskz7EoEc4Bx!*-5ySC^$%tiBjF@2+hPt6}y_FF)As87g zlJ~5B!tEpk!)4p>fajgsvrK)B2@{$iVpl62>4FG(!&G8&+4#GOQg=vWl*?jBrV|kd z)F>Z3)|Zoy>JgT#=!VO#*LkWmd4_ za`M%0zMYfdiHU}=NQsTf4*F|cJ&3kFF+gta3;QITsSegTI8DV~;C zz;Yk`C=|=~u9V;F#?IbhoOejy*@*EZ0aCh9VX>hoc zh`gl(CyvuJiJ-9zKRa@p`YS-=d8;~D8^_tbOMb=KvxDD~akh3P(q1=q)(+#mJ(;#0 zakoAIDjbTD5!e}Az;&UY4yV~gH(P=0f%84}QIC*7Wyb*}%V1zZ72Abbxc-kl&P`wC zCp6BRy|dW@rO7kk3@tuU`~-E+)*NUA`37`4ZEr)6FKCgvJ}qy!?4U`s4mI=Fc=22O z_gahkg}SY{VmVj3rj@;LC1HAj^7t8ZkHSB7=RkJ*&Xo-d1O#by zvC_3IgjSn%?(p~K2#16r)@KLeQBtUV?@X1%-a`Fn<*i|$4qGnzzzT@@geH?wXvsU z#g1ZOtHyn+Dsmk<^^l65{{*@&qx#)FPlm!>p%e_q=|YXX`xg1?c2)4EW_Tm3ysnLR zI-QkG)~?yY-CT3sj*jNKJ8W_>&v`y**y7t}4w?V+%i7zwiR9EfNDs9|`$DcR8-333 zZkID`U835;cWWkycWvRjwdd|o$^?y({s56XxRiYqoPbXh9;Tlqp^L(}0F2Klbjne- zlmev#d0ERzeIMn(Hj4n#229OIO2}zupF|_4b%nC-4iPk^kt1X^GZcKrgwIj@4Ye|P z=A&#BNT+z65Zw8<=NHGPP+vGcyMU70%cECsxA!pGvG~Cb!x!u@e8CQNv>wXN0J+bj zZ~!W*euSc+2$FuNH_IZ|1!)*rk`rW=F{XLnwg=!y21|Dm0uT^4a9}=_{06%WA8AoH z44>7&F6}UU8wSL09T0zq0r7VjknKZms|X z99;#ydG)OGI?$Wd!ns@AaM??HI?P7Q8eU!&i9Yjacn3Gb8~!0QoN^k_fZ)WDc+#x5 zCg>(l(5RaMvDLV6mGNh;?o4%0X5StQ)zdVsJ$pC?Ul-za-xn=VcL#C0MIzo4IBH8) zDzp=*H^XN&Gs#8q3qhcTl@2EtL|umRwf-h@KIAJ91|;_w2!OuBo#3o7Ya26Z`yAeo z_(d~GJebeP$O|&q`D9jzJeGZ^=NM|M zqUFN1WXNfiH|Vi2X|ac`&h=iLzG{!Dc}>0S!(s#=YLe_Hw~(~R2)k2*e9#`p z+z4WP8GRUDs^7UWnOmLL=6P;zU#kzg@tO@j=!Was#TKi{r%l+SB3*|VI7m*yB#7?f zlMTJ?fQD=CyWTV01V>sofxZhjA(L9J*r5Om#W0bB2nLF*1|WC=A_?d+@=OMP3TzUR zh7Kx9A(aRjkkCdt*oF#rgzBr@2jA(^vK9`kF!0@-IP$jvrY&cZP>z@*bYbNK;HRKR z;vqqe5;EvSK-0_(`t889!&e7q=c|gr%QC_SnO-KJ+Isn?q{UFdx%S*ahVcsmyDdr@ z+B+Mdy|V#sA2G$2!$meQcY^`afFLzR#yLb8vm2qB0YNJGSfS;Bx7(rMoE3)wFlo@N zcAeZGSoC=62FMN1jv@`)7@+gT0JkwfZx8=VLKhTrBGBCi$n5WGfL?!B1N8da@3c~f zQThl3+la!W=oIoFHr!<|3^N!c$crFXgPK!FaDs^l#Jkv0XI7NiVeEtB$Y~fLOwjS~ z*8sgHO!JEY-n~;HS0na&45w*>C+Q@=THvqn-m5Lk#?w|10Hiq&+V(Ex;g-G2B&GlQ zyu5c44WQ(zqRDot&LxzcuZbc{;Qk+sAsIfOP7q0y_k`mNu-V?hl+tnF1qNrRVQbax zKy!0bI-+aZ(uIm0yzUfnO696O=%ILL`onEJdq1pstEwKNJB1AU2BWNAV>HHg1$3Un z0$NZ$^ea5*tLjc&al#jLU4?cO(xf(}5y>(;p=<*F6OC?XLvi^R;2+*9E8kSgtx+KUGytDb7P)ZPzc6cL3 zzxkdBP_l+7V-Lf_=XKTl&~EmO;nZFXH-L?Df7rwSb>33j1$$EjI;Wj0B62Zo*e`H5 zxR@dwPUMEfxseMPk=OSUr8Xu$z5Kx8_4Q(I^yb+q+81NlHq{#tXGui$j*po>5VceZ zgf*sFTA)s=nybPo> zb233+4X@=SY&{{OV}2;{*aqH>|I9ve1F3xjQ=S!fgYt{^>P!bKrog0<4l}>;L8L@M zpnWx-uxd)Fo6+leH0^e7vB_qhWHBzTxKXPaa_c)%9ZLwS$fkNi1sjZjye{@rB{Et} zy=;?&PbTt}u;F<71g=-NbCuP<*r=GGI^_h_rG^V$SwF5BEClC?E{+!d4MhSJ;gAU= zGfi%UJ$Eh>Q0F1ao?Pyi|J}b?v54{+HDkc75bqG|{CoMD^3GQv-W^S5lbfsCue$ix z>=wG9=r#TeZ}pXQ;8*y_=O(^`=mWwG*fg7&3m?kxM!CBUdVLM{=LDN$aR zQ*#A-m9rl05>KwZSGRntMhjHy0(DO=q3;}lad-hk=_F_Nd$FCO6olk$YA z%4W#|Jyuj5zHzqbqfFTmryOv#v*rO%JQ9lIJlHlX5ia$UH1^WL3@^hG&)s?sx7+g~j>#Z>dIs(e!zoRs)DOiP;4>>^pphI948^v-dnng~EW zaA4(#$mOUsnMm=?C2x>&xkA$}Y2@rB@zl`y7_Eo=0s*$wmAYF>UzEPc?IT@aM0kxJ zkBSh&Uoa?MO?S+G0TFQeksmYV`G?kUj=5i9`6 zjEt(jl{c*n;!2b;vhgqQHJJvix>&kPllA*Lf}`{h@_ARqtPP0-fKpV4VahVUnMC+a zd+-fjKk9a>Z$oZ?gQa6HB9Fpy0P}!23woJ~DA)%tS&P08h8DG42`kJpfLG8)V_Y&W zeAnAwFwv1s)psHqW+(zG;&Src%>~ABH?FdoH2D?jx*$s>%k&=V63y?o>NR|V;$xt! zjs2ftp^8LwMieLXUKzdwb(#2`JnC^aG zAEg=_?$EIj$+nyC4^5KQ)6wThaPJf~6LS+2MNj=Le8 zyD_}>Zc|?2jw`n>V+0Qo$dMexNG+7Mqd+PFV{;rplP>~W5#=+o&`SCsan3+~3YsMc zdgLMX6;ZN3SZ3P*jNb|E6}Am;Q+U_A1F5?lQxv4`zy)X+HCrt53I>rMyFm7!VPF9X zQM%WH)ZN%)KQ?fe>U>xM3z@P?h`ILTs*+r|p(=kP|Iy81dC%oA>V=uTu@{7UCF{H_ z%;jXu#6DmQSvJRAn)uyDgL@FWSf?CUK3%+wDqa#=^02PPAgSvsGk<|?;ATu#y#&Ax zh7;zKn_;4LP}%npZ;AT#Jt$1UH&|dspU>(J1_aRrxxg-0_xJ;YuwDKER|vsl{QYvY zKuBY+6kHl{-k)Xq!$hRbW_ZLo0&@KGIp+8Im3Z7nMGI?$`f@Bjmo)mMD6-USD4uRGBYoDFr6!HfGl=c;ui7* zDEESJfPZ_-?G()e^JM9Yv2Cp5m!`-cz*k?&mYV-MM1Kg{Xr^nX^ymWnZ?;gpGvN@E z=ch#RK&4V;@o<7v@X1|9!|RxLxxBrWJ0M;)8S!tYaIqv8z!8#VWQNaBntKjMfL%@S z^;>EvE2dg^9oPzF*HW%$c0wcUBF6J2%YkP=!yK#sMFOZwZcu!#H)DE9qo#vkzcN%T)vRC24CZ|F5zO?s5?yPdbg_kcoy zfVz%(Nmehgd)^)16X*VI$sW2F87ILk zmA@NCyE|Q~q8V$wDt&ybdTy;J-pMM}E}Gjsb$e(AN`uJk;DXXI2J2)Wqz^15w_Gp? zBgxByVv1}Xv9^J3(m6HtIBc|UBZVSso%Andi_JP{e&?mYQBQ!zUa zb&?dE6ks8T(-foNGDs*q)c@Svc$pM$x(fGS04L_r2BL7Bez2{1;M8r+1E+4UXo0#_ z7er)`bHZPscAff>2UJAnpg7KfNL>U)3l}63eh!Xh$A)8#+XLL=g$XKHo95vTfLyok z-8?M&ch)@oL96t(;{?vu<`Fnsn@6yH$uqW5jtk;23#{5nhPfliAj%X45M_!mPYhfT z%Xn5lMKOWz#un5l{LphS3NdODp;6H^4@m5|-r#=C!*6Oe{nnbt-GG>z(PEY?K-ATX zuAR$l(nAVnRsmtw9$p3SFQGAa0q@^%cZ02sdN5FCWvmiE&zd_pL0(sf;g*fxOL*D{ z1LqNQgtDo_P1mv2u6-2{K{q#Y;y+V?R{Xta<*Cean&u8AN3ga92$&gP&xjNA`bTw2 zvb~DWi>`U|-RgThxaQOOp8U*R2LpXmCfP=jc$LWnN!j68f%O^#EUF7Cggq|&8Z^3f z9dE?g0a|C~rEE1(K3dv<^NVUq<8jL^fxpqjK3O`9OVc?&`g%rfUwH);PA3T(`j7j< zqBx>RvP+6S&&gy*PnPZ~Rf$vV;lv%6drJm)GBubEx$(&uyJ8$N9s1(MVXxk6B`Hcq z?~JZ~?Xub+tU0lE$K8XFclyTdIQlz7k)_SZij6N%Y(vH)y2rI-qeJZ9 z8}*ra+D?^TtsnNgO%zKlS8UJK3->k#ARgj3v6I@qlljm=jUafF##TbuGV1i;*t$5u zLakK*p57ew^`P>^dS);CI4%~k{wi;4|9;`|0q6(fB#VL!XizL!0O|*jssnJ<_RxZ4 z6VX!p?DF@@#*L(uPzo>{qZaisql>>X#<`Ol=fLB*J zx$VFI*W>8I+;@3pdz(c?#d{eXM5c&kRFdJnz7Yf{@s1>~q@+EjMEMiTU%yPP%vQse z8{cmoX?vroorkAD=3GXzTM49S_N*k#WUH?cTfxrXNEpR2fyJ+s?wF9C7GawkS z?t?{A=sM;43k0!NGtpWbLyDXuFo0S?;=(N)pY!MD3R5yY=xjMakmb~m>G2bfNVC9Po1eyAxP^$ zz;so%KrVJ13AD zeEx!Msu9N`W;nYlc+jvgYyBXwUwiO=$g$ zVxmnWyvS%RQH6U2`OjzuSMI$@EDy(6KEKxo5^;eOyF@i83`8zT8EOPU)?ChdOT2_= zG%P!K#fyt4IO<53&Gc>Ge50JS#Z!<_9u{fcDPr3DGlX`vE58{QusN;lT#SKi$}q9O z>w&T0Yns(Mz^F?!U%K!c@p%<4qWu1%7^8(|UG!ToKTi%paebMMmT^e{-OHHc*5cG! z5$40wlZ&Fta)sCy$JV=SX5d=af7t?Gp-EZNtt-rL@aeJjRd*`K@ z+1vd$=T84^jby@-pH_=uZsEZ>tsb1$9+=v=Otnib+9j0*{=07pJ3~*YtWX0 zQuYSc=2mryJMGv1RKwqXCj(_pzn>t6h5EJtgma+v4O3L1r+M(qR**Ef{Pv?(*57 z5~@DG9T|xVL-Ti?nraTjZnIUQoT3o5Kz=lzhbMIvE($7VVf?F!ov`GRWqI6(OYtKH zgCK0Sgstinou&`h-44N!K)my>yl+%-3QFvLZL=F-38|ex9T#E_ES^KyKaqkLg|vwF zh6c;^VuKfA*(mTs(mi8^(Cc&wh))DMY1U&oRAV5GZr) zg@0JDT2V-=4z4Xq_M`&vl0WJ4%wsZ#@e7L#0GD42)xv+I{Af; z3r;y=wEsdxP);!)m=0uBEE~X}PlpOVa!>|mDYB!)L{bS*Yj0_vY_nj7SkFo4fjz0K#mTzRW$rKi9d63HiVGGHJPTwryNtaFAwewhhgTA-o8 zk<xVjG#$1L^R^$P=5b9Y2wNc2m=wy+1q;Gf5AQ}CdajV|8-|bMkw1Qk=%&%i7`0EQ;P&TJ4yNgiMy7e{d#k--x>ym1gE zo=aFxYAth32O$&%2dhdv-e)qD*vlSl5(fdKiJ`9!i4tha0wD(}(Yd|@{az8GBlUfpZm ztB0RraN(yI-1c0>126Uy%SlKYg)KR#R4IpR!w%g+0^Lh+96>uH@dwzX#}*)_b_kv? zkk*H`3w?~B$)*nqronEKxc*(T2}>KbpS!``u}8i_Dbnz|YO7}V%=UQw!N$(mp}1R3 zWWKR8*2C_`o$*_9-{qb0k*0Z@drW%CCIx9qDr?!v=I$3;utJd>8#Unu(u=F@m(kSL z>er=bl`yi}1B@~goqd*b^U~D#&Xq9@;0JX;8ugbZV}VFjH50XsgOpX2R4KFGXRzXLVj=z1D$Wbo)4sv7?G@v{SB(D_ zzWAmaY$Y?f;_~v2ZOrpl@9x@zEx6~+ZFZaE*MEHN(|gORI84Co2nA$t20D%vfz#ZA9w8EM@G1rk5HNt|uJ7y?eRVd@?Bz^S zSE@_>09a)x>17FF$+qi8pmhxsH;X{--M^d1oMY3LByx8g8+XUCaktm7aWM#9m<14` z3h{>EIt0WGdgB&3Ls3-?Us!TeN2+aQo)$h3dhWiM^6Y8O~QZ z%*_o8y_9u#=o%ucxkNn6dzEWb4%Q0jlw^ieoRK}m55M|A%Ap|w4C5tX7EzjxNbtm^ z^hAm~(~@z>PzFVEF9e8`g`z--VSSq|zu2X=V|F`E%;VW`kOw8pGS4UqHQLA;7Q-UZ zQN?Jb1gsIh;pK;8a-%6DrgHU;C1_k)m05Wh-W;cJ@jXYfMg`9uLljcZafW zqFcb(de_vk{b*>^EAxZCI$2JH%W?uOml+L`K|?QYn+wOz73eK_RpJO>Jf zITXy0GVMe43{(n{+fPIM5jlMqg*5O05ORWd6jEj;%1CB_T0zHNPOgICx$i+s<9>H; zI`!6Td;-UT;G$=RNChMB;{XMexQUZS38c}|;4Xml+D`i^kulSPtWVLxY%vFCcMICS zOw3DIJ?c0P65D{2k2i zu|dB)2%7xM3`u>elpfeQC^i1uZ9%(PkN;^3n=Phb?`}QYv%BtM;o*^2E^Wg2qUf!@Cs}BhS_kOL%`dzl3yWMME zG2=R`>)ty=8s1ml^}3_0C<$i+>-I*P7~b<#ZrNaoq00a#Zjxwep=9TOZ(S|nx(4=8 ziMmoyajY3{9G9G__|cT`nax$AN8HA$MLl}3&p?}UrOkkfw$h=1nSx%d25k_c0h#G= zLbebP#0@VO(^>=x$=^9Mv$LU5&Y|;n4z^>#@#C=z@|li|$Et_`l3E*{6Qu=R3ecL75 zX9+Us;dxNoMSYzchnbT&S?&k$0{np_L|te&K(Zl1Szh8;9*XslBE`aB1qriR4PVo7 zpwxT+5Z2{blIwb5j?`>FALPIjLRcqPK>XLnn1 zcDI%7y~;dDIp|)Wb^D%;!c46E$smD>U+Tb2!oc>TB#mv`2?6&HoI&OzspO`hpoqOB zS+*4ms+faSU8A3ktrX6{eOZgWyS3Q6Tg&!dq&u*J6u@90Co&M3K=&o_G8Bm9X`Whk zocow+Kz%cCp|FC`wH>$sTVTh6oF7DB8rXQpgA9w~^?#n$vh!vGhSC-)58U0CAb0mA z$lZPkN~|mhbJSC#*F)%_xmMo|L*LIZ=(bBuAS^x}HYBk>2p!LLEnv;F2!G1}nQ$A^ ziUUC_=pNj!p`dix`5j(@+HGa`CCJ@<337J`Yc*2A%F-6DVNA|eZU{mWMDUzAn5+~S3_ez8(aCbC8*t6c3*c@4f{2yDvfh4ogsqK$efF5#*&owKu@RlVXp9SQ0q;LKO4a zaAa^Cge0y7loS%`DSR46N-^2waoSjdK&<5r{QEW(>vwnw+F&cr?zZCWZY$d_L5a;B zF-o36at-!IghIjdj)f*hJsBeHV2Xi|A;_jd&FI09EYHQB*d0WmjX_!`SdOh@MuD7o zKm6I+$|o&B8?43N-CFG3tz`!#)&VH{@_az}BoMzog5+RFCsAJ##|X1PmDMNUGK7np zxN&9!I|6l9E5;P!Ah_O$ufPKy*gw;m<#U7PYz=CA@}3|ds&M)8@2k8;{sz!wP|&Tf z50vw^%`iD>JVtx@5LzWwjh})t6zUjVmOONdDG|(JCVK4akSTnKQU{INZe5ls8bBTb zdZtiiON%>a6F@jfRYF2}r@tEUZg0xG=*P4viu<`*FMyW zD2tLXd9M)<0Sq$-`7_8-SZI5RE1o?)?FhR)Ndd2*;uKH5=igE4le}jQq#OWB1XN0( z)e#Ys|M>S{$H)I^=huJvzdph(4q25Z=NI?CLIK@~MRbK;iEUMEMY!VWWHOf_0#Q!q z*Z=HqNQKz>4%F)){06tR*7*quJ1ku}4F9lmejO?Q4{$3580h1ayh#OhIpwvctg;lu z4r?IywFGh>Tt1-BF=~AK{EVa(tfIS~P?k_G;uEI|TTvD$va;j#Rc}ZR3V&k7zt5L= z@ZgmiP%$8;_z?e;G5J$aAPL+l>--~HRgqtbC#jOzlvo_4+z~dUF^l|0B++LN|Jh>5 z&K1%nQ!01$F*BbPfmBo($f0WoTAp)) zqYOh8*_9r?oa`N9sMAN%^wS}g#8iYpKPi53Ma`e0v9y^2|GHr-KY*6}{NO3BKGAy; z?>i+g53k$r^yc3eMVPVHbxN_|RKP~24)_{9!4a3z$31;AhXC)X-%)AeIzvE0U{oq5 zDr)J$gCnG~E)@!EJ}vXX{HqKKpa3Uqx?O)*p8 zsVq;0awZF{htR?8ygJ{zCg;7#h9=A9&V?1o$agD?2)5@C0MiO`+a26iBS&h)a^^bRL)aHCgG z-d;%_yKdwTLX>JIPHZ9C*9VXqHFQWZWk^xlfF}WdVtW~AuK`0D_CGJ-Co^o1)s~7-@wAz9)r_vo}ra#K8Ui=4w9%JCwROYIBWdpH{cg}a`Ni*_~Pv3 zu*-jeUF6yE+3PdRDBW*Po}7_RWVI`QN(ea^lidz-vfN#a$=$`6wvY38ffoi*-}aHs zb`TjHSOY6{@*s9G&u1W`h5Q2IfY_@C7&74JY+&^vYL$9f4nmtoP6{yo!HV?4=Npsr z#h8pSwa-k>F2>~SVoW=5@k2>0$-rmlx(P~hYz`6P$`J}Gk?4$*1o>}ZE3!0lqkd@R z4(RYu?u*?&(w=ti{P@O%;BxRYG^Wj=0Lbk0E&=z0WbahkT#yW{Ij|fIutS!t+M9v_ zu5cW$4+gAK3JG0?7M*s;7f*Lyaa|Xaqe)L4>yH`#w^#Z3qS_1@I@vv#n!^<~q5tr& z{`!CF92#;GEjSUW$O;DsZdPcYv)0nEj~Fl;-+XaXP|`g35C7_~{ts#=zwZ>f3Z$UP zbBPS>3HvJ*I4Mt+QFL~SEB?h_b*LJIKBrWik;}d{^&S#(&@I{|pOZg|^^`o0BXd5s zv$v=GDRL!Se*>Xd^wEdmHFuySh|yzE*}dtOB^wlVI(o_z3-+VV4-ir}d%p*0*uVU* zJLkGBe_r2~OOQq0S0&hMZZ(?nE7@@(?!jkZU#GfrItu7QzVBalOleWKV&bU^iXi#6 zP`o1i^XC_*FXR}S$|n8w|0n}brMAbNbJ>uxe6+e|BI0&ZO{#lE-Psve9XI%E-4pXJ zk2_B(@muWiM#3y4F&e(6*ujHe|M&mm|NFoG8(7%h(d2PQ!$4wJl~rg@oh})|<64xc zv_8JaUktA$Qo*MDS6b<_2QDkzTdRIJvNGJJB#6ofroO2xVd^UJ&hSQb+`1|qt(~W* ztC&QoBA4oRhLH`e>%qhS+yA-jNJhg%s|~T0VpEQC-`Y5^TFIz_i4QJ{U~FkgguK+- zy{e1lT7I6QSH>5U(>Rp>{C`ukx-F@mGw$)ax-p&$by7*Y!xsNP%A|bGzF6X}k}73j z8zEV$2KHBm6}Te@D&6DS)X6XPpLbpqO0iWO2qsMOB>nnIC0Wr{Gy>Te|;st3Mq2LA0Md=aJG_KF>S?Li8ONhf?%|sY@bGADD&DJ zYvvR4ZUr?IP`bqpSnV|v@#C8bMED8?W$dX%v;ITL0Va1<3+f3Ul}clhe``FvS&Rzq zwPI>2C%xZeFaOOyEgq;cii#zc_|pxQE=@K1t%_??1X*kM*jhB?Mwxs@kOYyS6+RrX zYLysdD3H@NJ`T(pF@;TJ=@U7W4C;>ai;0rQ^WrX4Bq#XnAf@+IJ{0|Nw=w4Gk4Gnr zmi3BFZ#Al~K8_`jRt@Q=JYbC8(byAg=}+Pp7lGIg!CQIK#v|mn>Jblg5xWr0Z28EpNqk@mEen~T zqWZKm%q7Mt-=P}vjq;4Wu5C6X)e-#&7nNnEvinJR+iFK zI!~}r(AU95$E3;=Ui24ORvy$S+Kt9gA=6F0$~s+p>%i2aLWN7YuP&vDDeZ$XZa)#O zy=r96B%lZ{HH&731diuc3Vbap=VDY6I{?Y~0H~yKjQE#hZA$Z>9=rsN(kidg zdJ?9y<3D?Ie17rnWXoylaXN6~_+($iio>08|A{5gUNhi@*r zYliPf5Qh{?Y>i2^YD2EKEoUc7+&(G=v{3-@tO3CGKpfeF0b-tD_W-g4BryXNYlbms zJ`e$d?j&?W5r_hY)(0_t?tvq0kTxw55|Msz-k0FnM1dRo&1FbfhbjQ83fOkbAnEE;1TkVBwT#M=O-O@BkI}M> z!Avq!kGn=F455t*&{S6hs|e2P%;yahlR+sK`vymg3D=ieJ$kbCCo!YE2>Ni?8fZc`;H_;#|Ia0&W_I?JgALcC5fiu zUjX+ou#8A3PbuHJQhL=ElW?e|ZMC-m+GIW=Ht6*&9M~~2CLf~n1QH8#{X6vF@Si{W zqprkk@z?Wn95{#8L+9HA{!kFeKf(i_ED)C78;+MkP1jBI*{}cdZwkG*f?%DS3XKPw zbMOv)gQs-4W3L2_KIzj)a)+$vBhUVp%sKde~Q^+t96 zb+M2NVkEWtmqx%Xy~URf)RLh3OyE^$xm2x>Az0r37$_%-(`ERx;CWQVUWsvFriASw z?oTu2m@y&`W0t5o+4a$1j57*)k?D|9N133Vn)L5@30+hdbbEmO**6D=-%|ItoI^^~ zqfu-VEYf&(&A_5Tg=svUXd%%9Hm&4U%F%^Bsm;{Vc-WV~h&bWQicgq4VT|Dvn4&^g zpiG{K>_DO0ASoV&C)M8+-)*%-;L}y*x$@yf7xn<85<*ah^YIWyWeuds%zR(XD$6Qp zKHr?3n7}l^OcQxQd{_)!SI-bVWRad+rR3V0K=YoNGZ(W63+ZNtFT&ev_K9?gEy(PqG%7~LqV<*?B+@}9MB z>2i^nj>Ro9!B{R{f?tP+lY2GWmnN-C{t`bvFT`9$i~pH!bCx3-?C>x#L^GP^jdMqz1Zm zXBpq`s56NPUDmHgFvFz;(C{TWX@oqx3hT4ev~EMmNijALUa<2PHwCYCz1?^Uzl5%F z)hO%4-%x^qX&@gKjh*A$)lSq^JcrLU8X8co1+6%Q4H z(O7ko=ab7!Q6hkvPq+_(DPzq-Lpjt$^^4UoVjIG*Q>Sp&;-qD~HnfO_d39lHlHO zioCrhaA^)}jh$q$rM+iqS6sGRMMHdmw6LoBupC3i)%ar7tRqkn^wa~*keuG)1HLip zT^LNHh#RkEo(YfOl8+Owd5_b!7@UkG=u~A!q4Q}fQ82mS^{?ThE@s?Ml#@)iu%~Nm z`j9=WZIQ%>E3}+u^4*A7(b&v5rX@;qi+@L*OY9iM#XODP3z05gc2?+9nZ35U`05=i zE=GKOgTBe;(jQ}LQn1g+?KTeBZ5NcVNQNm`uTA#D`YR#}_z(6wGp>ZZ zOoveVGLt^X^eZldvfP*%_9J&~*q#nA|&}bUl*oREI`FV!*MIh zd9E`yY6o)Ob9{KPF|(!~Lwrw6q-8^J*V)@{5OT5(XZwXGv&GI}>53h<1k2ZM+6z3z z%WyKq=}n@x(qqBIzlPy=RW|#s4MX3#U)k(dC7?Ti!gW9FUH=ZCaO33dIo6>43uV+K zL>9(Lz}6c85gc1qW`T6w@3YCkrAS6}2JVzM=m~gDV<=IUd3a1}<{*SDd z3P>zK4Zk!xy&8LJ{t=P=xj_ETIvc8c=S9A)=`X_)XG2 zxVCJp8Xz#q!1~z_WAKT1(3&FyB;;|zJV_xH58A)Dxlq_Z23Yo&FYL<~_Q_w^U8~sU zxHRF^2xCn~i!rjx3wGk{Bhr31(M=s#t-VzW_5+DKsv=*NkDug~sL?`7h^i-+k%=WWKI8`Dh&sx* zs=5PQ`4z`TB28L=Ci+Lh%~Cd~^YFWciJ^6#zCK6b=HsJJkjkrd*xIfA>hT6e0XEc2 zy(WNr#8Duwo#%ML6gzE>Pwhr6QGnfUgH3*x2`+bIpa4*K1!8Z!R~tq9DaU2fIm$YR zm>_EUKCQ||wT($KY7zU70jdKe^ebDCXW6KnW zq|63`0r<2mNU%ap!|P+yWM};la%*6X^J6gH4swJB1`QQB3v9Zsy#jsnH>`o=U~gXo zt68S50vGz}AWEDZs6Bs>25Afy1yCp;Oc4Wo7q?^ltsLp(7=#oyP`4IB zIeDDrLF5I?gvG$wJCigH zm#q~k+t0n@1O1yxOO+`?loc`~hCn`ZG7^7&a=Ld& zT#c4tGhelr8j(NT=hgWs)LCyHfP~28W|0pwEW^D z6tLu1&oAm~;n)fF*co!nnN~4gGm-NFV=yuK79x|TRaQvgP0Lz|$UrgS@t{x(oEcZ) zL!{XTQ>ChCM&|d>UO?L(XfZUomYBpEr?ugF$ka!{cZG3yRKc#KZj$*SQ7W9UWVP3M zK>5Lv9!ou!_t}lOIV8hFimoS7b0m2rSCs+!Hq@p2SGL z+}1C{9I7Qn?Y_&M_xNr&jcOjtDm2Ny6Psk;tG0`6?JDE7erjvi@M=AO`vnTJvS|=m z2*J7@cD#T!h2+(@U9jCG5hOeY(1*izoSLs#fE*tcApOjO&X@1mgNCS!?OV_l-cA0^ z-na#C&f~HL2UUs(fIsl|5oAQb>mCHY55CiW4AlugY2SiB0Z{(g*{e4%k5+cvTkX!D zym)hb@#@vZ^XhN!4vtPwYDeBN7ku*K_}TGM_vx#%WAnK4Y&t=m`E+v2w$6b;eMdsa3ca=V@H(PZE8K|uw+1b1J8z1DUfy&)(jO)VwDtSW z>FEJiz?*2QFMx?<$a07}Vs|N5uNCL(P+V(Ysu#NJuv}&Pv`m28CRxFWhTYS-v1AD> zWqdCe81k8te+cmkRvw@h(A*Mu6Orm*A&ewx`Hu2VTzr%WMZtJR$r9_z2ZY2Iy*38Kso9j|rEVWz_>{ zd!di(`_>=yNTx$>j)`2L#S3ct^>(=0Po^dWv8A%3T-?X8(2mzx^uG2CoYLnr26#2~ zhC!lAA9)61&+)u$tkT>9&&0GXc}k z3BA0%?vV+Wj=!9Z5*V)wI%51c=yuRy4JB-RFdRbDFhuD*+J(=~yVBZ_p|J53%`=u# zeJh2qc>M!m7d_*|rW8wLRLN?!3b5Lny_jZ;fFCELVx}wMa)qUmka|vp+)c&i%Z?aM zQh7*OkW-l;aC1cw&8mF~U`*`*A1Kr#C!?9%=%Az|POHAd-{)q2;f1D;qLk4}ep_7C?L^EjY!3c|+>ljP1!}oLIv%rMK_UJ40G~Pfky|ohPSf zUE!%fyQSQ%Lsfco6L(+bXHb17*E~}uM9@BDr0~u3QHST>$b`ku&=d!;6iD}iv#TuA zt~{z3l#E4?M7TaJ3kh)3(*7cwqH+Y4R>EHft5z#xAz4LYwzMe7Wfg1ita1W`fu}rN z_91tR#Tdaz3DCsf)AB^)R1C*^T-a~0)XS8wrbVJJf_R09IxXvohU40jr|VqpUC(qi z6DF(X3l~m=?Q|X_)wWBvPXGfC6d?=T4{b;-rBRwkc4A?dncMMz{Fn%RLv9r6p*cW6 zP_?&^{{<=oXOh&iGzVpZAid#kiK^i)9f$L?MfVc+?xGB2EH7HYX}S_L2c8 zMo<(JHic_JK;nfzI}7WrAuSWXkGJ?)ywuTe0dWPh?B}Y=_wkMKq zHnd#DbC6F#DKRs}ASRa#lxfNha!r%j1b^^1ZBAF)1A2`))fO(6+9Pp(+{m=H*)}bzV`+&P2DlN9XA5R8#uL6rT_p zhC?Obm@(z1ShON<9#LfzIT7;r=_kOnfe?^Phe&baC5tB-Rxoy2DI3A(zXn^GWDI)D zE*h-D0W%V_VS)Sts%cN!RAfI4IIiU4NV6NF`Nt(+2{H;G>qv#DLGTC47c?sXeY1tg zutxLZ{Yx5z55tKF9w=ns#^^DY7=`_jSsxpLEd>`u>{2BadL)^_nj2KmBUdAG1#AG0? zJ@BBp?Pad(pr8wtpPt)q@EltqvG^-xX7;CJ+?tcLtY%@FB7*1xiRU9Lf@**iMW=wf zC4gb~wT^T>#%;ND%>4Kk@xJYIRwRTT4`!L%Rs6M8H^(oMu7+jc%Ns} z$*(}E!YM2Q^9%2AM)0(#XBbi?-cpIC%o7BpZe+T!8r)1222;?fD$t6m&`6v=drxvaPvs(>36CthL$d$4`S-Vl=rHebLQ_Le zqe8$SI|#)Pq948l$)2!)Cr~}+&=e!^?DfSSAUmZkh!JR`zL_XIuDl_t1d=OqoQhNt zyoQ4*JlCWqt}H@USJAL`{WzK$U?lHxMW!Bf4JC)jo;5R$8#_fVQuu#k_<=!ERe5Vb!{X^iZSa_FWItJr#eF%W7Y6 z0b0{nFZ!Z#Qu3Bx}m)Dp-|TqYDg96S_h3Q>$_cMk);c^$k%)5Z5sPnKfsrkeIO!zOjVsrv}j2B=VyyiPVS^jD5%6{^v#PwKAvy1 zbf@}b4~55#-?Vw;i1Iqr5hKH&G0mq&FHMEq^(Kl{5>Ydu$U?shSD4;bFnH#UQ1zCR z&n{jRQr`v%V`eToudo8G)Tq4B+ZAL@g~gRQdR&8P>Qi5xpUbEkw{iEX&~n9C>nt{p zZVN)WS}6M&+>3o(`nE!!tX{Sw(bJk6Y+`$Q&eLH=Jikfzbk)=Q+)4H1^t_&Xrx3NZ z4ZuZi)S~ukwRoNl?*i(rR*T2E=FII`Vq@^SGI|z*8XyM`h=rI0hzV@Zr%-|G_XqId zGDO>=Fn8S)tT%QNfKWDvyn$sklE?_lA<|;qyU^=$m>xhI4){5=$AAU)cAJh# zb$IR07WX{Wdp6{vv8ySRWo2J*T0Wl;<)u)p)E~9Bw#PPDNF%Q)Rw^IQeHF`0#0{<< z#qwoRcJAph6__B#oV%h+G`QTUX}PR%+cRPBMcv2Th;W$7Uy1#)(4fb?q+U|cCTFY< z;2sC=2pn$UoqHWrSC6PF!G97~J~kx}6D_h34(yvq38r(AwCMGEqNu7&%{b*3xC#D& zCh;Z@^d>AsDp*ZaJD0&|+(`D92_;jLx!k8NN!@vH*)xs<5aeD zEDVRwd|J2Tbp<|bHtxCbNALkv1(qTtXqcD+Y#42u#wV-LRgUdaU3>J|Utuz!qZ24?W zG5Ks{bh)*hMm~ls%33RhuF7%a`MAfM@$gp*3a@iQD9lnTS?U@|ZR-3Vnci7CM!>g+ORIJ_g zPi*g-zet8Zq>GCWZ!WUgNAF^Ad+_`(!e`$d9bNzVUtE8K+ZE?F_gH4CzGIuU=CroG zR(a-nm{jxdvZvI#ro1cHl=Ys|-4|{j`ya4m9ezlV!{<(bqyw-awcuw1;m}8L-vOiw zye2MCb$yn>gN@E|V><;9B10kka_rv?sqnT-=$}r~1_GAm;!hXJM#4Hs!$BWkrQ#!7cXTz6jD)+tIsERlIVrqx(?fYH zmA}iuy(_dm8?U1wTi3)M@*``V5}w4Bn5#p~TECBxtO`O6wz$zXm$c)*{>%TNc&N~+ zBc6rDhCy$x&JlErXL^@B6LRxeX2{}&WS&Y-)I~1^%8H+-o<-~loP$J5i{6ODkBTMG-|G*0hswg zyAC~LRIYWGs=1MvWhJ(-q?MZW^io8;JEO2Q&RVcO`UIKgg`|_a1?LJ1kKuE_0DS?< z$NuHN#%gU88>qbXQN*{&?~lvI!U4y(CQtpmkU>C6>dlp$G-D=K4wa<;a!b|7Xbwy1 zL`|ln^uYsWt+WMS*N4-rXxi9HP=qz$%L9~WMH*pjiomA}4Zq7xjS29hpGTrHv1~SF znc|$e7I8vEY(u<)2Zw0gh3TNzlUXG*lFMzGGBA`eAFn1O^G=3)A1f%!iK#;e0DLRVRG#;Nay^=M;wY zd)kbH%3#a7P0Bi?(an5T_Fy3bgPAuqz6_`?M8H86+bAc9T+j8RJGvS?k@V{SA?ip%(8p1oMK;?8ILGYm|a6=e`qRMRa zHKb2#tUqx!l236y8OtqRQlf2w#!P3w*D3t|o5Iu6i{tD{c5_Apt*i-taL{@E>h1B_ z(>E_Vuird*adQ3~JY1dEFAgq1K5{B~INClvPmJbOq>M|&#)F?-BoiGdC!$}`@hv7Y z9hVeu!oD09ypCq`!XCezs0g8v?J%~$_EOsIM8GSz=3`J{2mSyH!Rza+iuv#)*Q*qZ zW5jx7a-`}U96E{@${e9dJ=tfNO-pe~uk1C}Qe(KaN?244dAn+v(CglhCm%;5@u#+@ zD?q{FB(8V?MiN4df-#&7?myXJewB)KT*7MerZt(u8-fs+Sp zZOlzx&LvG=1Q}>4$riS z#K+ZhWNs9-r*g&URjtM>D!q$p@GGvvx>+_jCU?r+$&_RZH#ajw_E0hx%<3XAW}nDg z;eE5I0(s;GbMt<((1U>st7&A$n5Qq#{lYTJ1)#K`V%3}T1C(zS_rj46#D*<6tzhEk z)&+;vubCM>H40aKP`j&(k0AM)36QJoB21T4Ga3=6RPh1Sw5E%p$$eAl2TUP`$3r%3 zoPxm?R`oe|UbE_}s!ge^?uW>U{$5PQ@-4kSdv)>Z@YM^*ks@kVj`unBX(~sFZovpd zbPU%_Bjr0F<;O%^k!#UQLiA#eRzX2U0vj`hL2;2x<5bJ%ikbOel-(YUWR@_CmI$dbQk03FSflwSvM|@!T*GH6 z9zhRzN(G`wRDj_s(w!^b1qM5Aj#z>1AZ%qg&fzZqQA>_3#BYjz05=b3&T6!lO?3`!*+nN41CW39Zy*j5K?EWPMyMIZ+4h3A$79Qk%58A%W zwc=AkS-IT@6M-K&u-Gh*lfW5-QJf?m3SjX41Qv9F!w>`)zV{pXlI$;E($Dita@yfp zeP{PC$=Ur&+Cgv64m{T#q(~`%3I<3U5C~;Z-a~6_Kx*YV)D>z$W}*ClS|d$0CR z(v>QSh#3Z$vxKG&XzwDrY?eN7Bg#FAQJO`qUBn=U#vBot=l59b;A9U~-FGo@05_Fi2)>q--+>E6^7*9k0x;pxeH?4KTpI`#}P&xycZ^hVea z4lgeS6y3=f^Xr60Rr$fgbBP3{!<3DjAn5>bj%1Sf>a%OsB2G=l9~(T0Mx78lGMAGW zuQh0c%gH0AW7(iFhPh2+r|qn*Na^i;E7*5JY_jHilRfP7cB7={H}pAl^m6^3 zRP=W-{W;r6-B>$|p{!09tqLgXPThe6DRC1>w&hWr1&~6t6EF>=*xC)k6dCluj&g{+ z#aRgAztB(keR)2}{%qfi-JDhROZDRp%JDpJCsuj)ZdUomUN1+>kShRI5>)tLO+h&z zkdzjn+K^)b1EE=0e{_O$GNaZK^h-aj7RA}^&_Sbf>b6Yo*^%CHd3OJ1h;%MsC!+{8 z$3Vf2lr~!l zI>Ph2TEum30@m`6HAJ#y0}EF18S{*z1!)v)SH9*G+xhxehV z7t@5fKI|jEPLTNm}SN47)?1 z@a3(^PGUMpCbN=vy|{C-fZ5NMA8Y#4sNF0*^AsH4bAkw%f;4>k0&o%IRabMwGRF_X zjcw}P8uveEAIVhoG6PCt`K6*8<4CEqn=*IGP8G#O04=8?Jj5n_3 zSiO~`N6o8V4KeSJfyKO@)l9GuvLNw6%j`sc>RByKQAfu|tYy;TCvFrQ=+Sew;v=d# z^^dB*AKVNHi(cGlDR24v<4!GG5aCskL|k0o6X zbz4viR7ThA5;|sYAo>lk1tirJu#}CsX8|y%*1<$d?3X^ogU`!teL~{u&IHwR1Gmab zEnl)@`DWu{s1n5cR9M(Qe17oq+3|~4&wAJCx7`hoKIH~pyt*Jo-hO;yQ zF>oIQ(Kr(u8Vl4{NKXNgyS!Toaok&x;3N+CGsqRykFV`kpgc_#EDHpFP?qOr%gaGl z3|?6$Ne8jNEkDS1p~0hri-ULP-@ZItckul6@!<)=T5ryeHwF#mCnWSwU$Jhh8Bko$ zI<#Q}wcjnTM1sy=OBI_Utca{u4Fxemm44>|1@z zq4u_Bu4iv+=6d$_wX-hx*pt{DfTs{f-bX!bimKTxXL(0IM=d_eXQ5wcYE(nVO{^$# zY~)J^!HNKbKzzTTGOQdmAKrbNd85Aii<$ov3w%+Z`U8C^@T8OpDB198WUlpaBT5JP zY-S13 z^jM-(z=_^o$Ee~=6eZtPL#Tam(yH@gxw23r)9#JYpvRpztOe1dW)xILtJ?hmZJTxl ze|ekD%YxbwtCWFno$_XH0mR-La%;)mZd_dk$B5Y`5ML_na*m!ncp1gA8 zW074n+soJ*1Guo{H9M)|``s^0Qa-d#%xxV?$QJxl%wU>gUunxZqAgwd+p*F`(X#dU zHxUq)ddwud^dC}|3#4xMTzay5L4vH~lmV)?9qgv3@4Tg5-!WTlZP#DA;}*B&CL39b zz-ILq{Iy9@l#=9&q4FVdoGEARJK2itUfA>Q;>a9qvkR8{tY^s_wD?P{3(Grq(Bd?0 zx@3DT=om5-E`*Zu&~|f+pci;v5U7BXiHH3_0zR790Wq}gf|NIb#X3GE(I*(Xk9C2;%leiq;jvQMYU+|`vTeWlZb zmjHLH!e7?-P=EI3`0U$v2QOZ{dkw@5PC9QT<;1DD{NyDXS3kXSsWyOa3WER_;F!d) zb!bX)$Mfg#whrH1bXzUyQ{A*XfHSW1BcO3M-n1Q!^W$us!d4zCH{5*&2zQ?WwufjV z;SF-!Aa&g&q`tc!^<%^s`XP{SF&JoLCrMoF`$ONcA!3xKo(%~J&xbTkm`6_BV9F)5 zbVZNvGp7Lm%M|cs3i$1t0$QoEgWacqVD~9tdu+Mek5VtNKuZXALeKZXF@~QIAP)!7 z80!0RA5?Tn<|7ggG;HMib{v3?%t|~5Jlg$1V+wGg%ISFbI|aB+3d1i`z?UiDCp87! zh4SiM;(pQh5(DGzCP(b&N}weL=&SvVMs&#*3|K3`*V#mjUPPB__g)S$RYwf_1p%eW zWHRa$WogaeQk9afE-lV|Tu({=VdvHOKLJz$e>n%}D>mVZLV_JXpCaL%P?Q`8dDg3Q z32X4Lf|1=nL|dm(=f$z0g%;eo{~;s%kWV>ma&8bMr~rAe?@{!hV1-i0R-#J@LBsQbor|X)d{pe;^`j z$TDc?VL%uhZ$o00BAil^y6LUVKWX(qYp^k67Kj^xc@oShd6@Q1Gp2C>W4+4u944>U zB6Z7GG%<)atZvPCb__D8iQD^yjqlZse2YlKhU<3>H*C7orkI0X*9Q2ETDAqMEgLeZY1q#p(*eK336O8k zeIRjjI|2sRwSoltO|BKYp__ot3q?5%IYX#|6U@5|4-P(Eki%I+lMIdNV30xJ%7(LH z!K-lkDH;>UgMNru$6$?*ad>cge0I=%`r_dH`HPch&o9=w6HC;mx+t9=UtEBlQmMvFo8eIAP4^5-ORdX+)Fe*_QTa2z2Qb_7Tj2`Ku4A}B*-F+`4vHd) z7JEr+OCYdvx2a~ER_l##D~xZ=c)A5wc4L>9yFJ$L-g=TDqT|=rY()>!Qe<9zy_O{@RydnE_?ao41 zCSA%xj?U$DNUFo^K3HFQ>Z*?C<~^*q2Y1f*gYr<$f|3%eLdw&)WmU&Ieu}owQ0$f z@{!bdt7@~8l-|(9QO24Mr$V)^d{k>JzJAYIuaQa)Pgr}h5tmv%`%lkbz2uiXUZ~m< zTC@OcA`->W(XC?JF<0_R;q>E6Z-4t!GevCMy{}ovo6NWE)0a}krQC7KWop>?OvMJv zweSKXA|!?~&kBiK78Rd`icxh817Cf0-fCW7+Yv8NAlJNIx6W=Uy`#c$C_t{6d+i+9 zTi=r_k)bWcRq>!M z7vCYhoAn~9IA!OkP|cs(e?fPoa9oVnwiV~srd#Y78@l6WQhgz{;&)YFoJRlt-;5v> zCbw$FjOeMrS@>n$o5UwvhK<&8lil7#quzB^Z(=(+nRB=3Yq$+1yxyoKpY3jh0B>@X z-_cRtgb2Rt;_bJH2o;2&A{&GWHiMAra!`tnpaMcoDN1Eu4vm4gL< z_uZ3|SEt7pXD5eU{_82We#d98&yb|*Zn1Oud|2+za!i{{%lRDBaz4kjwyz{|`WC{I z4w9rGJ8&{9umS`Zve?Oe3zR~3?&b*#EWp)eM_}Z_fh>lFC7?futjukgmP2hY=Zk4= zLVVwob=jX|UH0c#*Y=eSft5sHpBfA>P>z?vu5vpN#W)9Aas7UX&{mqc;MNG8I3FY) z{y(>%X-zh`{-B{Q=-L+e;DRsKwcfglZ0}C?WwoL_K4)PJK4)PJwqF>1%tT=px{yJ4 z?8wg33`H@BqsEAxf`bL+G|0^i`cA|entl`op^LJbJdJ#iq(q6ovM`3WvI-H_j49TcL(l?6azCzP>-L%8_zr-U}8G}_c>54 zNCexKANhdZ!oi1XU5HI32%p_3jFLeDpDSNBE%H12mKA<6t$SJ+H(A%`ER62wER62< z3!_B{6}U?mkS3~~rcXnaCTc#07g<*+*nUj+a=!;6HaqZ!TnluL!RG* z-Wv;;an1t(Rd1*dv@rT_Lf&Sw?7hx&B_6(H)i&?;d%KfKnVwmj?^r&zZNh_HRo1zp z^UYnh$aAc{vQ=}IXE)Oh{Oy-AR%C{1DTTeO5Z3m#DTHk+gSEZ?pS?F(Z{$eRHTS#< zR4po{EE4p2Pz@)k5JQSmOckV3nPI(W2`u)w^0fz$)xFbkPVlq`F5rDh7x!G@K|M-1dFyS|f(S_-3!Gv$RrM;Ui_kg`| z!aPbzzcPX#@{K6YQ!-FFbSaUwQm{Jg7^Vd;a?_X;#59Z?;6{unC2Fv0O~9?-1ob^r z*{+Q#WLq)emrJ;^XTp>ccnRfpIAj3pp;{L>d-hQ*B0PVYu|^rYVSXAx`|a-YV|SmQ z_S!hUP7D75g+3l|MlQ!&9%WQ5`56_;%##U*FIY!rE{HE+`f}{~c?gh-8~fnpujYeV zj^(?)b%*ogsr;NI3W;m zJM|An$hh&5*bSu}zoTt(L0P`k9uB{}HzmT4G^ zepY~~3*?td;zh$Pf>%O%6Df4r3J+$}oyFAke#(hk_t8g??`+Nlc2o@@UXF zyzc;tWzm!igjW}S%<0bizR6A|$lc<)-WQ4hQkfj`nu@Ph(RS-O`?9H_*9w(&%daH> zc+({cp6U1-$xl)$bz@sPZAxVwm03yCW_i_uS*CSW=4l?2)jH5j+K0uwmH@w6)C7&X zxwnF|G!vwjU28^wvUgnry7bL#iva4{Je8^}C526wy)Czh>IId|se|U!sYd&uLaF#> zq;Q&FDSlOXR-<9Dys3!`qq0NG5wOGLsP56ntA|J3>{z!Wexx|ytz;8tb!&o>f%HcG z@~0bY?yM<$snV0FUX|11YO9-5m_U^E`}Qv44A*XjP3!gTrepw4w+4-B?IgUAlW?;t zwei{xyi|Uk3jtz6B*Gy}#00oAgg3)>62J<5%LIMHO@~5ZF3`Pm zv8>sgPhxq;#7URR;9J0z(Az@KpdMy2zFNM9&}}S>4?EqqPQc_fd)o^l)UDoj)hbng z^A9lVteNgLoBcbbh(Rrav%6wSpDgIG*NPK6y<8tPq1i#d(o=2DUw7)CR2&L*k9_xD zIG#4BgSuPCE8SYfp#55#^-_g`6$_fEK(Y!6JB29I_lJtUPFeInjpL~*a2#le)k^0C zv{ab#7Q9Bw=hL<3tMk_Cb=9G6O}+fMU}F*B zTAC7ETv6sJH7#td3WjN=s6>}1oxFM@=qB>TqR$w)!pc>YHe%UFOKTIgUscaZNy;nK z-#Nw?II_LB3SU+Fs>-T1byj}VM%A>OFc@@{X$Z4n@h;&1>V5(cdvKrtUscVgA_+nc zs^f!4qc51SlcESxY(xe&5`+?|896K;t^QWf!SncfpfOF#9v0*-z15*Fn6UZ>ok2~; zKnEX68%<~?azS?>6D5J-<`fb7nHNp3$8#LIX^r6_Vj%DVJE||K&mY; z3u^SjtG{(W>)L8Ddo*1^8QVMxpVg##aYo=(N7mzQU}zh=*@pFaTcg#v-oYBK_5!#6eHqDVZ?|5HI^75k*!*9A$V2cs$56kB++UB40frKc{K@& zBQwh7BXqvgl7D!Tm3EXSULXGiF2v&9gj-YNP4GHrfBse=eax>+W^)MDYbH@GHQ9)* zv^$@vZ=_wYx{!s}&lIvxGz6DNqg!CAo}a$eL4HAt)vxer`4gNY=PwV{kLtI};ra8v zCYZm>i096DtM`HsF%;nw5}0QvX9&QldP_CEUms0`%v8pHsu@18Y6Mu9=RLxnkVB87NEhi;_#@MB^7dr=3g~ zM!){*0Q0JXHKUM3_e@FXD_hD~OomQn_I zDTDTAA$?_hJuA>(u%&PxObbEE*i(>D9-mnZa`x zeWw+)iH+Kg)nHbzgW;`mj9pm44uZGUi`%FF879Ppp`Vh<2lD`xfIx-Dj_0I-mxG=M zb4ieh9ZjkiFo2X6S{8VEUJmhL5+ti^osva}ecr7K+G)YoSpz5sqLj?~IP_xZRa`1J z97yVH&$F`(=u&%wB3)>>cUjM%ruGO$AnKcqQ|xt_Vz0{-w~yyg5aVT#_(g`K;t^f+ zZ8x`V4<4GtGP1;ldxFZUKq}aC#?uOeEG9b_Pf93bTAN}cAj{Lb^C`A|+f&?#@@jXP zVY|x=cQA%y%b@(B7+qLFeI#?!u74AW)3Nd1>usYsy6RGN4Rmp?ZfNN>&>BUS~q zpL(v+=N9OPT8rCMGg9xbFi1C9LV&>^^dEa2KO%7Lj%)m*?v-G-FK+`s z5w`bVMCV`h1chJ6TX@3Df|EhF@_G!FUHO$D$=_t3Rz838^%~ijVs7>`VK_WEB z$~Vt$Vc)}UBRN>p3S!G$V#D#%x8v%C-+y2I^zz%@cTA!5QPBP>znn3bt6z{$xcq$Z zMpONj?}=r2E5K*d#SnWIA1?Fo(EbwJr{eAZA z=!1X0YG7Rpu0xIO0va=0 zVcIv+4I5hu9_o9@g=uW5b=Yuq`&=j}Zz9*uBR{a65L5vhDtOQ3^zufVAFOD{~0cu6+P z>$Ccix>Y4I8b!(PB1wK1L292MEjK7#VA&A?0Tz90i`%I&_kP!byZ(DE%l-yu-*v7}70vi3`)+VqCXtp!F$}I*}B`!mpE9Km&5Xpl?8@64)f9 zz}uIQrtJnsYDPYof>}m7Ev^)xDsBEqgVmy0!q^0@!%nPz-C;)3%uz%bXhvB4IC?;* z0OcvpNZ?IF*V=H(lm|)|?I7q{m)?4IoX?%!_K8N8Em&3=HO?@yQUhc7;yp976kmmz zMChXl9yIZUQ3*{hKIndi5xAj^iv^q+``)K_9RTZrrq}OU*4}z{?9ZLv4khoNiFO4G zubn0!5j&}nXMrahfME<*Fa9Uc88e~mj^XCTHR{^*@V8J0 zn#_rcjfpplcS`bj8S|OMpNn+do4MAWKy4S7%qCCS>g!N5w9!!}t`C)`U3TYClfytD zGd?16D1qAE*Jm3#xz-6vt4G4%Fi_tKXniC+hRe4h)QZMHtay+dN;H02<`PSgwuq4D z4wJi@4^%`Lw~dO2^1~^Q9^M{l zTA6_ak!2WUAg42OPKiIGob`KuxEcIGJY8dX<4^jU<(m?mkA`#gid=LKeIiah33c;U z)rbACoFgTUy)R^zq?A?C9*EKo^)xPD|I5llNAv4gTz!9(x1?$HVr2vSg-xF=1?Vgx z#Xc!HJ5GuBYDNat=Vw~3#!b5JvYpP}r9%3SOX+W+m9E~>Q30*pyiwO&)$9@d1{&Q5t4s7kmT0s4VEy9u0CEIHa_}l5YVAZ4JPh;QXeb7drA?{hmf^IY7RzwjPm9$d^BYzG2nGNb^;0R<&&mT5DnXbJ35PkB5eJr^#Bxg3NDC< zVt1LA4sl0dT~LvMmhw`o?fG-OG%8NZR2gni# zRw1rU*Mc0~yxVCB>Imwcr=?X!tu4=14yy`7j{anC{J(fy(U{p`cS^Ti*k7l*nFq#0&uk;zq*IH|i2uE?1a zR-|MyCbR6iEC?(6Hc*;OQu36M>qakxb#0oFC&_=W$1#aqNBLkfogGBWfm(@2{^x)E z4_#|^q@P-hmdbIH^sbY%?9WtHk@Y`j!{O2U5u{c3TYnQh%OD6RDb#v{wBjsMzMi_r(XDfH!CN;0>+;uGCV77EVZ0 zJ@)xR+?LNSL@--S_Mt8jOi2;}B1kf1xj|56Xgc%^5jA4Cdu7U5L$|0&8z%w=>#b;k)yQDODS z^>0)@Fyi?3#cOgJXDV@27OQ@ex?vIS)xr$|j5xJbWLJYmqTwf36Cq$!SSxCwFQWbA zX;#S@r=4pHUlc5AO~Ksy&uz8ssjOx-8?4AdTk}z|H6KNb65!psc$WqnD-X|P zGtfd(uRB!jPd-d`lAlYmlDEr6Yqm>S^qSl}+hKY)opTrFc68o`d)njU#YP%=kwd*t zfF~yal-GyaIy3RT0*4Xif(c;Zfdx#GATzkPSz_=jcmck|msg!R5Ds~3eAbq?zcXiX za94B?GXl;daBYf~9NQu07i6hLu~L#apj9XF7H7_3^Pt@)l0bKm3%gNu4Z4d^&|QMs zXTX}N2bnb$qmc(IIysR>0B|^^rGAuROAtP`u>r|Lz}g5**RzZ;0s|vALu$DQHP;0Q z1on0^b3L z-U!SLPIAM`96Jh9iy%Xm*AP1KHLQ`Of3^VCdTrO-w{kj%y5Z2Th?GQ(6Vf);k*tuinqxgb`BZKXadD!0lY zn2&U!jX0e%z8Q=sSW8Fxe+LJZhYhBMowe!Bv_(b>1JqTO(~1$Mf;!S#5B6q2Go;9g zDk5bQ-AuHdB3kl|aN~}Y`(3J*#tPB=`sgFzoZPGZ+GIjR1$Mbo(+MJ>?^CYEh;Y%f z;o)AMmr0VNjj4M-}IeL2b` z^FoY1Z3b5-K@WDnR~fbMk+)^j`qYHu`f<|+>3C9N(yNR8%GEPfbWA>7Upgq$J8!mK$ydTkve&n8A8zk z^n8#J!C&=5!eWKmXS#PzS+&Z{VOo(OQf3gBk~M-~(g4T+gmuq0f;<$2f|hUq`pX|F z@h`sl`sEL&=OB=MxBTnk9h00XD6>t-<`CCJ^SY5{6xV6Y{W!+ z@$B83x8J>aUCBgz_2!!w&;NRH^20YT-Yg*nnm`HGQWn*>8f#58(S)zY*Kgi^Q(Uv1 zzGMM)&?aYg{gB;L!`V^nZyQ^R{TFlA0yUsrLs}TxG`9_eVB?g1ZljwgLt6bDTB{wK zncaCavpa8Q?Sm?Dn&x;dT!%~|vVv0p{cR}IA`H$1&|3%}p=2Q5;>AQCLJc0l z4wo2SEd*(VjtN@h(`NRxnLTZ0zs1dLA0o9K`FR^lrs}D6n{P6?rA@J+RvW$ zv)_htc6U3O*?A{3JMUy2_#Yt_jWRfoZQl(;YPLxD3P=xg9Mglg)+F~ohsQRtGAit2 zhy^l}q%mld{FQ^`px8yZX251 z(Z0@wY!*tZ^9YS9Ln#CIpHL_i$st_tfc^w&451v*Kn#QtlW^%lp8_)=VJwer&wL2K z*&3wo(~kDEqdo0t59K%8wwc+TH#58QX4c+sMw&63fsao=fjbrqB=Y_teh(vjVm>_Z zt{aJa%rGODlC6Zl9ZJ3|FG>wJbHn5z{AN#^+0$nBw3+>yezR@+nbmnevpVl*?fqs+ zXhlY7x?u4EbDqEi2JlS)0hk9XGdYi-HHHK)Nl=s83Or?g0`v*G8+%@Ax$#5z&7StN zr~T|{Kl^q3X4`f$v-3`7cHYU_`^{h~rxMH}jW$Xnn88yI6BA0q47S2pXjlR#v?#Ys zT{8p565G=c%}i1?;0z~N@Bn_Zr;Y4sBYWD&eqFy=BdnZnb@rQCo&9EA%8^5XjB{bg z^g@HcrUlC+RRW@{xlWu1l)oB&XeC5RT`P)BRU-@9GIc0Ufbr=+kl*Yn67aO6J?&_} zj^AwCW@dNZ%}fN5+RT16zuC6^%<8*{|R? z+qRRLop&;`^G?>@Zx)0(2;`}o3pyo$_l9M8A?anIK#)eZ57{w%Jx-XTae#C>w2?h+WKSE}gZRyMSGj)@-cDv12$^Mld*#a->X;>1`i8YvWxHyv z4%PZ~#`gokYCZorJ^}$wz)Vk8QLcoQbyG=urJK~HZ^B^Z7Y)HGjX!T{To-SbTr^U6 zeg5WLGj@Jg{!!L(4~9Z%q;AkeH`Vr3O}k+3OQU0f%%83zuP&&MzEZX1t(xRYRrb$C z{Q0T`D+2@SYMf3BxHN@jRq(V<<&MhBd;lDkTJ<`1STvNfX;4$;`V)OCHSw3xXz)vM zg>)QeYtw1KTo}V0+zJ5n%)TqCh zh~{x2^DQdFE7x~KXRY435t_9?hx&^wmL~2>jq9)z3#t`jt8S?Q4a(S}FIATFh1tj( zwBgDQo~s;IJD>aqgKV$F!BPXSl^UWn@~M}!6VO(iC<2BhPO%Dsp|r?mO(o0Kw#{Uf z>%|G6MD&ci<$zyH$4C7m5|Eggml%=dn9$#)@EtiB_%P5jwpNb{1!ucfP~TL4L-;<1 zu+cTjmC;(RjMj411q3)xJR9U*@|=A<1*owBYIqRCPZ-8w2;Mv3p}0bGj|yGS1Vq3k zy$~YJ6oK^W@c%(*!Qykb`0(M8Wq+<*{r*KLY?Pp|tpJ5>Lih3l!fjEiK|c_d1>vSI0Io_hEY;^&plgc@1zL9X$s32_xd8eKX~AHG zMt)tW*Mi)$=#mD2l81=e+hd$6&`N)pDzMh+fUe$FN4qP6(rbCrkLbS5*h#OIly%g1 zMd**0Lf=B;UAbcyr=_9M{kF5Kw<8p2h6%K`Y7r~T8qo7?=fK*v?Wwn+2S{L9_JEbN zQ}W-;ET`NmGa!NTQAgSF9V?YWQmPT`JO=09z;%`bTI!nc&#cBGEIZ^MztdS@Z9NMX zJ5=Q?FnqjsKw_fJz!y!%9&eJFd0_egCx!M-Y`~S#y3-xMyNKSdQ>((rlUbIB&>fCJV&(zr1?;wuJHfQv569kLpB}GjEb5nILf^ zZiBoS0Cl1r6e#8;M!P!4$`;E5S#Tev^}a&rR_xFkq!98+d<`=JR6JnUhOAq>Ld*Uu z)d7^nYw2pnp(%&{RJ~JA22_rId@MqiIp&M^3QC^0K9DQ=`s|G5jC&=&38=MPs0pMQ zzfV&&h&|zgr14xIuHg0$hjP}B75kDjb+G8t^zYQcsT$qSm2{@S9vjZh+K&~)LB(9T z6eq;%SfI{@oO1KQ3Zr5?=Nz1=Yb*C2%fHr-*UGtBC2*XxPb9`{booB{@X7w!zn)wN zzrc2oPOhxn8uD-N_S$G9a5VaE-wb!3x`u0b0B5&3e076Or|+VnMxK)Ts=Qd!zuI~D0xZo^b(MLg;plWtt2M4W;x6Or+2OL z*|6D~I}w*SyG=A}mAE8Mv&_ouBnc$J6pRTk$#@GB`lwez8`|P~c`9*}HrJ_8$gT@toeYLJVCy1q0)LN1Aa`|N1aen<T$iM6R zHphDqdcF$*A@Ju}4DX;_Uj$Yd+#P$5=pO(tJDtk@mNXwC}omh%|TYA=2E{ zo(&?QbcjfR4;_S&1Sw(!n7?a85WHKy>44fp{UqFNxn;RI)pFZ z!{FvViS#{-bVo};skHqF4g4DJo(7{~^`e^n;1ERr9Q#2l)hE*UnKWJxs}~n2ik%Hs z#JQl3tH5~e!o%Ljs{smjI?gm_RH0LA()fbmN819*4ETqi=VMfK(X&Mz*B^DTSL}*> za5)#>n(FlV!b(`x_~9!p!sg}sltx2ASU(q1RB-@i;;>T#s_DGS?}XWlRYb#sYas{Q zPrFx7?>$pYQ(BonJeT$w^!>`^PfGuTDgCRfY7hOtv4QqlP~s$b~zomc|m+A1@d)aX`sVRGIsPF|k8dv*6m#IL4#Zxp01N_b-f?eje; zry(Bbdv;FP*@{f4h^R75sm;lKI|+#(;wQ!jY=NB`n!wmcxof6&Vx?Xd2}e*>5)Iq% z?pYn*LlS={a&>p=?`E;+pb0lN&_16b^X$N+n9Dcg%rvYtCfb|ZQIz?fsxbZ z-m~(VMMU~yC$l3z4jhp6VmnU(IB=Z^lmjE*ur)6WH@d5#k4?X<3rlweb5jfLxkbo) z1tKiYLql9%o&n-c#6O|yW;uosvD_?!h}|{8Q6w`h#RM`SQp%tOG-5M4-9f#B=}ycGDy!w=r~pMGt7wGy==D2kc@$M2j>6&xaliqnX%|WlIUJk+5MKj-_n= z%n7oLOK=59HEyj=;UPSiaRAkiQ2az;5Xh@azC{3+8BQ-84&l}fd3V+ClX|&PranW* ztT)r%l}Qb{>%o?ulFRDaG4PJ2VFor>kEjb#ZjsSw5REk)5)RNlQ_IdNWOpca0@m!II{k6|}GwpXgJ4^gHAq6!Iu}PRT2dI}2_|Tm4D;ETKT$dzvQ3;q+ zu@#WgWv4lne?b()sck;E3i>-0tG!gxWH`8q#}xi<)kYUr-HRgnY}xwb&_Ne3v@M8E z+mj3y@9cxtJ|zXXNII5}MH2AAV(6cg(MO|_fMvw8 z_nH~-TWMzD44^zVPc6rzs>LJ2DI@0!)BxW@5w-DW#t-hl^sF#*t=#n^57stL43rH& zz`d0vxfR&?odnMxn_5{Hk?tDhrWV>KYa6kXJ7!9ybsWV>idtz&Of&TXqs)C4ISCtX zNQj&iR*ypL#4*wY#spguH{6F;Cif7s$EGr_O?YQ*aZ?NJ5tmjebFOJ+;^R(v6KVYwFwT7wy{Q!i_BsKq^{kkuSEjT5YqqbW^+6V<4ci7H?i@ zuehiA^@sYFYImjfeCGg&5wVy@5o2XH@I+lvr@x=}zL?IDWJ>0z*VAb>O25=#QF96c z<3%AJHzNB7+;}ZwBOp#v(9WJQynV7L9*;#)*^c)&DmFLL-hUW`2^B|p5MxS>+<>5T z9!iO^8(V3PaS@~x)CbTjP(e-gv_ZOB;^dKIV_f*5_wcT5!}Lkb5JSkuKNX}t966bl zP+AD@jh)<4c^=?|c)QO}o}HgPJ}YBgX7(^NHaF4%Q;aCK4b?44hafTtGl!oTl8~Ea zi3eK|*^wFaw*lrZQDsW*0|22?%gc=ztJZvY$8~qX`)!HQom(CoB%r(TvAL1J-3=Dbplb-ol^vrUx?X6>OXrT4}Y)K+yNHx1{*|>Xh7t#ol z*AmAK92bZk$IEQ0zkCmV)hJF)+XS%56$~c|@tpdZ7jtSqX)_o2?NNXK6K zY`ul{Miq`u*darZ?kI}zBMKy(%m@Dt+6UrFQ2^oqRbSf!h6B%dRaW+xq86k&{?bR)S$Miam@w#=P9Z(5(P+WPP34hUGdfkgYM1!F!lKVntrB_bTmwPI(WpCxhL|-f@p3bfy z0+<|bRd2W6`lR(fu-02$t9vWH4UKeg3Pa0c=V4~MRuUvYF<3sTh5?rg{xc~XhFpqh zu;the)h32XRM=z>HsgqMAk7{g2@GB$N~^QP&T@zY2v!4;1MO8BW-0FyHnT}Rdvo&c zOi@KDs>+gX6YiGEmrse$8zic`_S?`%dygB)bZNp`&pZ=K01{jXAd~Uz8yV0@WU-Rv zO7$GhH9Ty#@1PLNMP}-oQDUaigL{4V7t2-odu)pCCPC<|>ozpfzG8zw7ZyJ3BTtsyBB_v(%Z9^flnHMtNO7>BrVI9}pXY;#&}sChH(4@>h6j~N ze{!on76nseU~k2;c60l3Wr*4FTse^e#1~xdL7e+3FoqtXS>Ffl+b}J*UVzGNqO`H= zpl~{n2f9AEvvw9gxI$@BYGMXS5`hgF<=`(mjv3iboKu)*Qg8iK9k*RNx+{mZn>%2C zgurvflBh~Xp)ineVrPK|AucK*u1~|^3J*^f^tT3HQ#dGNJXbNfvCjFL#`#ZdB0iY1*Og6{)=ComiUtcIv}a3h#d;XsmLCX)A@+Mewa z&?mP(Pm%}M_kPoYvX^Q%%C~EF{JyT(1xJSUnTp-(=g%ICUdOAnMsJmCYq+hwC*0OY zrq8iv_EzUs?``iMgeuIX_6Q_u7y_IOup&5;Ezike{wC0vz$1pk#Ypkivqkua^bp)=`pgOt75J^;F0K>m3L|ifdhJwjZ}X}*~fJ*-hvojYUSmg2`w)=%k8?F zNq!{9E`#l!@8r{JJ0L(no+Z@o)82pnFaM1X$($sYTlsLX(n;M)zbCyfI=o66ych%q zmVWb%dX$24(8TozB~$Q9AHLGp6VPY+5A_G38UB#%iy>C+_x1q&s%Sl_*AJ^+R|bE7 zJ$rK_?T@8|`qRwg=ph7U6L>dM;Y9RYhjc8?AXZbFacDuA;CWD$dO~Ouhp|a+YLo^K zACHd!HxnluRKZ>pB`9Grfm%!Q5aT421c4{_X&WioLz&*(Nc*yX@M>{1QS2Y!41)+a z6R@!W$|SJsp~ofZl&m`hEcfChaHz6^7B>qFazEe^&0AEp1b3@d?;&cB&7Hbw&bw>O zn;U5_H~>MUCd3&)&I%L+p>xrT9dWQmCOocImOBWIM=c(35=LqP&P@aeKfDR!c)UA% zG`L&U`8O>%duhWrSL0D84!+$5{;QYXOBK`G#Umb5vH$8@HE6|%VWB?qS!tDYl5o2u zhbDCO$^ZIz6vWk)w6}`b*i8HK5W`HOgtAiy5T4iu=o{70ry#`j!z_q}-`j_;o;)~? z>18zro}g4x(iduzt_r1IGEPz@~LBy1-F1KuNwfXW#{yyXE?YGjc^vv!Jg zlba@ftvK@y3kcKb!Rwgz5Y*qV*xj{qx>;U2tEY|4v`-X42Tc;6wBR&P4c~`Pm}+=% zsS@C~gByl#fkt$KhIFJ0<-Cq2+|Pj>Ldl&Ztg?D)eVo~VgD9^vwh zhF?BuxsOK6T`sJ>HQbF&wBEg4@pbwFIh8;Y>v*Y~dYJ5Bqnd(N9tZF%5ey@7D}m-P zCTl7Wa%%FyrgTj&eE4#F8%GBq=7wY2p#g8G2{a7#Q@)hcT4wA&xj@^ zJARBsZZT~+u%eOs;F{%cT4?st52shuwQN-l)fF4dhCVmD^h~$>I21zlGEEGxp9s8D z#vb+5V3q|$CGFOHqA{6B>>&H}N%?y;%AcC;z4gBO-R%*WKqa+YPFpW0xeqR*km4c7 z5l>NK`p|BNc^szjxJC}hH({JwG0d4M^_srvX7*}C3HV1pyl0gUmw^{Y5!-x1J}fAE z8RYV*mw|f?J8kq7_1+~O-L<{?-5t;yeZa@#1pYMxcuG$`@)M#g-h;MI`&|)!D%{>6SvT)$hH4W%Wkpk^bMR2Tsc; zic(mm9$Tw>d~M!BJd_Ay4VSxAHgK_>E2kh5YY(;%c6qapwg8yG2$ z>MRIz+k&Rc1<4aIaLgRgw?VJZA0DD}57GNw%GsT(qID_hu8wYKp?&QeI6#4*{;+5H zmJ^vk&*QzreFx@2B4$eHlHF*V!h{ft4rq}sE&`X47H&lkAIpApa<;D?yE8SjxaPie z*vFKNS-j59g7~S@1OG4;m9@v%L~%6L@37Ec571xb{+bT^q{9Bqs<5Ax&bfYnd-XLY z(g>}MmrGtIE(8jEsINzlHIkfD!tfj?i%Ik(z64M+IF#*uKiF zP^9ltX62guQfALo+mA+>ReYeEm01}@rM|9sTpKjjgH0N%^owUyd6=e8`s?G>Uqxa* zM}Mu|-+C|iz+Ox!-oW<&DzW2d7QiH#gj(GQj|8!KQsvM~2}*f}Nreagu^?bihervn zks9MOwWRlr4rK^KpD$^pl!StEpHmcInUR}NFaQGS-DGN>oxc9HcmG{itGWSIHgF+5 z*><@mOyPBYeVw=>rhDncwfoyAB%|g69VvF2XM`>$8PMF+KF2uNU{Ao&Mj3Gi;UlyO z7EWI~1$#6$0JH^@o)m+cm^1Jd)K74YbejrKlCMKFBO1 zI8`DypsFh~{4fnE@CvX660r@)w@fJQ@_`ou!T_=WC!(t?eLPtw>!>VAVVbPsCu=aHpuXrQ$WkuqjmY2;f% zgXfw!uU)j{2*}mUab1vLDGqc(bZu(9tPp(GI3s@?_5qYw;4B`#k)LwjkpAIVkK z5TIN2+T~^ULL>Df&7Ty_KMvh?`9?caEkI_S00y9V1{MnCkAvrfgXuihn(veL_-M4p zX!W9B^_21y3=zle;8ZLNS#Z ztZ*m)6+o}xKnOB7OK^gFxtZEA_*||FePu#HIYG{562Bi`JbzK3x;#~ltw~LH6>?(} z?LBD;C1Kbofg(irT;GX(fFuFIH1MersD?%;bV!&mJu8BjAfx;{fH%VclFf=z`@xmS zI}yCcrbDjFKxZv-V-xLb%Ls!RaHr&UBpDBWWJMte6d_Dir0GTyS4cyE(hM{;gTe+B zOOJnuD|j~2_V9_kzcVq~TaDCMM?2L>ec8RKk5;2WDWa8cK}WC>JH?9{ z2Trt!8T#3GC$GOfeNrm7O3i*sRL)AhsQ>Bu=4P`- zxq8jr#@m;A*E-!H2BG!_vf85_>dV>p0nMVIEGOMd zhdz$>G`{p*Jsa9-z_?KhXo93&!Q z3!oPnCE~Ho@!YOP1gM&~eIvYQE&BeJ!a}hBCZzbz6#30k*j|foyuZB^TOtFnWsw#| zeVK0(XCSXUM@XU&%weg(WOI-G%+E0r0k46v%HitRA|zH*;qOI?*Od4tDefx8+wBOn z$^P{`jwaFj{&mc!o2bld*!a_`)gWgsPH4NyYJl=quGnZbeEqC!an)8pb=Q#plhGl} zua{S7)bj6M(F3LTj-n5P%Yv(P)rRe`%!a4*7`NWX!R$)#$rg`@(n{lp%kKxHBe|X|TFh(o^6EvX=!@%=5{ogg{9S4O z)A@}o>uE0@&#snNH}tHeIj+1WCelz4awV5nFx0A}sH}#;_02HDq*)G@Wc@xbTKa0) zl)7R*RDbqVSB3-maCRkhx5+SzCb-up>3|$xSRAlX^k_+iVRSjlW`m>`{W7?o&#tC@ z)vUgMrLk```@?j3%XCJ1$0aXMum0!%d)|2G^EsB^w{QQ_QzI!R(pdU(sivxjL3V?h zKhr~Y6Y*6#i9YgsV}s|a9Z4&Jj5nQYrinx0r?T6Xg5L#)rIIh((GyBL*wRa3O*cewj_il8voQ$&w(sv9a&7?1mii zWFkf|eP#${I+o(v+zqj5ljsI}8#|NKa3x4ZKN|1)dOjG=^lblIHj_QCC#zm^*X4~~ ziD^4PG{4C>7MdUaZjPH=3_1^b9MjhSDRft zjLm4K$hQmE$)VglPE@e&kAJMRFrN*E&~g9qk7^-Y4P=ZD8SU)7{H0eUYQ}Af8)h!0 z4)iG2``=zy5LKm`Dt=(rq>l z*~)VkXb;y^RvCns%We4(chukm>-i`jOr}j|UGIodN+NMetv^8;Z+7#HhPMq2;gyqg zGcox31%<0;voEI*M&97~mDV0!bN7erxN_px{W;P7A5>_h1If#v+^Qa;NA{`pw{^v@~vDYIqwTdXR5NB?Se4IqDUd(D+( z_W8%%;Yu>S-7ciEp^YYoG_cXs*r>W)m|lZr(R6wH5(|)8h+&dY;t3@4>-jt@q^B1qzzuaYXP#O5* z&o9pZN`>CDH{ZT~q3&(xT3S46n_p?;%=W+e>+6$OFP>c#;CJujri;_Rh#7GTv)k#r zS1-=axW8sojMQ3Gk`_&_J?H2{5FPRtwK9XF4}Ns`De&a~FFZ$-`o+cnT&k%58tL=3 zj~0}EL=);ym4?$_7#E8xx0&H;P1sFq!s)gqoNjBvX&)a8h^YoiXbCw>5JReGWS}Lc zM4ojokiK00OtLfwxZVmpfNdeV0@sUZF|p*- z425`ul5qg$ZT?`(9W$wQ=+#{1ng8h(dV9lvITdXHI=5u<4iQAK&R~$+5-CBqG1ln#! zm-I`1t*1aL%A!SET7RFZkgqaMXIFzsdV`+63{p?AMTjt$dS`y52U-O{^*~OKldFD; zaX-tHW!xV|AGtLeO#1jsuEdqJ{cc1l&PJo#zBsrR!SU`k!V7t?w`zH(c3%|0Xvq4Jf0Id;mM*99BB=dmNx>NMOu>UIuOn z)cX|l#)K=Ny0PORc0(aRrrdQ^w~=yd)9Mxp4z}&z%iW+#S3;;eAqZ_-kahx~9s_JD zc|WcYHrqnAv$lU%A_CMXp8iEX5z9S0fAj9-TLD`-zxeUw<;%Ax?7v0%V*R4_u_c-9 zCY{#uTFPhyL_{_xDDt^d=gQKqhTe|n2 zpProm@b2`i;>YXW+QEPR$N%V^;2|xwCrL8fG&fUyd7io4FPRjwSr%kt}eA#Y({$6u-+zh!HnGZo*W`dT}@ z%Rj4L(aO@$eL~IXovAZ?>89rV9Y*^6RYG;FUXU(IxkjlzAqMYd3>pQda*7krK-oG< zNdr9o?z^*B<8eC7mh$ra&6(u!sV|pO!x(gId)eZ*(fmCE+k1`%wQO?Fl)4uqej-A1 zc$N%Q?%zK|*Ej#vJ5A@wHZN>>+op6or2q~;T}hm#Uko>1%SPp^oR0FI#f)rKz#8vr zsf0D(f3#H%a+2}?7!XVymgH=6)qZ) zds*5;ug9b3gu!0RoUQz@>FjUx@XAvfP4>zreIc84v$bBkvb_@vxa8PL1L{*?d-KiA z24>I;5+5K!@Zvd86NAE37-z7KaduIx=NOqo!Jc3~cASb@ zxUro!$eA=fT7JVW{%QG^|MK@%@b^8Ln1A{E(g?3(%hlWe`{U1RbaStz)vbzaqbt^K zExdMX;kDZfuL&}&Vfij7B%}r<26YE86_V5B092F7nsc%S5Sqnz&WL2(51Cs%#ijV`V;&if0_oWc{e@wb|`Zi&%hSz8jH zJe9h(Nn5AI-b-n*?vK)WIXaXbO>axRl{cRXh+a>1hX0b|FK1=VU$wE@eal(X-8(+0 z-;@M&8chdOPY68VtxDRe-qn=-@Cw%Mfjk@qH6IkU7Vk+LDdO-bf!UA*EoqqU<QuzZJQsiQw+0g|}lO->r3WBBh zqr7f=vQXtQ>aJZqn}K?G)zhyoy%}7W5R7UNw+`NUbXmDdB|?xkfo6y=@G=?=eo-&k zoK6&xsDc?Z8P6}TT0HKo$O2M{L6SD7I;^tE)W^nm{D2mws;0^7xGpDrh-NpJcWuZ@ z>F$?2(`j;AgZ?gyhO?`tT9qwVw)Is`u8s!jadf06{77vHE&P>hcQs}(s4JIRZe1aD z`o=D&qSMBysM^`=W@-wZZr)lhtfts$6MEy6C4;sAL8JEn_KwLazDGCA41L6c^Y9ki(Jd@g_9H?{vH6o() zXkInc972nRZQZT#n04>;--z%u?mSjU;juajkJTRQ3veY^RD;BZ_L6$#FcM&WDtw4| zSVFC8x@ImJP7b(V_>|4Sh`~C?8*UjyJyuz$Jj#L`r<2iOZ?B24)z0HRa^cyoyr)@z zG)qxj`h_;g|4|1UO%^Krwky^u7)&Y{f-QMSx^OZAmZ zRp1NDRAH2F`L|NYNL57B0k^yCa)IPsuX8x*-HbniWHTQwvX-mkB&y+}`IV=VrK~@z z3SpfBd!;lYzMoA7$#i3PV#%~VLZ7SktFQgT=|BU0OJe$XE;;vfAQ_J5QlmVPHm0cC zKhMd;P5YH>+5hkV_}~Ad_kDJ|C_Y%rJ(fTJeLg_bK2?LpC||DBOdRlys)`mXa^+2R z>^!5(nqMPE?m<&AXsLP_G@)8n?$auxsj3Mdc$1Ywb9Si6=rPwpP-FVKW=s`x7 z+VNyC%_;-A9PBcEm~CK+KeqA~l0-VmuA~;6B;@n8;*W!aKNh+vK>#%qU*X6e4KOFj zzNgB)CrRu~+4T1MhW;0mhg);X$cguVN(--+llzr^2vraB%hEj2L$U}cU==3vdio_~ zrOb!=W}5mo8&M%Y)B{gzGSKC-D{^y$`&QP4RKpMGD5`hmt-*I69-R+}$$TT-c((Gu z;`LUC;UxVa#TEo`$U3>kXUj0DvXE-`#D4rRNX3Yw{h72+_yS{UrlTpUFPVPzm6ue> z=1R8Xae8~mI}C3MaVNNeH7KPX>B`g&uFDMbfw9*P&`^LUlZNpy3i#~m-Zm@`GV zO}|aCh6U=sM~TQpx}8Zqh?2^|Y+>m$Xd`9olA$6&h3R2odNZPztiWltRHbS=E78%9 zN#wU;1}2Qe+en+%zl@QqxF+`HhVix7J6t3$bvdcOD)RP&U>K}!J31+;zUgpn^R6Qp zUdd!|qbIxEbhzbI20)EZ_DR{b+WgjNVuewxCZAlVah5MIN~AAzT~FnBBE<>1UuuNajUTh&@aX+${IRm(&c@@R z^kZ?TNTE!Mh%FnZSck3dR#h`xvm z%DciBR6T$wck0b=%H5K7ONWs@1&Tvvb`{BP&xb$EdgDZ`{8UXP8x8eh33wb*>&0@+ zCuj%LuMSqWY2;W&R$_t7<3x$tTgsNk;wLQLgv2ch#d=>*buw1aB~#h*0YfRON3vPc z|C0OOaq`HBmggWUfmFbiXTvjf?v;&B^u^Pac(D7TX`<7si55Q1}9ukRh(Z- zy_R}@>9U+nc)6^yWyX4=M_$X$)PJgOP+yi?b1@suTd|%BnFFaVE8a*Sc3S*6vdWWM zqouBl@c;V%pwXxTMiB}*lB>gO_Iuy*eluBnrA?wld;D?P1Kp~c(S^W^hN$YaR*-k( zlErp$VD+VTviHNyV)qrfU2KOxi++1lG+wBoss-&=AM1)R*<1CV169x9<}2m1&#uQm z4b&1^h@GnC6un1f!W_|!DLz=G_pq!PEUif#*nK??rAqyk9KaMMW=Ena5A~P&MT%{u zG$8m&y#8r6!_liGr=lgG;41Ov=yCco;wy0NbJ0pmo}r>p^y7f(oKjM(lrYtRY@U}C zwXuPrgtDbzeyJ@~>z!O$#euIWcqk_aZhxUWpHD;>zv@XjZXw|^cLQC0sRjchMd!-Q z#-%b;C`U_O>cHz)rfhLueAO$D3AK5bWx5Ee2orKnA7vbim0Q)3H(8L(*SS{N7V@31 zDR_r6ON9{`Nx7x;uYa&#MoMXNs2rlnVSA_g>WxjADdNx}c5|t6lcyhm$vQ6HyL@eB zH*a#jx4gB<3*YMYHrXrf8rqh`qP-FI6qe~xu8zh(89zDXy*1<$TYY0^KCu(-cKsLa zY;?dmVS@v1nIl@44ZxeeC{sQZ~A za637Ogn1Tvf|#CDU1+U3<1ArQajmo+aBzbBDxfkqn*qx5#9yS#|RZ2F~E+80PFm6ZE5Mqn&e-q8?&*${OR zH5$58+$W_Uq++1d11V)AV(?7KM7rH{lOS)@TrXm7;g(Vv^U9N@T`>pR!H%VNjr)2! zP`ENG--;}u*=+$mtL*$eD)H`FSFhx){M-0yB>rt6Sc_>p{s`vE&McYr`&y;RYl2Z;wbp%syAo>(cSKmhh;8Q9*6*l2CR9kn`5jMZsk z+GiY4)=tGcxcjb^Qc9QSL6QR`n#7)%Gfo23BBYN|Kq|fo;315Pwh;J)hR2jbFIt-z zlt#bX#MrxFbt$I2mx*bF(lk3wjM-^o+N(J5Z7?mUEj}tvVp04Ed@I^bo{?k*v`r4` zvByeD?Z9N_LOZtrfAuZX1MenCtQx|7;CXjAEsji!^_kPMyNK}@0wfpU5U2rMoa&o? zspVA|7{=B9ZAy@q`hd-+x}3*fP(edznOFg}QOdC{ISA>On@;;$0>gApo+~cV&EXPt z2IF!SS1A>EAA3Zc7~f50koA}Bvq-(QgzD07FF@?#M;u))yr($t>YW&Wy1f*fv&-Q; z8BbR=iDg75AI=A95wt&)caeyo{5LAIx6=WA?GqwShVxY7b5eMT|8goWM~MXJ2(BW7 zm(YZchcCj6rIv$JHV5H8*Gi1|UKe>9y@P`u0hA&OSY;az#(Ipu$a%YLf+jPXeJO8? z%*e&hbYxnC9w@(S0fW!$6T0K0y6WuDIvi63Vg*~UC|JzWW8ylK+Z%}}{4%)FnbLC8 zNvr_i)GMkttHwg4g}2{4FY>A(1(4K$?M?pny`{79FUQ4dXyG7Txw@54v~ovhH)!=< zHqqml*a-!h*DU93a;yos_^p4PZt{no!v}yY|5s$XuEfpMPk)k?cO)xsM_u0yAfJ(f zV>s4-UT4R;32A`_mL+~A?7w;X!{pt~Pv4w;dwFvf{F~#=uBU(g@%#Al_0K0C{K=2c z{6>lZn<{;Chr%dZ#BP#uAka?eS7ji)Ik6oLc`?@q2fPs z6AJslvNAFh7uQX~%mNp7$2*z=ijz^v;$;x5 z;p&HP&9t+?NKz*XhHvce}Zy%RC* zZeaCwNVwU~DlXWV#{OC6*1O-BsioiZ$>MslxSlMoCyVPhWpNdIQ&+>QdRuFKf!#Y# z3I2P28U=!cnzDN&k!8Z-N>tSY=?t>qBn*UM%JVp=EicVXs=zYp!rWDTfovHcpXNQA zUvLMxk?RJwpWA+HrGyva*z=7vvpv&|yx)<&P#i*!$O@|np|hXtwztVBO0?i+%J8H| zK^}*$?|ZqQg2;y~P^g`FSu9zv0eJ#`O6ejIClKKs(qIJU*Q?8}i%NXYCfSqh{)R2H ziV#}+$!>c)&5r_~2&fxU=8Fr#a&zCK#5W9WEHn>_KiDe`D~z%B=uj^x>QP>K8%NRDHceDo~XPY+i)t)LJOr_jT4T-98%8n`{ z1rB#~%h#$RQ@)oZ89x-xC`H&DjovVVtVEwpg9jTBC*?s6U_W%&xf&86s}uM0HF#;?nv6WCC2n5av` z;9E8=1zZ$CF>4TE3H262aw63*YG8|*thP}wd`C;`vaH9}wsSFe#U0hM8Z~=jY^Fs4 z*i4mrm4#ONM(Mr`k}lUS0G$LPRxDo1Qm~_i&P+dKI@a}j%4Szmp{r4fH9$F)6xfy* zNIi`dppB$XN3l)m$_o){#prcCNUK6aRodiMoD%98(iN%G7AUJsq0%xkqv|WqZy@D6 z87U`8rHG^)!-1|YksI_JOSz<~nJbs9Dy5?qb)c3nKVwbn1hs-OZdGU(1vS|1ly!4; zIw%*@%E0%RSgjPjWx_xQqN-w^QUzDiFKT+#eMP;$E<+kjivrD9)zdIrH&RahOVR3q zUVvH<2C8s7`!th5TD*RuN8(N)V(DUNdDFi;=$^2*T*8|i7l-G1r{3tksP6ka z)AiKpb6h=rcI4@;-_RmUuYOZwj-I-(?q`sKCU%NH>63Dcmtk>1;Ye6_C4!+CE1;u+J+pBo(ufUfRD)WV9k+hA(BjmDX7p;5uD zD-9QN{T9u3_x$2b3nu%)xf&Uum#K zr>!Wd7jNIZeDUnBhhM)yr(OBs<%_fP)7KSp-ml-iIr-+<30m&6mv2y7&;H5o`;p2iWbgxsl-qYIqjJ4OUx?Pvs57 z8)8%D!$&e=VFJml+zAXXj)}0qLdh{|XPNJD5RyY;<2DJr2p^>Z6Q!NiPCkGI@$|S) ztM6&`-PP*rSTHCsu8k~zUPqPF>!@;iT~tojik#eoJ_sjiO8GU8eJ{+!Piol)FzZ&V zAUdPi%2=E*0p@;&b_%V4FATD)#~!6AXsAvnJjGoLPiQcW-`m1_T2=R^Pi|dUR>y^9 zbzE2-^hr!NlM-AqaeYiWB4ZFK+di4P#DSg2N*qeNoG5odMouXfchEI6NigGIiEVqe zbVu5<4ex1TJuR$zR2H``Ewkg&GCMA<4s~AvRb7rBQ6))LdSHbV;YIM5r>2+WUW_3} zfG=}$!_Q!q_kz&#sUA-=BTmV@2$I^FWdqD$Je^rjOY5FALA!ZXnH@Djv!fL_CEbt0IZOz0u}+`)D*D57@oaA%2& zsDzIC6y@NMvt^=bw(`TPMYj`)5 zY8chbt@TVGem1se0`*SH=hSn8imV((LthXM6t|$nOcyynDyl6YtP(=sm?~5qGyPUY zk%0nemPXRv7g7X}&I{mNfkf5`1PYU~fIdzRvxfWQ%kSm1dw2Ym1ovSiuUD2|#W~0W_?#o`^nQ4Q~$w&+~&I zftI+MCG5FMTe*&Y;x!ft7Yc}Ubu8AiS9EQJYO8#)12O^-4oO8(ShkYZq$)(l1+~E8 z0<3nW4?TUKpsUK8sYH;2lrbAk3v^O-c>#A`^g+^3J}y#B{vc5C0uOpKC-WjXEFqN- zs-i%OMf6^VccLIGb@GZN1r_NpQxcd2pj-y-avTkdWSjv+V;@HlB!H}}Qi!f}zKN81 zD4bN+FsKf>Rf!yg`=v0ZD1y=7Rdn$J(0D_y^Z+9iu@IYWXh;%i1aP|)V9;JQ>-3yVF!cu`{TqVBIl-`~%B6!vA`W=DeW(!)aq%_1A({BrQ?#flZ_5YQ<*DTWrov8-b3T4!ff6|+O zsCNGLwKB=&;3yu9WNgX$`!C6L`Y#vLqfZIb`d&@im0Cv^I_v0yW9t0$ZTQ`J82sgX z|KCs6b71U~pS@3`oAlG!*S9~+zW;XeZ?Dr?W=>!I@{@gi^39LYFI#e7y#6lSE7J?x zq`s(z8hkoUptAcMqPTFH&}R2-uC>aS5+_92vp_z`9fyQ)Gk0?8%`K1v^F$bsvoN-k zD3weaDdUY(-@`v-Q;uVL)+$01l|1B=2Y2cZDl~?Q%9rH6pyWc+9Po!_L6j%C>Y3*N z^+hPOZ7TQ{J#Yu{Y(rM~XW`MVJ!GG$zb7w{z0C`>>8*G2DtrB)acKp2yR?G4U0Oj0 zjIG?lAeVZ07c5*r8(2sd{L28yhd`ml0( zdTG_-@=r_aF)S^=acTK?yR`hfU0Qzo;=A1UrRpb3OxRLc804SDsR{6eN6Z4OxXgBs$1mbcPz|Q+DCPP_WPfP3fytH-~Ol6&NGAH=eDYRg5nGAadoXoOpFE<`G~I!_9-s8&pMbE9o&%_Mdx991_9e#GHT;r#%R zFw^$lESkO-GQI);qhi|D{V7ttbF*5|qx+!BIZFCQRoIhLI&`S&hI&QaP_*b*G}3)j zU6S4@KS#&DsY;@oWueiHCc@ISR|(I?>>n&KFJK7yRu0&d zq-s-jKf>t`I1)^8uRnN+iWtE^MWZ9V!qe=yEQ32P_M79i^}f^j@RMrnc=>SE#bOz? zbU$ouqZxXd={%Jin`zpu-9;rv8@t+SPOLSy#6q_0q!*^KB`C7}#t!IK5p)(g;oJcD zVsv%FB|ucUu@4-eVJ9dZY2so{`&JTjw3~qP^DEg*X=udODlLFx2*BW4lsDX_bojd~ z{%g?@J5PWqgcX1+dx(@V>MeSi5I2Nj-pt|+N=I?h?*XxY*g3p!HipH*J>?+WQw%!r z_E6ctY&;5O6(+lCn<1tBWN4p9ZA9Aj&ZwB-j2`W?vQ@x z^BF-1Q~F%f%%_6$o)$6vqIx|D?yc#Sg)?1R|!+4Vp&&d z>ua?_h#wymxA|~M|5;_*=oK978~8seysk0Fw(Ol8g!2!I9w?F|1A7IJR zr=nRE@@;*4`I?ICZ=uJcMpU5zDqPcxHxjj<{z3SyOT|UaSCOY{1!q<;LslMF4hn_o zd7dfC=;3X_v0J`Jol%id`S^u(M!0Js(nqkI6jhk-z$1GC>rPnN?uOOP0^8aP?suI0~5Kz_g!goyM1_MH# zpqe-vjmN)a8~8vK$X%s1>Bpk;X<@!)Fgnu=T#)+KBbaO?WUdqMwH=yvG8oAkWG` z0_YV;1r_)Ud8tK1%ljmT%k$N^6-X2+q^~AP##;^Mjbd`uof0lm$Qvft>Qq^sg96oH z1sXzyTnp~dtjq{ttqJmj1nEH_#B`QvMb0%!Ng?HD3cgXpBWkG@zz{9vitv*@h$I4q z!M42G6CHuB%5#Oyxj9pW#L!y4{ju6t*3-Bj5M!%VOkU2hwC2un`_nmry9Aj zw(u9bm5S91%%fl}YJO@V)zuw5dXH(Vg4B~m;=u+w_gqKLnMU=*ub1@5Bqs<-@Y>S_ zd_*4oZ)a~_t0$`)D|LivN#Ss=3|0~e)|sB)HRs1)Yx zCt;7jk*%24za7lJo5x@EHefeA*C)By+r^1grV1$rT(-!M%wwXqf?|!9tz3;~H{)49 z8mxI1UcETq>{RILN!W(!xiu|e%Nw?Ww5Z?IX!}1O(r4E1^KACZ()`~L^N;7ZXlDsx zlV$1=g$}ih;IH`%oW8RYiK10BUboaxkGsuYtm}D{*KRWtH(lNy(NgHAV%z{e43|kB zm@aHbz<6YYDMAW$T^qA8@uGw@9LJ2|7NB|w!dH}W59-TRL`w>BNwZu>Y7agOT1{)M z6~jPs(_sK2LJ}EnoH)W77zQB6!#gW=Rozl3+k>cmUS?%8{8~faW!0Fe&v9!DX7k;v zLAO=WJ`!#laRUCOM;SUynnsWU{D6QF@=kL@0^%6 zdu$romWOz`neRm8(DQGq@`SUM3J&PjvJhIDETUe6H2fTQBfK8q&6*_NKgg;utfy7+ zyId8!S#8nwQAKe`Or&4ett zZ|YvHQ}w+{e3z7HGM{^8=fnoo-)ckiDjMeM0^TRWl$nhc03=A2$^yZ=A(pfxZx7Oe z0R00#DF(83ut1nA2FV|L-}A>;z0>%x_cj0j{IK`zu=nj@@0-KkiGs~s-1b!u@0qX} zr@BQ9^uwjkM?5Tvn5@i#z-E03lLl?&ioUV<@t@PioO zXXDY}!}wFtr(22zGT9{xAWiu)!b>$4>-A<15b7Su+PvUEL5Ci`nhQJeTLpqjm(KAR zH-%UiH?=?kT#?4(a-`Z3_inzol?~PK*cNHryBon^S0xIYlC{77|Ji%DEjgAmJ?z^3 z6v^Sz?7e5Is`Grn42QeXXaG$DjV8LWF`S!Td5G+;jXLehDgccu%7?Wy_E=+y;~6b^ zytpiD$)_uM$<|nwulOc>0r3(1{eMIrDl4-ptI^opBx@v+jmlGGL}bK&`2O#s81Q4h zpgkeGRp8e!I=NP@`o4w{Y>+E$ULa6%7Hm*0ZNI3yIVp5FSB_6O^46lEe;qC4%=#deduC6DE10F}P zHM&T&x~5~3Lal3~iW-3RG{UHwAg}_NKA5e1Q^#BY4lpol!|QlR!dtt5`?d@(*sg=V z;eUsQw-xVccy}3I?=HjJ-R$-C1o`pQhgX2X?{3|dMlv7H7Max^<)mx za)3Dv-{OA=iPv;%l@SEA?UigHqR`jNIkF3=hde0ip(t?vlO@WL5F$^9?b^?G7V)V1M(mlS7ZX9~JOINxY?wGCxP}R+5tIs(2(+pI zqIgt`59K64%~Bj)MYzKx!Y;Y4FJphCMn^3;5Tgn|@f{n;7+08(c1lAOm!Vl$!(+>L z8iE5GyX!ZID`}WNZ6GE4PV>jU)BNc!PXzv6&y2uAC;&f1bOr+gMyC*DPoQiFP=N+TUIRSp=^ zlho90H$nCaOtJweE?tNPStbOteULZ@x(_HBj5hoP32o5OhH2<|iMv`C3R-9b_MUa$ z^T)J*SLe@WI}kXJF@WZT%IDTj7E@{cH5^zIQze_vYqRov7$S9+J766LH)Y|-_WF9h9 zh_50kpLINmJk;6HdxSgo&TvidHLTqsslqnedvxB;mR>$94vSIjVAj6l|9Pk=>Q7BSi1wzA5kQoAUS5Ls9pVl-h5Fv4Pqg&bvCAaGmf)>gap!Np* z0(c@V-3NChs+uE&Y$DrE+_+(hZ7xP^DIaX;(z~w0n~k~F?bvLsL1`XteW%Hx??CGg zbT+UyC=!R&50Wip+>i|i&lTkPL~8j0;W0~quq%q3F!UXSNZ=({E+$Qi;?lLrVFk{8 zPmany@>@GOHt)2<=|HV$*BOddCmi&GXmv=sWOC2lcuFl><`Jg6efPCEv>3GQzL)Bj z^?Vt2G~W2IQT3Hb9dATONuT7}>WbtW8$9r=HAV7Y|KeZ#l~!!M>H*cqmYXmda4dm_ z1ki@_pUcHD>k|t5-Vsii7VnJlfMZpO2xlHu|LR$8KmRke_*-fZK z@qjNXerDDxmgbul%4`xto?l0&U!!P|WV<9VFGi=%L#UK*Lq4LH32Tq$b0(3ojsULT z%AnK=&6lFYs(>I7hdMxa{+CFL&D4XzIYz2}ameS=(fgMi zM0jvaWLX=HFON^_{IrS*n7`U#`OxokVZsnpQ1oS#&*cd>oFG6vR$y~hwp%^BY(i^_ zdfw+!gk~H2iG%}Zy{%Ez{LGy#?S@zGWNbHFx1+t?bcbj(jSin;lP#&i2IHhD;rwJy zZ>e3#d$i}Ay@9pGKKDB5%6YB0MXxO)vT@_O^4dZo+b-*Dk_k{biv%hjDE@@{0HO>` zlxmZv6_`#fB%bv&6uyDT2_!&(>++3AcZC7|T{EoN>n~4yoq!s^3|?qe)<6?tigA8 zz}tK*PruuM>vtP)eTS$gIC>JOo+Bx3M2Y1%B1OL11Gi9Ygs9*KVOJRYV7>!E$~*QAF{Ni6HiD`TxSrj+`U zN$5``lm6qlbEICzv?``LSTE{qsZdS6!v~d)Wyb-rQk{4T@KiLEs9Y+@kk?>sPUYh5E#}oVb1}+&;clfWQ^NhnLEaN32vzSWSb%t4td9htDkCXppII=^* zdZSMl$FI}r`2+s(OxgA1qkb`KQX$%{BW5s3A9Nu(l%Jx^AXn0hXAkF93ms6uh zr(`{p8x&$9N~^%un%NvY;vAY7bB~8hx$1zbTAO|VwmXT`+IF^j5Ho>))O+ON9VOxt$9DN+ z!2|LP_q4W}cyXG&UeG-0&EA^+c8rFzS9Z6t-d39F+6;!<@&P1Eq5g9dmUNvS8T-n4 z)k;jQmu$YO=}W`$;mAKVtnpVF#5uyvW68s?ko&k&f+;V5g;BD z%#ONQ3TA1m+QZC#EVe3tL*I+^AsHZ1TTg=W^`Ruep$`HuVBX`X8e4eF6y-W)aoPYe->da>;-@eD|UnT%w%mwp@HFz z$hZ{dT+5HBPq6^>7WN!eEt*rvQ^I^c4X;OZY>sB7)P(Sz&_51sndUmbVgls@U9A;R zw?eTp7C_ob;H?>-RR(4)gzD+SJu*2atT0Wl743tJGZVkBy%Z>!`{6tey?v)sh|^HJ zrg<1$G0VFuXfc7xz|#=LAOq1jA4yE1D!>Xl+mOEPX{K|Z`2quhMrhcSLJ{U8Ba<0Y zg25_^>f}+V8SuRF$ba?hdrO>*cYBft8>1X5icDUb%q~>L`LZed>Hm*8jq&FKl z1pERJPL(P_!dCLJAfL@GO?gaAReVKi(-OI{2CLh6G6Jx2rraouvnG=3TjkSRS%`UV zxhJ5yCe_I$jz;~T%pS?)3IeP4ji}nW z7{t6;gv1ZAwTP^lOiVM%q8H#PGO?A6q_Rs2o^&fwisw``yGR1i5*GIz+zcwn4-_?q zZowj*ge#7TlzC+aK`?+R4gQ06UeKIFshn{_K$eLXf;`Z=Kr1E;bPKhBrsxde1%p@Q zQ<)~DQO!F_zQ)ZY=ukuBW?Yb=J;a9scmLNYMI7tEIEYzb?-)3Di--^nT#-MgnZJca zwbuN-qXzoP#njPfxd5mB^DrE+?i}*DQ=@N)rmi8Y{&xHQfmWNN|MxBvS0`}aWFr)V z#*lAoKCvFsBA4jzq=7~w0sb5;ae=xf75lRaGg@-7&e1r(oVIFh3PBLA`7lnEIQC)6 z9EHCWF0+U&|LY^Ev6>liR9XEDtocZ~FIlhXOv)^2D{P67lP@Z_ei0cYcg;=#Uj=8fK#R+25f}WDZT-JT|C5DW-ogc zh6+zGW^Er6tM*tMSWU3L1BEFw!^F*3@LR4imZ64^@*DnZ=#H<6AL4b^#gCqN4h{%4}sUB zAEZ8nX?!qObNt8cSt;k$6t(B`*spr`>$@0nq1x@cuB?FDS)=#vZckz{oNMiD`E$6$ z>KZo8lGU9>2QGgSw1z6S2KOMc3gd~%G-!R==JjDV$%IN>fiGcxCdYXzp>E<)_ zBzPA+qsY)f3qE+?%ByKRN{i>O!}#awSB|ae_qFJ!cDFw4XI)Q$o^28r1h&49$nr*u zbZ1b5L4=5-PH?#lE0F+xSb|`UWp*j>+#}VIE(!`acW%s_pb1brT3G}hX-|zv&sN-a zreGoX*0+}MSSQbQnKI1?MUajLk$+G^N-zx-wu z>OPDsWdW1a8s!p|#UVLRWsw~w79J{?NC0^KGuUZCgE%IHf{Tz5p%`p54-_3P+&@F# zkAAHn^_^LRW{;j9UC7DaOM`2LC+o8C|A=652?OfKMuNn!(8}Iime`>Mp#5;O0X@tX zK>&BqOhyP$H?Trof?>!2b?Ij8fFAPH$AqAeqpZ)rLCxz-s;LM5od}<$&Z3vS>ZFtej=%5mMX@1jJXJBc2UX-T&%^tVX~ zLwkZ=?WAD19x21JrXl9 z(s5~&v~?80(}zJRe}C#KHs$tg2ZyzTSz+@sF2(FZd_rW6v7-t zLj!S-SW{v!C!xjhNdY}>WAyw9lMeQfKm}b!=3jd@ToXg9Mt?PG-|%>oo2#@MgY-j^ z5doo zpM&j+6%9oGO+J?Yfh^9USI!S;oqhnL{0+HNXf?(Oz#D8DYA>H-57sOC8pX0cIFs3R zeGMFATgeavDi7a~KwRP=W*56??}*b82DwlqnGbr2|1QF)o6YSOWc+!b$1=KR@QNw; zqJPghrHGS@tCh<;42!y8(9hxL_E4NhsY0Blp#1FL+TikN<2Rm~ajNp#01z@$u50?@ zlFSBG>xa|E-AdF#K4ao6uO3NwrV;=jq=fdR5hLOSl84aB!j-pDQy2=l4g6hM5ZaL# zm*VtU{G!X!-kpiw;F@mZmye!dsq_k*hek0h7}$ln<_r%?{_`nh7?eoMXbwKcCT<5N z<`jSF9sypM!#VIKv^(}9!UObr)XG$31wz&v$7013iR`~}1%KcPp!eTqDkrDXMqJGn z?Jk9GnwbwbsawAw-C5D?GY@Bx&nLJ~)!};slO$JU#ajoWjwWtB5pIs0?kRI<4b{KP zqu!*5nTwxu8tPGY17jG!t9lWO#hH+9Mk{5OcSrvqI)VdkPA^1&GUfckZQ7l~SJRyn z^hY)Azj#V9+O)Thq(^@%Q9?-)Nm@#?D@QWXQ8D)T(>&fvF08Z{us5M2Caj4=ZbGJ^ zFGFE>+6>|bn{c?>xF#96&kT#<@(0vjIQ?An6i2^GGBso_>(3irx&zdiVv5*5+S z38$gRAhztYNe3E@KW>G4+W}VS4W?L! z3Tnl{4ZPnnfOIs?Jxp*6Cn`d?K8+n!uyf+?N@3lofh7z7C2t5u;nZQ zG^T0eWz}a1gEfq*Os5h`%?@NVx=)VV7on1d!WwYNA3$;S^L?F)g=!CMUa3i;Vn#P1 zY>q#_acr299WniL8;3yKn*a9+6Ok? zNYj0eAcOSnvkT^6`!fxSH z_8GESof1hc^-!{ZfqR@^gV^rzd)nam^+pjIdZO5{JDYHwnTJ_rUvZS` zP%~Ur``OAk>iO(+c{BM^YFUjt??_SQKu6?~ri<;xT4j+*HycG#U0G|ipE57GDTVZ` zV_~}R$)u*K?fOFrJVkN^7W41G2FfI4FmIkoC>nO+k-&ub9SW6Vc`0z@e{DqH6!Lqm zySDra!+^x9qnMiJb505a%F16yLv~lM@#jzsxUFi9cL*vMXIN2_@B7NFDjgO#$gE>+ zgWCgk(7SqB7ulTV#;QN$D8W}B<>=Ex4uxGazLw_B4%)3-McW~4*@%x(5_Lt!x7Up! zuNb3sv1axdb$R?hkH>M-Skg)nW1E* z+FxrJ8sAPup$#EmGK-)(xf7m50!~G*dmt$@Y|Co1F5nTYMG}TSmLA#;1e#Fd znS%1x0fbTcmQb|Fe+Ilow#d(XkMpt zdg3p-OC~AEB9*s%n1qk;UpVXyNmGVQKv7Qx!RO2y*dfd*A3~zXmy(kOYm6fxiB>^oXCcRMblnDPUE?O4?VuzU zX+|{WJNrwEb}OF38I5#ClhBGaCTwft=3Uyl=wWC{7lqWIrdaxGyfOM+WJN6Hg7}D4 zl97Ws6V;AGef^-BHu?J>n;>qbT#v~-v(O^d%_wpG9A6wWEQJt!DaTQZom9PeoBzsf z{o^Zv(TmK}|9L+raqTV6Y?kyKnj1Bj1#&y!PYoP0rardvMN0Owpg7B8`iG^K<%b(< zq=utyk9>ezi5y-fOZXrdLbpcixnVMX%&3AdN;{%S4=?NYsxpjuvYO5)n(otZy*k+* zX?HX@RZgu^P@?4A*?vBXizeuAAjfm~XOqmskUz_`Jp;4EB;}8y7u)okQl$blu#HZp z=*v&Gr0|~MY(=qyoHOFrwD?;jIbEKJ?Fk3RCvuQ8%zqE`1l5gi2k4M1emjSXrZB1Zqo#i;a`8 zOgF5L6OBM!50PYV2bF%tN9fttMthEJ-%z-JSbf4pBpzNyf`24vG$#PK9u{F~=xdF! z9~vAa98ln1zpxo!4}(+@MjT{XYqTsxVW|B!E4TGF<4UOkUmZK)NCMmf%7zF406z^v zF6a#r0$h#1uDnH`U4u;D&dwgrE)L4fN6F3#I|DR~23Eq(JC7?kuZzj#UNx;ax6RKH z+{N_cg=xb3fWDbP@6dz?i01CofW6~Rh1!54|2f1J=pVP|b4oL%DB`1xl%hfAmYceQ zI(lw>W|trYO+O&-AtLYIYoqUz?w|65BiC_@?V2^+YY4 z)OXeyS`!BFQklqY$dw~Gt%*(*Y~tkG>skAt-fZ|OZMiNJ3G@JCfmM!4=&U1%{9fJv z8-ceZ-|{a@mdt8)_*rUB1OrY%lm>eq4>C2t&KPkr%OMPU@^I%9syW9+qo$M;=xZcJ z0X~k3*d{8}QEWNd_;8luQg4egIbZ>Y*vEV2n%OT< zNYk|HE!92J;n>>uWjuAK^m%*ZZ0(zN=2Ing(0ZxWvSV3P=aV6T;!Iyop z97SzJBZo%{W3BYTNlejI|5@6Py6dYw}W z(E-)883`!T;NY+Nt0$;yO+^;Zze~HVi`5~n9_yg)Dx}uGOIMw6E^dvn+_ZtsX4vKX zp+gKy|A8-_%u0{*B{%*`@p3?nXGqY8#W`LxLV?l4DD#EJCTJ5_NpJ+^gl?>HF4bQM zuABN$gnCxdp@Q;Z&Kzs_KUFjZTtHg2$SWkB=`{N|tj&F<&RczE$F0oUm!&vhiajd2 zwgZOBM80&&()g~=LtJA)2>?s@pb7uNFI6Vsr^N^AW{PF@+Y7*|D^eZ$V*Lse6;w#| z7@8!8GI>iq^#U2Dz`}#fiu8XjBk~Wxz`RcP;6y$Qg)3?NitlTh$`;?qqwG^f@sl~4 zI5A^^!Ty`h7$L1soUDQC&W{%m1@rmK;71fmAb&ytIPMm=wt}QT$U;K?Eq4RAAsO6` zBq?+MIRg#6>SMIb)At{=5&8u>T+ZmjC{0+HoM$F;A4?1`yqDK7bC*^6&HrPfLOO+n zP^>OChvUxc%t!Qhr1z^=ks7f3gaFu&2~FnGETzOEPy|(U`ZA2*4n;z9;-C(sFzf$*6bij?WT zYF*t4&WkaAhc20JR_Ne{;aSHZs=}yYVY&QhAFvC23JAdM18CacW5xfVu7V;U=tD4p zICfv~3@Dl^;TjA8-$$Q=9O-9z2B2fD@xtqHc|Mb8bKd-K4_dsVYjEA1{RuC4`Q+}~ zS_fh@_t7d+lw|AGr5mnGzqc#zvTZ0%A0Vm5UW?_QjPz+CN|Ar1W?3dWM&1qlh!KXn z6^xZQ|9EBj)BlHZe>zzdK-vl+0osR1ZVD4mtdm4jF4isf$% zqedtZ#Rt2I2OG>b!UNc913nfDfGnFtt!E@A6_Udw))YAFc_?T*mnLeSi*${L7Ty9x zsxkW2q6(!y!X^dd3RXH9mBt@gO7+J`fFU@~A7(E&3q$QxyU%Cq62M^%MGI&?pzOe# zlU+)RoH7Ow-ZNB^l6BxR2&W;3r;9;TX7$GF>PaykM3;$XFlQ$zTxbVM%u)FhIHWls ze9|#Lq5N?;L?py2cXt5I86HXOQzmmk$*Q{) zEN!~`6m3**yu>ZHKbOaRvG&sTe2F$eSZDXMcfgLeOSpRb>KvLK+BiI0ORuFg_7Ca{ zwDPcp0@rv*AcE9mVp3V^VTmq=xc6Y3d6s{QhK1#KgM$(R3y7Z;7V-kcU}(%Dt?Q>u z2N-a`EUhxj-bh%5tgcm)tkDHxw)tE|?WsfgFS6+3QSx<(n{a8nX6<-}eYX^o<5`Ej z8(lN%cW6`eNGpw!YbXWR(?Xh{8=yc!=NX0ws%F-spyXMo5f&W>)Dubt6v*WB=LG^I z+zZvQ>)SBP-?;T?xUhxne!nlE>pM>2okX<{dY|h-W)!D0g~JUVI><`Pm!G2W#R7C1 zzEDSq+mySYutfdoX;;BDgDp$N69fHOmWWQD&rhQ?>w>;=G8uH6dVq?u0iLntaaBtC z%ez@ttaQf4F{L+6;N?fQtCuWjI{+8)K}+E!%V?NOU|EQQvaZ61|2#6B+O1^sXBDh6 z+Dlwk)xSGy9-M#p(+(9MMuQ3`8hNKo-bGjWj1&s;E6J>4navEgjou<=dnswG5GG!F`n*R_JzHy{Cy9t51G!ESbqzV|7tg3y$>2j4lVkJg4;Id;DT5ob1RlcKqkp!A3s%yx}MDbe~B9_L+0TQ#mLw*|J@8bW{`Z?DLqM!+sWE_ zheXGD-yR(qD@Ee1&6HVw&h^72xj zcJdE8%ut-h84#MxZ7ZBsi@RTsC+vo}cFjuoR{<|5!w?xYspdwN40fa)>H~X|NQr2H zDglx9)Wdsp16Y0)exJL?ix%B9<$=keg7BxHY8~;c#m8V{>u<-#;(KEHTThYt%iPLe zGR?rGXtll+vQY)dtfw1Fi5Fw$=zukprHqH-_qwdCH)lyu;gD@Ihum*1!KmZRan_tO zw{eg0N!`R*FIYX$xL61Kf*Z37BEaf z;Qve>W(N;Ww~}V{6;mmD%Is2Mp0MALUw;CJ>C zzMA@DJmrq9g-J(keI-GZAB><%w0C2lFmkipzf!IZ4VCoD6@+qI`j@jA~T| zLx9{c&b8H721yms*8EEXW4j=8yobgA>@mWoMu|BPnRE;bM`{R-SW1sS_n+}1M&(hd zhmhtYYl>S_B+Vfv=<49()8_7J((|uylz|4O=&4-dA$ej=6+4Okl-G7rK88CCX2yLl zb^R4P>^LLZl--X}1tw*+>`)%`BHH$r;6_?uJWaxBl1}l zBZg*ir+itRr`72$AiaKJh?Qjb51$BT&+&#LEy!N7?jmX`6)s6=N0>tqYIA=dg$N42 zD?CR%5Ol0M8D79J2gu|))!Uv8181FOAWns)h`f?K9@OLc9#*V~!&nDlWCP!q`Vzp~ z9VT$H3L~7Fm*?Yr`Erp(!Ag&HKYI1^p%ZNsM(Z%Qw0Tga!8QHQ_F=AMHMisxwn7v~ zFc9~|L(a){8LSe2&cZs1Pv<<6l>gEe(% z@2m27n>4(ZkLMYWOvw$Kg@J){KyN)L;T0jbu!p)u2~h5SXetWS?6Xkd{!rhtrWzEQ z;v7e$iW?lp-&X|=HIc$x+qKZ%O_dFpZ1{@<_Fgn)%d~uoG5b4L6;z(Ec~PayxfmRx zE(c`@!OdCCBDnmx*)4wxAfta`!pU~fRRK5c*zkPjX8Gwv;LZpj=218*HHCKAdh;sE zS^=oOhFQ{~XzB(K(G#0rD?mPOLi-|8F#2>&u^OFG)8T*$>-@GBeV^rjJmf!*clQN;e_$;) zqX4Vu7*A>nI4FsL3RsT`HM>6yUL1wif6I(VjP0XCr%=_LQoB5ns0G6PJ}b_y_k1C5j2De z9CV>nlNDJ^BoPq)QHFMtI0s?E;HnATAxib;tfZ%BPN1`chj*;+jMPtIPANV@#ws!p zzJDv;Gw_a_B&`l8U6e!@rCO526!l@$wBe)F)gC&&QW%?uy_CXD;I|4((2gM6`G~c@ z%O#JLM$|=0$tmr}RIj5F(S(@2O5*QQbbvU$m2*~AeQSb(B#uqS&S9Wr9qF@Fb7+c{ zBHc1rPy!<#LP;D&7^S69S(MpC!qcOrOE&a=K)2?zt>JK^_mbmLA%-U?4`aLug_`Bb z>y9azd{9fVOQ^6yaItX2wsEM$=)2g|Fqzav3)?@O`@ak9 z99i2-7AM^?yOr6KV@wxZI+|=30MetxlcWA6$;$_?$f$O>rTl~+vI}bmW5oEpvvGGF zWEq>VYZ)6|7Us0<75&sAFmbG=eU9@?_iDlm{D`O3s@GPH{qBTPk16473YT+2cz-Rh zMK+V91#7ROol{8pg~FYOI#KMa<2YyC9aOd*N5R1hl1WaKVZ;Z?mM;u56;}5)4S!@h z{)U(XHMtB-B%|h@jGu zv?WIt;Y!Jk1i`z8T1m8Ly~2d-nwBbAsOm|VvKn%o1WQF9y()5%$TKRUCh_3pp_=ch zIm)2gL4w1%tgDO!X{P@fk#D2PkSmdQrf!w#5x(o|AC0=%VJY*&CrvC`GZ(1?+L|^X zCUEr!nFu&v>?`jr$=HBKTJyf2>FZ0~v>AZp52^W-oMK3+*#trv(yc9*t}SnJ65o*s zK&_6j?9O;uBpO|#PHV2P$k-GNF@uDsYlNq^urn&LXuYkQl7&OVsk_vJ!geRj=OCGE zfa8ei{}-kKrC>C7$kL_in_g3KrZ8N}c7wHq}|DC5Wb5w)=Kae?iQGIg40-g*_>Ps#3rMm}W4T zO@ZBB5r9HATpK3QfzmHVQ6nT-B>2K@Gt@@HXP5=3bXfJ3=NV)fy8Sm?zk>W48 zsA|}W$VtfFLsJZE_BXifgxl&POuy6;yo=KlVU1R>^CLxzKHoHqO9DwLy-_aj6VFFUwr?cN6ioLRl+ybviip#viha_s z`eZz>YRb@0LQy`hA)bs*^EW4VfLIdCY(k%gG$ER<{}x=B$k0tI0zS>e#}L3)l~WMHCppE>O3|N?>vOp1&NJtvffDal z#bSkKWKZwU4E0{fM2A~j1B5FOf0NFpmS8M^{Q$ic3PA|eniabxB~gT8g#c4KYJAzT zy@bjNvYOa&obe z0%N+^2awLkEig*zo=h|62r1xoFZzACe*P{Z6C`nw$1p=57t6rVBE9x7*lDrxfcW*J zZeEuw!0X|m_$?)e@;*3;5htHl;Wy`=Qnz9>cX*1yK!7IuK3;|LB)C&|ipgR(KP=Bc zFQ2QA$H(L0QQ`Q-M+u|D$Ln+db@Xt2&h!hLgg?-8%D@Ekuv=$g6RV$_ovk}Rws%@5 zN6=HelIlBjnO2s*Dpoe`SNoj#30XHofOlWR1p1Ja zaa(qN)LhYY9jI;|leR^BFtT_Yu9CImT59!jp_@juw8P$V`gV@wsXx6CyUNOH^(kgW zS*Z?uN}c*BYp=|19XTaDI?|N8F$d-@Bvz$bvg8c}Qe~~^H-b?1=ydeHI8Jxm#{9+9 z7J#P4cfButrbR%oKS-sYL?3VVy>b2~dEV$Nm51dc|4`^^UsFZXy^o$!CLqAwJ7(W} z9yyP!w&x$ujdwqGq>8+s6kwWG0yw~rw}2la6%QM zB<^T{vkgeTItaYkw!h3`rC2E_{z)whuuqei*%e(ww^&Q-*bDml4)Lb8iC5HB>l*PS z3v;hW)>KcCtl927`DJb^M1<`g5u$lVt}I-%uaS`Wh}cdhR3j+kPrwv92ph!Pw*k<4 z&;FaiN+TYJ99=#AYN1IFqS;Be?0?9m|B!$?!(Zf6Oqa1I z=`IpZHW+_IgjDwwEr#hGv|_C{{@u~iNWp8r3K>J;(d@?AqLiv#kU|@%uv0b&wZL`d zpVq)+aF&K7>g(=O(7m3l?ci=^8GAX|*vDQ^|Lf*bV;bderaOVJRwGBlV4b22UfV`b zTD`W58o%t=G$NGw8w8a87H@dr3g}FT+bS^>`wOx;)v73x9Jo5ln7@g8o`6Es3H`mP z0-4~zr)wz$9=YaoyNK>|@wMQkZDSvGZTqjAuBCPOrDbggeG_!-#wfwVt!b=_N6Taz z*ra+gmUx8G0XcbznDZng*1niqR6rSQ8d<|!A^valP_&Kz2rZGqLyYv{7(1^Q8@Cyx zd((}(SCox?!u9`>q8Auw-Sz*Auo>_Mb}j{KvMgluADaSBH+U2z9y!PklZb@@POSQ~ z;7*3{C*Y35nI_;^}LfD)C*?{^I|Y{eNh&%j|E{$z`@ZkK1@-5$ggOPZo-l zDJiD)4gxIz;-ax^! z-2S&Z2M35}5#Safq{XbjJd@jmuAp`skqP^sfT$CM&H^FX;SZM_;9-&B6~Lr)5DP2# zuNsz7i?1O&KzF{nbDCQlUtCvqW;Ql8y?Ctc&A;*2G3^Hrrxd)cz4D4uYlqPYBkz_S^ffvZuOPd z+TQY-uBN-BxwYvfb!BH^LtCr&F?FRCa(~0dWE*L~bO&jmIjE4tf|!^=k`Ozy7~uLO zGbg9vis>IX;AWeyHiliwsBZ*Xj1uqmA<%Be4g4ROhw2ROEeZvR+4pGN4p*pps8E5bs%{E1-2J?pz z%=7`#IGG5nhbiV9)v41wK^UNbZa1qbTw;*J$XWVe94TrArbI?&RxXY8laK&!(W4kd zx+amj()%xO;0ApR;O5tg2^U@fPIC~Wa`4Qf9IR1;MI3=6)P}iYklt?OseTa z!IYhuK(-;@yAY;qTGZ_%>|EW$fIv*B24QHDK$Ov87Kc8A*$ED!CyITl{WnK{XI;|# zCx_9y?3a+tNn`8Gn{Il{&l6*BDS4)K;%%!ULmIV$l~TL4UGe&b(8BWcjPa$Cn<<2C z>|v>OhcThQS2*%4=yucj`G7Q$)8$YuXxuzB-DCsaE?hRNE4W4hC<`310GM)YE;h7P zYq!C$rE`CjvUv=9AUr-vfC#&L^zel4Jtom*YNqSaFoJKS5eGP-8io|M#z2H7(=m!y z^rp9n>65Cjr8Box)?H|Q@cP7`omj&iMvgC@sWQAV1`>uy`A?>q5FSEEWWW+|zHHT3;AO^c9Nan|T0U$fYS~Qaw=9_wIhfi9(Y8r)?M9uEAAf@H-VLmxdtt zM28{7l?C^;=`l*#QThb?zmP!M#%lc+`2l-UDW|R{O5z|)(KUG2vWyp)e#X?5Ot2Fx z;w_jmejUgw*{%Egi=oV7J^{H2eG;1-U(dmJ`Ri1ZMSCAO6Q7hpboo40HQyI{$eH}Q z%a$QFouoVb+LrQzWAiY8LpF7b#0e?(;|mmPi{v5}7CQ>sE_nb|m-gO&)Ay!>6uWc3 z+j|SZZ{P+--)F&H#?;^0w>W)v{&?=3UDLzCL8%+Nr$8H;4jNTGdZQ(N-HYV#ZDHVf zMt~3Diqi&e5e@iS`jb}rc#e4rCxJOAv@L&jLf(}fEBcGeFc8W%dn+DS@XdS78~+Y} zsqF)kJ{xu{(J@>zCO<^Y-JxU{=;CLz&DPe2*9|}4jX2DVKM#9?AAlJ)IG-o>_AdRw zU^ShGKS5yq(!oCm`GOv`y<=BjwaIE1VVj;A8gf_(FR|!ucZ$adj-q6e>1u^T3^xs= zVaihihG^r!z>abyB2-B+mKGdwOs;3p?rN~E22`YrCp9tV)fF-o53@y zYahH~>%0`rl*1O4Ll#9c0|GpgB16N{1@MxV2Ei7q14gCb5ZMg{21YRtUt;h-jUk_DKDP^?Sb6bb^#^Jq1 z*1YTDzfD7Mkg*eYoxu;7VU}HqXKq>xtQws#W7#(kPr<$~K^T7?9awJ9@rRl+=a_R0 zY7!(|8IA<-Gp`yrTBJ)bcJ4UH@L&x%LUHxT5^Zo+;T&0CL;V;{=ZuG5O+T$L3u-h2LU#54Jp5uuT< z3lkDetF_MGclH?Tn~mh2LaObq6ZYIr={q_(W5`ZhnC;`G(6+p~GFLdvW_l2-NXQ8XI(XR<{jAUZ38M`=b4(9y`QMs{H!azB0SpTe4eymd}~{NGN`%D=uNJ zrEpbW90-k^A&pRHAI#R$bKnw2fq!fn;tW6@v%Un+TUxP=ni!4Htw_Y?!dKuJ?bGm= z>Q>-b{+r64Jvd2dz(?wSsGT)X-M3OK%Vz>y71h!jqFy9S>aLY)Luc#nGR%(^j7art zDsDiUKW-avgCI}l^x6(+eLmvkKR-SKa_p~4Xdq~1<~ubaGVip(x;Kb=1|*#!SmZu~ zWbKMz91m7eLFa;QTi@cYSmL^|^u~5>q=^f-L-&qsT4vcF*#Jwl7rvanOwzJAL8|c3 zfy5jcLwIxQJ8tywRk=_sknFdd4^{CTln;u7@g^gLCIUK^p=Pd1Y^9~-If9Irq_l72 z=u$XwC!H+e{sIvDyWQ|otFlwX8s&aPAWK8G%0*j<;bPj z^RYi17-i#Qp5m)g_<~dp&I7Uf7}kNboQ>?_6%mJD`wmQJ|06imBb%T%%`a=#RdWmUCrb|Lz9B+d}@#Y>gTrsS4QGC`{FSSl0A`f89C@HQZ=i#j-hTDH1tD{M!vn{~`}WAdxG83d{Qpgl#R? zu0^l)Vx;>*Hu*#pGyHqt5!&y>Ca39pApdg5LG``{ra`$w(PT*N>7Sz>F;h9 z9Jk%5pmmM?jJVdk$G>$8dG$`k!{?*Wet$=Xk3PuHxf&tuoXhc3%-37u?cfHF>TTq*Vol`1HB9lDucuX?I`UVB3wSG8Q;)ub~X(j{?`$ z@j2LdLGeT}Lrx;TSHuy~OECc_V$#0zG#Z0FVUnzvWoWerXJU|lM_B7!d2#RdSMr3& z9&w|&bPTT9)lcj=VyuJ+HFJLFqJ4@4N$(Szw@(*nf5Tb7ia78p(ha?MY;aJxz;Mt@WU|}s z&OKawE3u59rmNCtH!at^1!F=Jj&e>yH0D$q3PO;=VjD)3x@vu==2!_;$_&Hi_l+48 zA`b(fVLH|@2fht$am7b+LrR!w-qZ2oq}%%Qz#pG!;BS(`-nghau$bLpM{zc0Z)yVm zcqF7}t)~OY3nKkCK|Q2L3gjq<{O%D0`LrT1;G$OH!Uca_lVU9HJ+BSR3B11|Y}C7b z%_q3V@`F7o0M1qkBJ^M$%EEwg;eAwIM9rzcYSF)|l-Yax@<{6Gq$KM)13%qprtZi6 z{rz>mFLg}^$M{6iP03N%r8|ya`QHEJ4*jVTRX)2W)HAxw6zDbf;j$Jj+VVSSVdk~MC7LVI`Jj}U4WcYBV5I- za!?=n?wME+BM3FI_--F}NPK$k)n_g>ov5o73P_g&5DU8$vR|9HqRHMR%);pUr{5EA z4)r54=v3Seq$O<)5p-M5W)ql-DKoS(om5b000PoJ4H@O*wQ-Bgrk2O@0WuDsPk_(* z4slYSJ~nQ~YFzJyDX-U4+=S-t*>(KA@^f(C(|N&F?%v8JGm#DQ_Uis`V`n=VsZwT-^WThsOmpvR<@Etuh4&Kv8kW6pNy{{T%uvcHK! zq5F;l+&<7*!pBFc;{#1`OxP%D3kaV9ZI{qVEbv>#NuZy$0C7o9UJMaM}7e1g|ejGbG zQ0536lW-GiBrKFchjvU@_ayaf$fCo(u!M`SEhITn2;?TU6~Xl_$2IS9MR2(0-q^1Q z`xW6kw<2r~NyAqJZ&bLv4ype#T%k&j8xGV$(md%6eM3mv=u<6tV@O)vuudM7;#^HW zk8x6P{^4H_uO}jr!P_?se4@okMiKHNS&Dav6j50&8*Ye6^8-2ZDTCUYig?ZiR~=K_ zBFA(AZxp{-Z3R^`sM-mDj)s$E!D921A74DbI5_zJ_qE3=Fmuo+RM>LJk&0KR_~!+9 zzmnxha7cz5f*;kN+CvP~a#sBTxV1MqO6&+#wD9{U*Em69kSe!0S^)oaLru?Qh4C$~ zoAh@IvYE;~FVHdaCXWP_@FG+ym^rC(&g0<-!3g==5vr*vz8z~XF3)kzLU5UK8L5dS zCJiBSiABq!u!MRe=viwdhOnE5=WnNrs}~n#+~rI4%Y#e!`ox3uQ6ZLd#`8(A7DlHf zs?v5=S1i~0S1&I6FD@<)%jw)9Y_fLMj-itE>l?!(dA)ELqLyC!$udml!+N!Ff>a1V zhcofzHT|1z-%>MdplZ(B66xrG?^3sEv^?6-T@CBdRU5*q@!ZZv26EA;WwR4v@JT^N z;gXKGAK4!0d>l7$e2`@XfolOIVxt5EI^V8m0V5s@Xd(GtRHK5mfh_UX(Bsxex*fEj zR*VeC4t3!J#1c7_DGyB-HGzn{_#_Kp$7$ExbZIzLI9I#v_a&w3$5D}<;JF0>;l;N> zL0Td5!1uo)+s!{eubfgn{q@oL)%mN7r?s^B@zaw>ubxpz1TTQ}_|ePfy^E7G`KZnb z@|~$c_q3gR+Ri)Cc7A#W&0fRu{GGSg%}-Davhk+hZe17c$Za?=Z-*V(+hIrU4%-X^ z0l1YFK(_`pn&9RLZEVCL)yG>PuZn;ULk(9D2rEEwSB%mx(~O-E^d50wpc=iVfaCeL zOZ(^j?#TLnN8azq`yF||BkyM;J8~PZmb1f-?Ch{3JDqoA%NAfmh#gxGeN;T9 zu>qnYLr)?Pye~uF8p9qq&l z#o?x0n!K;}GQaU^P7Rz@l)RDf+1Y&fHjHmcVS?L1S*E;U1>k*7dQA@?JtB~iZdd`f&5m|6>_-pGJL*NWLSnJ!d;O*s$vu{j2Q zm{lu4iVuL&lB4PL4NIQzX%DIx^R`*>>tFo$|4e%^Q57!nNU&@vHd9QOa=jR|&*JCRiH6;qMkLdia|Q27 zeAB=B+23$w*l+?;L1M?oNKo3JGvVMy84YQ$<+_9kb(Bfk@K7r5Wc+E#qm^1PJOU)+ z#6QTBJsE~vVkQeOXx37DsM?EI42vl}1FQHj9^IA`n%HP~(n`QN;Og96XzAz;J%t|5 zeTUR8ksvZF;-X}hk)2dNAZJb|KF35b7xS#Ss zZw|pP`9y9NURBoK!5{*E&Id*82pOVGsA9X-eUG`rWnb}1Ee8dk0-e+=UqkM{q@qOf zMaZ3321SvO^EA1Ub#yd~$xMWUn#kEuEVti7Z5gcxm_$5b7WkuhjJyi$uaz~7ZeE;_ zg=wW1>suff;Ehz7qO2Lqc!5Ky0$wak#%P2*8ZQ_0RW8(;$hUin4d6E4W51{U`WOG= z|Euh&YwVb?eZc+05*JNyiYK%JFzsj~j$-X_tum!*1+iUpn2%jyvTQ+G9M8iKxBcfK zX4ho>;trsTO*hyPf3fjao1hqcp(Za>sFP!2_8)?b37>x*oH6DTzg+9Q0-;eWfLZV5 z2v1jaFNb9T%qoThI2s!D&0L=-+|aw!uWX>yMmR`s30`eAm9O~fmT*?btGi35Sfrhz zTTu{#+sANS6ZW_ddgffd~LLZr(Cm;)ov@q!qNzyQR1AWdk9Z*A?hyZW4+dFKQ_k=$YR zzXYKjIz^YjFJ#|5iZticggsd`=-PYX289*2dz1$Yav z8EWb_>!?Hid5{PR9aPuS>(p+*_61QTlgvqUT<&eu_3x$^mQ;7C^fI3wnE8AtIA|j1*JArd6=r7+o zTXw0n!)x`EawKa9S(ejx~QajR|!23iN!YVgybEXp-vVL_r0Y`DToyeREVDO-T^cVX=BZR-2q z1IpG7sXh7*g7(iq5AOIG=)oO71HHTCr5zg3j!j(O0}Hxg8K7$NL1GwL5xABUWc)1C zb};8m&qzE>xd76Hpn8fRK7vmosu4G#9?voj|^yZI>>Z`C(r{CD9X|-Yyu{9IL*j~)8K@L1F~_{y8EFOyS|AZA?)c#Q1w~; z44~!-D$x6N56rqk%l}Gg$Em+i_Cj}pE!3oFvWwa1oq{JJ)CQ9wkZV(Lp3<3OWWS+_1O{g}x6qT}Rw1~St%gf8ajgoj5Fu4@a*ATYvsth>{g2@Y6&WffDKd2Z zA89Yr6n+l~w0>H)UXv*mwBfA^=1Hon0blkPVm{T*%ZdL0!Fj4pn^Pi%DU9V4X&vRp%9}oB-m;u1>w)&8^OGk~w>*0yW1p`@bS_W4pv;4INkWYe zD&=70Rw;%b_=EGg^arJ0&+z(&^n_HH6albgL}_on9W(!ca10ke&>k--I16TUqFSru zj5u&hlLFe3h!PjPYd;oZrbd1EUW_o7qp1o5FI4P#a7PmGT;-=tHNJkh<~3FVgAnb- zJB$Um%n=E!d|z9Fo2}P%h-Rw8f>$D9!UFN%lm3i5Tc63dj0VTYuEzlD*QJ=6%b|_QYuk3 zhofw~yQE^l!L4#r;${!7*Mfku#5v9B`?Q>$DoJ>R9Zp1qiu%r#wv<>DVZK!R3aMl$^p(-akKa{7JiHCq9VAED?2-GmymM82aiNRgs0`g-zL7IBl3Vg^F2cC&2 zq7w$ITaxYDCc=nyIpSc`o@8y$DjBP8Bm7!42U2zrB1M`D4x|yACZwrSf#8iP8up~z zL)>wPx%;-l0|FSyXvJHSKdtA8pj;HRZ5+knmW_k%s9}8~(qVAvhHEf8nz-s~enCss zQ!<8+Vk~~NO0iQ_EdMX*2E2;F8po=``kPIfp2f=yK-yQA^CTt)z@})8dKXIa3XCtd zss%X*>6Cbnu~;n;e!x8;j~gq_dM8osci( z@o2iF49-H)U?|iCOL5ZckO)w`R6L*g=A*J-`Btj1B0bJpvA9u`^D5J(zyOrxET+r` zBksnQHoY@jzT!EH7I5;jSekohT4~Nz&7PXOfvZO5$&44Gu&>;rX zh=fUi7uXF6Me=?oUx*Ps^2L_45Pgr>gvCln;gl+Uvg(Mr#5i&ujCeUJm>0=1opBQH zalzz@-q)^21X6l%K)YZ#>Jy%c1{cz&aX~f$AWb}+sr?OSo>h)>M;9ISIsK&_Y8f^~ z{Lc-NL0_Oh>HSWI0i?3&b`=YFiO$8O6w|jD!(`&L*Q*rBRf$OfilKp z#ltPfUp~lost+pr@u2!4soZDoVD=;hjQj-i$FX%Zq3(;rd7^zzgN`iX;sadRC44_% zgs5qmbZWDLWBeU$+|tY<%LS;kB)7Uk?ymeqL&xR(3DfxS2YkUJ`n`jX$VA?8H?QRC z)Ys3%99s!p)!B!;X-UdRWe{9l$AoZ%wmgI!}3$te{fX57k5xCB>}0 zK_n3pBTkd4QQoko6kf_4kTueT%il(DUbLD&`I2~v?pJaxDP;iV;7ujvJp%-IgTeMQ z3n`LVus}(iXE@0f{doOMz{O5-2V=mN0X%s|{!k$$KrR+<;p&QmDCCWg9v3Zdfkhoo z-Y7`uBqW$peFc7gdfB}0J_eYW?)01`9@>`OwzIm@KCSj-=aqK-vK@hk4L9hZ&4(q_ z*}7!Ao#1`)Mvbeny8*$m{=Hne8*m$qr|(doin3D^`d*<(2$2CGM(9CT$P>bjEH5?^ z-;7f$b(}cxeLW0Q!%rL(w7O{&y51^vtm}A|<=gizFXXK9M1t!!i2)?BLKN5fNFJI- z0>axU_5~D+y$z05Q6t&dZy%50Yn}u~Tkba4X%aZWPLse1c5t@=*9lT5NX#_F?i&~m zdeT{A|K z9Z(d<3WC}w+O8h>_dAM4z0&+sj^bvM<^Amk>g3@!(o8A-xt=e^{|`3gi@g@p5# z0UXj{md825al~c`Ha}D+)NK}2GA-cUBcuC9ogd99J}d9=$M7jf;d}OoS8gNYvLht? zddPJy6N!OkU~E&4dH4;KO~TtuCLh09hMZSMQO%Tcpz>K%2lTFNO;0t1?t48)vX+k1 zKiXQvf5x+pQ?VqlGwgRbZF z1ldf)1K95zA_0K34pN+y)SD1)H(Y!slhE%_v?TGd2y#^4kyEVPZK zW-RE8ai6FclZn}L8ldu`jB2$g$Q@4dPP--}uN)WES7B1*b{|#-mhO?{XTcJO?)CL_ zDpj;4yqRMp!wJ|zmlRomX`9O1JfXoT?}G0j7BhsHDEyxynABULf`$6hNLG}E5NpX~ z2O*;nFCdge~7s`$vzOT(`N}wZdMO9bfm?lfxLh57P5j5R|++`E_lLFC~Mw!v6Aj zc741I<8lb~a4p5cA21p39FbXC1L%XQ;6wNW%;2C178Nh!WIQ4dM@zAwPm#2~67Ea> zgjn`Gl^G`q*cbr(K-y_TQcXvu2`@;qlVr*l_zK26ElNpq{<{I0A@2_W2c9;d|1E$E z{3DWbw}^hJLzr$!A7BYAWfzZrth(WHI3dK$bb-_tJ%=FQfc9e{Su9l5-*hO|b$)FNOzUR#pnH&06)%}myj$E2SWQtk5V;pKqBdF67+ zvzZSpC-il2mt!>x6ZOF2+#5XZT9zwJI0{XDE7$cmIFE9iis3n5A62B4KVOuQKGWV> z0~M^($Y7zcUMwhFk;M;%YXW&|N6Lp`Ber zdh1v2tjE-Jk4@EWk3~Y<8&mDs^23KUrr}f_9gOJcm&mCurX;S_R;JLb=A_!xR{ z6oNc(K*Q|-uj4mZ!FN)*Z6Hl5y7)JUhH02jZhN*`-#(x8?enR7fF#kw)Ygf&I`k9Q zaU&gp5aId4iG0@zEzgBUdWz~t14=r&4$sSi=j9+&0?ti9{8s}c9`eI(aNqMuclYyY z<9xDPV=(^K>Ev&nP5usOcsF&-Anv>?rZdRZN@H$O21;0zFMavh?M zOQIx3#3hUohSTedlV$7lJ>2JHa{HFPpG-SpDv+`qUWGtp4aWlkcYCrFTpo=5n~a$X z)MCq->uC|cZy7DI2Ra;Z=|W@d0k?z!JYGZWNz7Rln;@UhJ_o0_DG5xt2nAsvqA%qZ zG$JmxywhMEhfxSqE2LOfK2yOc=GCW42w%3=Zq?v}%FAk>KSH{nY<&9H7XvL%yO8(2 zDho?xyL5%bx3N@nyBI2>3h#z*hBGouS=N;Pb*P?oMfX`3MD1`*sn6UwV{=SiTAS$U z%CXLNGvI!=a{LafQ?ZNT+<57>gt$({ty`?lPDZEEx~OkVb#IxlR~r-HTQBas6+{6o z@mT019jRs~F`(>eBg>McMryhdP7=`801;?tB$fdfwlEzE%DOg5{W(?*b#2(XZ|#C` zGdD(S%hjc_75D?s(O2D>37aD>9B5BwPn$y$WBkv&F7+7tHhL0I# z>tjaQ>O9Jxm!=S33<%8Nn5k{r7)|8RfLu%fWe6-p=usf;C3<8B5yaQ5IEm~8y$Llm zfu}Pk!O{Ki3 zj!57t;(sFFfC&dQiylW1;!Pblp+9X1;aClW7JSzRZL?i71TJ2#^H8Y*5*qQf@C^bG z&p|p7OM_MK1*J>w(v&CTe;lnNE1c)&rRR}SJ-rm!!J)}85p!N;` z^#B3LU{25B)|D2-OMYkW#|qGdqrp$6(`zI&&haAJ(fO(5k5nZAlu&_1Rm$BjCu9%+ zs#)RwYhZ$`^89J|zml6yf^dsaF=InNkE-}qjx=8&swuvx^3JW^rV`U`Kk4ywMDa<= zG-aF4ds;CJrO}wRpL|Ib7bw&C5dn7}0*sGb){ik7p+@M&o_0*djCd(d1Z{YOGeAPA z8*=_lvEi9tNop7sXy0%mw}*08?WY`#q9x+R1chDXV9gC@0}|R#kX9H?VeP7`m_;&^ zVg6_MyawsGjUjM%**G~tcoOiO6!YHJ5bydmJDaS}^9AP!3(g^bs-5iBhU;1hWQudO zqf!4f8-?RFzOm}E_vIYn|K)7J=+s6?^*q zWrOW-W#`@1M?B3ABh%9nG{YY1`w@hxVH7!u0~?kZUlIgq!8mUlQECOiGk_WZlGZ-{ z6_|S;;b$2J2!sgPBVl%4ju86V-5rHSnGf^xVxRHmFNGFOs0kLdgp&AU|CSln^ zNVcHXzJOfpkAK>Gbb4|05VY<;KKbdBS4S@&H6cFR81K9A*Ed2$8_DEf0ipW%6-e_h zj$fXfoLv#x;_R|JwS0}leB-?Ny`<^>6zm}Wz-e6#9ACfVYJe;6xEkQf?$Y%F6%($` z3KJ-9#Q!r>;lz->2!K8dpkbvOSO-v26}g6+3d?{F1?*dakI{9^I!z8D_2zoMX5(^T zZd?w)4&v0|;opVjpzYi5{;A*boBj=bjHdr7z8ky!yYYg%em7ol*YC#b4%rMuV!$bd zZ$+Z+zH0}d+H`DR_d%u>pajx09UYpi2n-PgClTz}bfp!+b49qR_T6|+0Dbv;|8C5W z{cbvowtv*ew1uf>|5e?Qq$gYsaLe(KAXb1Qrl>QCrVANAXg*d;fq2YPW^5=zwHck+ z5;$lK5R^yC)WAxeJGn|+=q^df33vo17Pwy8+2dnLFi%x=Sv?)n{6+&S02gmP2Xb$X zAf5FCV!G!#P;zG9a8*Bkc`O^CJtxL-Rl-^ZF-|CH@^A_D_xWe#xd_4&azw&4hu{)8_#^G%a0Gaj(0=(ZCj@l?<-sriXHfKn z%h9w46lC~eh{8+b3+;0g|8c&+6{y^qVGh!%=$FTM>zD}K;j16@v{x5LIOXIo`p*c1 ziBsT^hcim+d~VUjPo}cov5$KXvnZU%@V% ze~~u>cR`&Cy74ic{#eWb6vKmV6IAACpEEcR`6mWrebLj-E?#fC&*BZ%_c;^=dfL;A zXSfeaY3SK>@b=A6BPsb&M1;a({oYua*6wZdeQ#ff)TrXv1e^_2_*(h7nPf&t0IFM_*1&`u`t{C38R1OkXs_n_dgVklv#O0nrvpsTDd zPdh%ryYoYAocyHPD4>R)~z8G zm5T|HA{W9Ym0jYq>;TZ#6vw&vK21Du>@XN||P+mJ> zf)wrS&(C?Cq!~xp;EF~@(;c3lK0bPRl6Q4}!wX)1rQ8bF^%ZxN%d;dxjn>iNR{6&SFWBF1p^XN=D!2P=* z@U9#qQJam($FFuRlfMv+0?@HERq-5f+7MiLIT?Pl6jFtPC0sCDu~g4#@j9i+_Su4g zw+E8%fa$wg$c7uN*y~iYKU3W*IkAtI5jq;3y5KJeZCm}8FlMNd7-dt|`xx8}%X${R z9bT)7*~UO;!wI!|bz%=1mkR?x0$ot9>h#K=T~V& z4z-o)PR(iIpjA1c*+RQw3$58@+i*p9L@Fwok_5QG7^2*+XPOo`MUg)ABFYznOclj- zk*6cfi7==a5$h<*p#oGzrSPNl)pVj4m^LH}>ghyxQ#vu&Rse5TOPrWr4eZ^zn>ZAW z^(pikJ8AU)v-fVhkz`qx*z@o!yf7GKL>1|@`z0EKPTnGdmB9!W$;hbemw;VKuW-6^ z{1TDOr*6!Heqc0>1_pvKz(BxABS?S*%m|~GDfD~yFEoVK1=2rgt$mJ}o12@tn==?y zSyfr6s-U}@9Xoc;wqyJ3z4lsm6q%?2bn?{LngSI|=TYa#<3 z1({uK7`XmU{Z)e}ZTKhIg@oW8L-#<3{Zq;n|w_Lt) z!gkh;6S@O<;vlubT!MJXVCdX&X8=kT*y&KLu|ISl$_Ob6uFb zZ3N4RT@)A?o@H!h-2PfD2T_{p6cql8^Uq+a~lm3Ul{2O@Egu7~uL#2>NH|H`}HC5kf|5ASY4$6o(82LuIcrO&d-eQvWo743 z!9xVCcgV+}4q3QUKdNWsYkn1vzgPQ?M$CfM0jkRsTn8$YR;(`1Z$$*w9_II!w*~>Y zdT{4e-tftgfI6^*kOPW=1pxZNb3w_N4*(|!hV48KV%taq2pIqZXabQy0!kc+gO*o$ zBw+ki&6>3o$o{6f47Ds(SNY70(Vy4}Qgj$^Dc?%*tBYzkIB&lD1?w^z2K&cU?qjWi z$5`$**5+;&O8sy;Bo~Wk%}a zZ&aP3z$}WsytP3xFZjvYI$B$MD8h2Ku{LKLYwKQUkoa~8_Z5Wc8I~{Pgi&8+gI@*y ztP_AN9ZWvCOXVSWi(U@c6-vDP41jb5EnuNx&Dx;swFdh%spliOd3oI0~?L~KsqN&2K(H9Fww(`JKo0N-s+~eJM>-sbqwh2 z6aO72Rwq}zdcu}o`qHWE9P~3-Vgx3hGw|h4M^$OcWj7lo%g)AVxo2&5zKe#Mc?hjF z+F|vF?!9NN5e#cMx_gub@*Nr8K^Ans5<5-2IL7-av?4e`@QA|D0XoWbatAsjD3!M1 zASHeX!Z0IY{zbYaUrM(ytf88HlY!h6(0cFsOMc*vqWcNl-Wh-D5Mf?JxL_ zs*o72E{u=QVF&}V+YQLr%7Bcm45)jU&dE?V=jSM*%zP_1QmVkjS?a_FYVQ)b)u8)G zVjCI(Uhc#Yp9A9uDlJWz8kp~rnoEs>_s}yQ(STeUP))<;Ps)I-2a#Z|n#4K|%%-Ee zCovqPFW?bpc8GtX3}QmiC^X$TgMe*@0BV%SDE@vqtOA-?)mc9n)fRYWIM1>vGh1wdB0~%9w#AlMadXQ zpvn3S)~}oU+0CuU7gU@#iuz1DdA+<5zReragbQbvSc$R{0RFNgP9^K^&;@{86n27_fwN73Abm~5uJLD|06pAVGE0wsd*Oo!#vb?gY7y%C3ohB;J9`m zN@Rme7tX2~r^~*BiQ;htT-?Wv$NANql_(T|1p*7|hQ&TYpH^2CJjM2@eq^>GY_z*jE*KyieTic=m!G}Jir3PcVLLakwFnQBajbtBgn`>a_`C;&&s$|lo)u7q6%zD~jV+0~m0HjRYgzeg$zE3ClL@HDhp$qN<0y!& z7qMs_Mhv$MjR5k8+4qP(_eCeI^x273CMEJng{N8617h!LzvojzIiBsH*5K-<8tsO} zX*L8uIBGqJ*0|yj^q$N}v3w`QXQBg*+Mf>tqO|$*6tI7&rB=em=mUyK_^BAuC)05W zH<{ut5KWKaev*Yl!s6r!8Dy*^fCoAl-aqYsE<^AagmhqI6QOcRv9iIgy8t@c&)LLx zc5q83-@*KLlULm#mRo6SyEnt*d7=aZ~Jmf}#P>SyoR2KxO z3k5W6Y^s?uMQAlOGd#Q^_P z;(HJ;3lKpJLDY#?PUdW8!`e7!=7ZC+Kw;< zpdB=Ik&TyrWZGW95E>vf;OPJmGK4RSsOxfI3>0?ZDqV;5aQU|S+vY)Ywi#}SC1pPP z4G83;ks8L2s9~HfP>0RGH;IW@)|#Lbes){mjNhszYp=!uG>Oq;yiN|WERRVmPss;G zbEPTysd11B`#f&9-}}{t1XhH3f5aey6v3==dp6$`9NEVBlXS0#7ubkz;8H2FD7j+M z5K@qds98tNy_R&ORHUicQ6wGT(;e>>xKZ%*4~I}7K@s&SfWUM_ic^K3m70^J-zr$XcbMiMETjV1eA$RPZgxDuEG@B|c=c*EfuEffHL zbs_BiQzdqxr?Jo|>7ZzoU)L<1hv`bncye>vXE6>m3 z6s5_crBisHMVX|yc~!lNC$MCMc`;J-BvJ1=*n2+MUx;PECRt^AjEwr8VxOy2%<(b@ zCzp8Uy}cJlXR7t{!*7nCTc{v4jSukVbeE=25lx;?m!|R?OxsKq?H&%sn#Na=zDd)? z#qQ%?e_K3gadt!D`bD(+4i$K?)Ku`s69k3#Fuv#u6%LeyyW%abvJLMx+Ld%~(y4#CHm8Xon=Rmh+#6tk`AmMB&q6DdMfrM6!a!}(% z-^2}uEtikb-Q6CQaUm9>ZcD=HIBJ;f!AO+-(+8DLPq1b|Mt(ez4Vkju^!{mqJ`pT- zI^k2w7wr^fpz6TO?D`$5jGw?c`WGnwhW9>!3CZU>mYTDsIW3Q}`~Ch?x$>**nu^&^ zOn$>rJw4yET?-u?a=_1{d%_ z8QUU`5vyxiEQ>c8?hE95gsfxO@Sr+NTn13g zVJZZ)2rTW6l-cPQ5a;&%^6*M(Lvu=kKj9sizVlScyR^*v@>;?ZpTVjynrv*hhb3x| z0MeziF%LL{3#S^=$h7&_1O&9Mob$4;Opj9Fw^1_EV*`gSv z)R9}Ae>1(_AR^5+K;sw->MJQMU}d8{C?Hz>wsQrKDCwM@C6U= z{Zfu^%LdutNb)yfkh+xXN;Ay>k~I+7W(V~Bx*~wKQ#$W=a;Q;TZtkTi${8~VS9_Tm zCJeY+5Q+gUa2g`JAA5k0WM=9au4O^7!6RjJ6o*c*wBJGG)N@zJe=YAbB^s>d{SE-T zz>=fJ3*cM};Br4jJ*JU6F7jE}qD2kC&!dC0lZykP`#U^+b9Qod0k#n#{{tJ*$;J5v ziobT(>;Zm6k#!oE*9I=x-niWDjjKEIAGt-qmy^H`4bWd1UXYOxi|ND&!FH7F<;X7@ zAcM~w%Z9`*lzELjcCqvJENJj1HRA&9`xh`S>rdCXHX=__5Hx*18hqcs1)NOjaF@7~ zhTm3ECrqNP6*_MT0DyYruxN8R0`P*nO}r>C6a@=ks`^M_zF4L2;TIR+ z=96L@jQ8UGDJ~TRIX=PHVc1t6giYL#n?Kp2)d^Gft|yl6hq)6Pw#JI;6w^S8$LI1hwrhiQKThmBMX0l zQX!4afBeYLf2!cybnf|9bHKe)R!Z_XI>#+^X@c*<8tBY_M zA|_Zs!H|K)xGGat{eDH1j5#uYjg$xZgc@C#7lW1T#kUB_%3D{q^9Wz4cPTE*(A{`S zF2g~#>a3tC&1Q)>a*;PrDPs!(Z&6^DDEqP_b1Sbsgh7W=yoIwRb|q((S3`&<86xzE zz|wqrqheFMh6ECHh>WTmvuvq{Di^VIIVEMWxLhH!j4M{pqZ2(^ufAdH7u+eHp5qqF#^Z}L`!ouo7L0|}XkNdU^L7ce#Ig z`BGxG(~^bsiMm}nX#7zlM9Nf6C8Q}lkWd0iZkqdguFm2~%7z$F#rHH*PFx9vP~GP# zrwSJhaEC-1>n)yTjM){pq(Z{XARw@&=UxJ|oP7K!PYFKIPtDc0+CY$h_z?9(Uxqhv zk+HH7m#egl0R*MqoiKRp$b*vt0xa}|!u1y|Jl7epN(kNJqp`qP6lo{*!w!mjAEx_i zHS8p^r;py2tFJt7EiAmancg`vJ+Efzs#pDC`t{N+w@FY>IRMj}Y;ZfkW`4g-blQYi z^d|q?a*Qq>xWA_G-dL+MR(-*tmfl#aD^@?avzuhYvjGK^4MFN4hMZfJc&KK~a)`TH zMut#O;sDv}2Nr753>&5Opn`zn0kD>SW*Bh|SPqdCRKJ^BakQJ8q)yb!y5cAKkQPLx zW#%B{Ly_Ygb-)=QJ4xVRXJ8~-lSiE$yapvyi%iMG2&KM66ww5%p-pJevYaif$#S-^ zrY(3JJb2uoLI?w>?*iKG*g)WrHZp>84j?xkh$Mlz@$Cda*ceihApZxUNRp(H2SO1e zs96(~b3(^`L~9bpe>3>WnzS{w5*}IBHr8ZqV@=i;DokLYK{X?y%?=C*1``H0FnL~> zp`rjEb2Bnx(9Oh!LZJZ@_z>6_6(x z%p%1;zXQk$zH%Y5Df^cRZmZJqOSxgSaV^fR9NHfQLWyD^0+32~f@c$ZPRLf} z@OOjh4HXl0uS%6q%^0t6F5(^&$bpR;s9hjCq3)-CE7k6t0tk=2%56LtMFoU{aHdK5 zNp47PP`D3A#04dj@pQUZSOZ5_gfd904?DwgZ$|?z^yuMKZ7+-8X^=doLWwW4KM*Q? zZCxeWO!ORyaG&ljK_4{8fj2V}?JQ12f@}wOiqibG%zLKe7QE7RR2B*FMu>mH140O! zVBHF%)Zjj}i@jGq=a-$65ifX&m*CQAO++*u(UsKYx-jJ%45VHj_OUKyqs6+^1uU_# znJxEQ9U5G>5nDW=FKHl`s)71$j2mV|$-XX+=E3^drM+35$IvHYu7xMR(xi0Iz@#O9_A=rmmnU{k_%rZf7h9b)lMQkWwi?SHx>kyvwQ0f~x6z$Fv zaE-;3WrqZv?V_5@t>xdP8o18p>wFHIuYU7ZoF2#Wb0>$QR}dJ0*25PgO+08PA}@oc zm$|tBfwR`;jGEqO%$n(a#;lp$AvxIf!2LuWwhL8sK>OnaSP4Hy<^djS>_!?3*w z+lK(pnh|&yh%_`K+hGkUN;Q2G;1wIhs@R1hRm9l?!IKr4i5G!X)d>CNS;M9`w7f^2 zHKShav-PZXaN!$X3m8*`Q-^7TAK&5VRUW@3HTYFbN!W%ddHIg*w)*qUuOz80U>$%e zc^uK7*&8v9qghXC&c0mZXY~xu&rwEu_y(J>-tocJLGSdk_vZDh?|KK{9-O|STwm|t zIksQD^P}Fy$*Yrt%M*%0y}7vf?q@&W*=gz6ld8)vZ{CW(4@Y!q2Y|v3{WjjT16aC| zX5T-e8(a1J(}nN2Unuq!d;er*yC5{*+$~(det5*H z!(4PgZypv~ffDF)QSs8z#F{Nb+bI>7ymI1mse?euYN?0dH=Nu~H}^w#`*1w|K=%hX z&G`|JxA#__7b(xFPSnb-WoHNaTG=zD6gJcyqym=!kI3-O!j)_mLCvZBYLTge^L;%R znf5Y6S9I$HU~_DG%FqA!*Z=Mx|IPpKfBpOaru>EydB6SdqQT-;PG=$tH!i<8&gQde zvS{RFdKdlne;yCZFV4}A`Stv_|9(p4D(Lc|gtzkJ5Ei`n`1k+v@BixW|LX7lx4-+3 zfA^otZ~wpl`rrIN|LcGFfB*IWQhxJ){-?kCzyJGx^?&@w|L~9h`oAi_JeYv)cxUGr zl77Po8=A?pUXv@!v73*g967FmK;dNqA##T`vXIyUWsxd}nQ=49scTq$s63^G4@a^< z-E!j^Ye2fHuR`9V*zJ_x^ncu&9a)MVW4p7|(Bo`)mYe+iEl<;BzP{#9Yd1V5qxtJM zJZ?AQdix<%;$2?k>-ohyw$RxfvrH>7t$?^<2YDwa0#l+1exuk2Ks`2m=<>QC_3%?C z3+V6ICMXi|bK(XWvJK1ZGM0@%v1vGUe=K}B!(Hj9R6#rFt$`OVz(cO*B>=Gn=(v@l zOGy~p>F0UTmi^fiXn*zucBcmlZ7Mzkz67mwxWcHM2jNKq5IJN&I9k|@^^~l zvFikG7Rp-MNjzu**YAkHGd&si0freOcje!X=czrsSKFV)pK5y@hkG{5U9;D*+T@_w zI`$IhuU2Rq`O7(|57EWbpZvlZrEeF$ohoPIRC|N1%-3ZS)4&_*K1PC)l? zTN=m4N4cHDvF-5A3u_9!wG;uHI17-7hW1(DfteBHFo@#FThs7j66ZmVFb;wkND00% ziKK8H5Qcfn0)#jX6@FV$lx*~Dth&Ia?O17)Tba)Qvr8;~W*~r>TRxOv@V0J9Wj7Af zkEsyP><8NtEw)U(y`80W_n`tv_ho=dq;3?);H<+tJc!*GUpq0<04BU$yw@BTHTQ6d zO~XJTeU?~>f$|6|@~ve{u{_UTr3rUFhvj)SlKjVMDgL&R?Ut#xwzHJ((QA}xq5{|q zpzert0pyw>J%g%W-$N};iZC%sWMD;kmIj8Mr#T8Yv5AV!5Z)cOP;p$d6jT{l&f{8& zu~G*7hi@qx>M!TS)L)PK(rqThC%X7@^@kPDk!!Hm<~hpGmecMzUa2}N- z>DlRD=z}hgUY;DkeMJD{!O_+GgI5O^XHtA$|NQ*zD}Wo1E>2Efzb7IRHTflx?dtSA z^rf&tgxa>&h)_4(3MHr?M=f+!CCr4@JTj|4Zg&S`|k#>ZaKR zqf|7ldD5;FttA3UK*d_3N*pz(1Is8&sS`4^Y2c*zJBvUY+rg!#$N>wla#OdJV+L^Q zcsLg-s|g9FKJ`A3=??APj^lwaU#MP>CsG_bP+pQmkD&k#h~;2{)bx~^B{LFg^%Qj> z{I12Bs^(Pd6au9=o^ia#3wBMqKqFzAoQQ7~NueI9unKxiRgya7y|F+~m@M<<;YD2@ zdN6}8MNbVG{UYId1Bq`8a#TP{6Pa)|4}CR2^gnTD*eUusi;kv{eFF3Bmu81h=F=$_LVh=B?v!c z3=%%1j>p0*I<1U=Fv^XDiH_(D)-R5F6ia7mqzFY;=HnAlz%j7k`CwLnYH1F)(GTJ= zgLro|09Jctke6&&s){+8I7hfRr9JiiBBo4$cl7<>7HoD>x0>8PQ}XHx23$z0s}JxQ z$)KZ1T|wuU5b01^(ny7Ola+ku1r|cFTrQL`7etMCMpRb(R@dB$VR2rj4F3w3sr;}n zEG)6mJ>ubHJCkUjV!J>#q+ocvQx|3_YlS=xq<8=-r26l4oXi=JUEs{YTuB(tx{*YS z?^e6jD|@uLLzl&!7mTZ7L~}|YEnT%F2Nm3X^%vMO3HH>)lAIRQqNsw93Ui6#&j+%! z2u*AA%2n;h<*7PyJleybL?cYArOfIJS+#hkbcv44`gr$rsd~kx0U4-hYHQ%U1e1(?RYwmr{ww*Obg&S6VmSSiI%;K z46BT0v9b!+@+R$ifc|4bFg{5+p~^^nb$tXC{Ln$RcFlZ zffXOs#Y5uF^4k|ZHD~e$;o&6)=xJrR)nLfWs^|>u`|?r`D&8+vd#D!>RXBsk6oCk=9y7(Os#srNf+I>{=QIoK zT|r(j;k$Bw9DVj2=BDRZyqa=xg2{o{cve{#c1wy={jjGL zSObHv*fn16!~hQRz4ED30=?Iju%KgXp-n}J|5<<|%iY))}rd0z;oNpEH(E4j!> zE4x`9R}!g*YhVyXDij4sMl%@iGkJz;Ljj6feNM@D$d~#P;-xP;sfga%h&am zig1e)w+-3>COT58;c+iO8n&6}F{_F9}& z`$?TQ<_5y~G3r3<9H@rG%)=-(eZXv>Y-Hwnih_d_wH_cn#8LRK69Wp4Qd!dr(gZv7 z<&AmZLHjc_w?V3R(L1Uk)dR=1?8r&dB#m7M5DkP73AtvKI#ALQ8#s@uvok7e5>cR=)A!Yy?*d#DqFEKDHD3gW)hV6iX4fGE& z*wm8LvwfHr^s^E>x0g)DGacWsgRM+vV=xC9BL8=3GFwi!**e|!L#Eq)$aHtl8-v-` z^pFUQP1c6l5khiM=d&R67%}q)SrlNmA}FFEPn<=dNCmZ?kNja28{lxYvZd*EgU}9F zky~r0+u9^tgOIcRqnPgXu`QIX_ED8Ak_n3vBTkI$+*E*T!$Gwum&=h+n3DbrUb)Wr4Z%w)nL0U8 zb`G-3NQyw7D(Rt?N2UnV@b3*as`?(#>JGg@IZ1sNXGIk~N_B7x-GTEBqFr58z3BVE z^(jpR_Ojq%()O~y82tT>-doYqe$^s6IBpH+wbQLfKcM&sBv{k&XcgoB!`IcjX#sQ@ zkCE6jwodGAcpO`kd>hZ}zWjoG8W;vhuY)NG)RP(1oq(AQfs6%!mf%cWQcc6j_%2LCjNGhtf@w?$fHGI8oJK=U@T}}rhv;RPiY^|-dfx-L- z+KT@`Tk*RiPQl}51c{db1%|oz^8iFjC{PbUxfNvO0zfgi^-S)%)o87qthhrDw3Dh)JkZx8ckY4H~Kq8Fj%EBmpXgL&?_=RV``WwNuB{B=}N5YNfEWa7a?&>)!BpQKN2qz7A)6LMU+OM6#SL4~cO#4s8 zjCFxtt(>+k@M^Vz4pFj?*t%{euk7H26%93)HHB+vzE_O#y%nyZ`EEMAyA%$<9~ls> zkpFN%`WB~Y4$0Qs2Oo+RMwuHSDh9wQc;o{7*TK8eN1-6(a0237@>*QXbwhKdV9@&< z0tdYSR2Dkd3s7YhB0A>)(hcxT>cy4~XAr%&v%qX#==dUgfA|J3vZFU==dVuiChPwC z`p62k))GT+yQ%cHo67DKD%caoj7^!u7nzxr8DLL=JB@;f#1Fxf0w|JUqEOJl>%jz4 zDKzn}z___!+cV6lHkE#`BI3G!D%TRWYf^FZM=_Pgw&lJpbK`C|m0M^CC81#ffaT=i zBuO$13~CBp2h8?P;3dF7hPIQULKNkL86G;@pyqCl5(hg%EISLAr;;TO{#H}zY?w-v z@fyMJcq-RBB4BgwUytT}L{xzA7|+HS`V{X?V3UVgkV6j#p>ovqxUPrzejYd(G(&tRbpaN)lbnci3;Q}d3Ua?Daqcm`3Fhm zzLvhOxm+snxz6RXTJw9VxjNy)WHO#q12YN;_VVp9A~#dfM@W2KE39f;(;{-CMkXsUlPb8CX5(oaE5wezU4{KF zF@-XeVCtSLap__vOsDs&^9zekAhB!Aibmp8BKSl6`&iL2D~)gQVf8IxnVeJ@Ka_7O zaB+o!)G|($>+JOoaJtf&?_H|X7v8m2x`7*BJYMwRpc+4)6>o;=zUh=t8J{eT&aX#O#7Br=p2aWUD5PbDxK=et&s#@hu1?YuBQ@waVMe#irRr1kttV5NckBg;o}iD*D35S!zKZ z(1G`7C&#A;@6RvZ{PN_8$(3r4M2R%I!*oiez)e=zR{1a~L;?0W=*hia9ya!3HK9|z zXsNdTR7PJv>kXp1P-d+w-}($zZ0ATZRIl2NbV&H^#o{yrrjRj`BflaC zyQ9U)qTa6rw|O8#_b-Z5Wl=ayvq&x5`U$BD&QsCt0s95DinO0w1HeRf+NC43&W*jI5z%_2=7O1E@m=)_ZBJzSL&eASj zRlB;K9bIK3Ndk>juf9v9$N@raT*wN%FfXmwoZ`ExKJObofYlp)t|{x)lQ|fXJ5yxq z?$ot$<78u;GmG)8Sb@J*{Vd+=c*U=5v=6%Nv@dl{S>rCd_G4YBtKNr=Tkqvj?zHJ{ zoS3dzh}y~P3ctU(836Kg72Wn;U(fHe6$nY|z_(&agoI=5#y{wSCTZvL2-a+JmK?s; zId@#R*^UzI@jtESoZA~l3Mgo}LAce$I2tvdp zu##oS9@q}S6;$8U+n)#Owd$A=m)VuvAU7W}#^{v^hI7NGR1c_%@}@NH>N{w+!)s}` z0R_8MN00#&SACM9hQU`ACdyszv zq$Gy&62Jk#4TEpU$UNAn19(7)fKtu$fGRc~+4RgkEBMLuKFjpn7Sr=Oo1WL%^t$Ky zED)i^z$bag&_M+*0_iL2^%Ee0Z9qN%J;_P5k7dpbeX#BWBkb8gHUk`N88+AlR!k36 z1&?le_D`nwID|;IV?<+6^@=hxiOM9i$xyRtHvF>64q&dKL^An;YQfha)p8^aE=N`_ z2h6poOw7?D<5T4)s%NyC`q_6@(pZ~YUX-JkghZ^#mopCT22#w4t;0vSLQ;0SRP9)G zfD3B=XB8*7W#4-^pUsNs(K+^2sBt4fJ?h&pxt-OV`U9QWYS_d*k5Z|qixfC#DwuB+ zMbD*wV6aDqYuNjzhBQ^V?|*8vZN*Aq0nzr2xxEB|eMzByFiDHT#>2tv)8JaOZufV# zFSGlSIj}Qj5@%yTbV_6EM6tPY-j*(TQw#BM9PhtG_;vs2<-zM0C$HYT=-;Mma6(20 z`f;b5bKO=+*UG)w@Ibw9W!`K&cncI{k^wMcBw$L+tsn(^xSPdbQuM6AjU_jhqGHB# zQ43>(WYLd(R@&JRSc3QMEf+zVw(ACNunl5%1D|oZf>mBNA<#*-2Q*9qtb}iSAazVL z#{@Lf_kkY|^~>f_5*G!z{`-5KzP4=kHj`&=GkM)})`^8| z7nF)D19b0Umhjo}OlX&xAv~$j3c=9i20jWQvJf&^0XWC8!;QQg`Drjuq_xQdgRbvv zQxdTO(N%XYzVPIA&|w;p-|!Y;{?YtjiSh5Mm5 zB;*gDkl#YgRI&9$3hpEPA%r%oR#j?7<9;eopMLMnM8!r?sm5S`@9pK`=lMOmXLHg6 zoeOysqODis@$m6|A%y$*1ngG~sEgoB{j5z$AOmm(b2AhOG9sqg#dT4wwV;7?EA8S9 z%@5;yedapRhV^H5Utb*)W&9}ZCB`5H6gEl#n00)pu3I4RgK&H5+6>qL{otVLECYJh zL*2LSU|(%G%M~7w=5UQw$n5%ZAbZnJTCFsd1r|lgP?P}KIrj2y>^neo1aP>3;f82~ zi43n}alJJ&6uBba+*?c5s=o_piLb8mF_Yf)P00mU980%19RY-CUZjo}XYS!!i& z;<>2_B~h?CqtYMlkqI<4Xh%~Qzh+hv{7&Y+hOzw-&3(9T>W%QBQ*VS1oqD5t$pdta zKn>4GEI`vHiXKwbod!@k@pBXcnutf)aDAb#W5glk=uJHL*a8KS3Drs~N@|5&P%Jgv zz!jU_1$SRf!`Nyp$2bRG{g8LkJBJKA@_)-L3LP?T|uD7y%eo(~ZM#JIxF6 za-v{ZeUGNz-3)$+5f|zG5`F!t0K^adI8vXB7=Bb$#teP z=;=yX+ySiqCfk#etw%~yZ*=?gFdv_OSNvJpZU^_T_3T!D0?hhNbSp69Wo`obz901| zcE3@04-oLavx!E^>=66X4K)W(T9s$oAItZ4tNzUTle(u~Qs47IwgTjNo`8w~Dp{uQ z*klNTnqFkvIs8c*(J>@kaum?HNWpkXZhN7T!^f~{0(-6#m@6z&?q`Kl)Jj=>b7Wy= zqj(*3Fu2n!#0xI=J$y^gH#3?zVwB$Od%Qc|y}9@XfKd`J>o4CSv7*0Gfl~F&+1poF zNVp*PqJAr0KD*6mT>19=<(t5+TN&eQg?sE z4FcE@bhK@t7b0km7|7qnF@h%{;;U4Ri%`~=0kU8iv5}+>NHP#k0X057ms*GwUTwIl z`L|X?+uj`JsY%9tIiuQOIFD&3PAjSf8-fSeiQ_-OP8@%m#Rp=5W}1U|3Q{vMf`Cb! zM;^j!1{ladW`VjD=+?L(8-QF5=u#m6=|FqXH(fWgYvDks$KZfRv=fi;ecSj0x08)@ z2MD80`)318Xr|w~3<;H#2hBg%9A0~4g&{KN%?|JB^Mi|%($!UMuhbg8etuM{#*l;< zPS9?_5lbC#Xv_T|e`d3!->bEG@%;1{)XLY>ek0+P_>y2E)tsO+^q}!N8ic4X)qas3 z<)12&YQ-VUASM*vN#rdlPNYr=nwx2=?mW6BrKm6?hzCi3u-3kibc5ac`d%(T^56j( zeoH3UB8N&Qa?K5|8hauLf4a&1__V3xdsou&wv@3r+V%e6jJ6E%CGK%on3)xi?oBjN zaAlmNm~%-a!SQWt;a%aoH(a&%1gXvB!`>$_g|!0nkA@`rYUy)4>Fw(?dc7y=TIu{& zu-uV!k_7@S|NHB9Higtf5ngMM5p{*kv zCuk5UvW*WT;%!;qKxX0IlSL~}`zsBc9bGA^cQC5U9l#|fNj`n}(ypeN94l9du1Sb2 zeEYB?q0p^1s_4uy^@!!)f|8s67W~)!r~0lN5dw&I07m^5?~p`RF1?($9eygz0JWVrYeyyjB_E_8V1Th`N8?&dFilKu0beP)H8FFtGsf^yAJ#DXTsu$?%VgaS2 zhIIi$vC|kms)YjvAEL>8YT5fM-iHFW+~N%i3LN@NXD7h6EP_nxt3`B)VIQECZpQPW zl7tXBw=tGJtO0x3h?bC+^;hJ@=hB}E(|-1SlHDowklssqfvvN4CA)R-80EmN>;!b^jqe)=!}V<8T6 zJzz&zdpE(d$Y=-G+1I~5?_Gi^31&w2c)TV058yXf#0P_qaB_Axiyc=FZ!48xq!BMS zNP_g!Z2vZyZRX5upNUQZsx=e29bjusL#Q>Y!?tGM&zh<$PQN+0czfB?<&D#N3@jg` zY-b+F4dJg8FQ-|yG>~`E{aqwS?I+iRJrw_NEKvva=gDpQ&)-k?zE3c%ALJc#GbT9i zeJn`A_t-{Q&dF79V0*uQb@uulY(q6)i?TT#SQ~s}bCAthL)qXP?R+=Q8=3dM(aw3( zdP?UlE?A3f0G7NI0;{MwPe8h7AOnl+i(^37A>pxL#xc zak8w)YEaj8g>pN5c!heoF{^HIgUHIF486312*4M^@vJZp6Ohvzv7oJ2wzybDcUy<+ zp+MvnNDbVqR3J5@Jn0CBqOG*W&b)AITk*oJZN=-J#!G;M0_Z7qk)$=j>JgDL$p;e> zx$8y<@dx02@j&j-N-fRw1feAs8*ylh92d~KmO0VI^8bZ&M_?f>( z>DiY^WK+Uewb?shqbq^4XegUqCGVpHXhl|$QLNW*f8CQNkiopYV>pS9SicF5*S#gx zNbedNVn|dH(}pT-*oYPZ;@Q#QRzg*YfZC)~SLJ03Ri8+RL8ZnW1oHza=)PlwCg*mY zS_n$tCmY>RFH01Xz!&ikfBA2F2WU-hMntD@g5-GV%4QO|j2L2211`yR2&0*DVyvGOA=xxe4K^cVlcswX|1}i(b|`tT224Yd9C#I7A2* zr6p*o!B5qeJ104pva|OX*&X~x?Mg7|S~ZIo=WMBu$`Fd57KSC$5G`*nAUt~U_ON$# za`f``8@RjQ^<*3L`n%pMNUh>g-a=^mOO|&uk42|_L-VPvS?%M&J+{oJAouvVaO&6l z?CRG)U-8VJ|HZp^2WQUfx6d!mkJ>zQlVqUY2G`v4y0EFKhMM1@-Wm{9!xH)4^VVRe znosCXEgFLJItT5N3o6(UNpKRRXAV%3N#x{amRknAE*Cu0fd#rK+XR*>!pjusZzoN% z$#^akD=uP-nSq{y2;3Z^fN(_X`(U(QOw+F&pkaqeJ#l&sUO^J zOh_HRdV6vO-pZGYzuqH=a{9XR`RL8-*QiRYd~hH9(k_g z8MzJALgbnNQYKNBnJW)Jo@NWX2MJ`S&!54^);swD(0i?)%beBo7wd1N<4(>p}|_l^455W zwAqtJYw-|pBuarD<>3g9rs&B;i*n;8?<7*7gyNKTeG`{?Q}?m-z(H}8!i4}oK$1bho)rC@J}pr1La?c1?f_UIz-Gz@@2l91U4`}`x75fdrsaVZ5x@H>%MwJbUJFdi1qvbe8F3nA zktfd4u@IuL6gUcCl58=Yf{iIJJ)JYg#3&P*hdxZYE0-3A4pt{BGI$0rAi|FR@LOd< z_gemjC9AANP^5{x3w7tBX|u41MvE({=B1)k^8=!GbS7!*NT3Y6yrP^`O|zFL0b2HP znNe>(8WI5^*7rRh*8z3!*-Z|c)@3gxz6YbCLZbgbruml{qJ=kLQ6&xvg4T-PROwl( z-&(>m%O|#Tf4Sm8U0oe=dyTX9&#Eg=TL5SMd9Bf#lj(;6Ub7%Y;6Q5p5J4gP8ik9B#u+uPFtvk@s*8YK3t4C5p{U|_KL@~)b~wm;CopE+u5O7 z&*>htalI_`Ax{idTN`N%RJo#n3`}IE=^_8+86h$tCb7W@oH85H9e7KKu3PI%<_j`9ZAm2N zwr1c%$J^F!aLTr3gHyI3iH|@h5kN#HjZ>uEEeC3H0C0nCEC^z7-NhLqfT(lXV z8G-FEf&w}=gU~HXP^X%q$QDWW0Ob8Xrg3ljeF6q?RqkaJ43($8UQt z@TG>~l{D^>mYhJn9i(W$rXfwIN(exgP1oxA5XqTrMYFN#4*DRMS}n^xDAA~|=}Det zBVaSnD5^c}oqT`CJR1_CBzq<|wB%Q{*H35aUJ!lt6L(cDse%zkiiu8c@ESL`TY$$| z)qq?qiTn!^W>n5*vQ-feeIgtzS=kAdkHoKV!%1wXVc@FKQgKP=bfey=+gG^mC_fgzynurfDXYXW;R|B7R91Be=$2&$BUMU*-3{&% z`EB0KHz(iqo}V0Cy}dZO1flq=13Y0z+HL;!;NlcQRlOGoxtzZ`_-^M!k4;t?Z?&W2 zXd%H`@Ss-*?B_j6fN~EkrhA6x4>aTC0bH@FfB4J4shUSw-dtF<+5%pE%7!0TkAD?S z;S(=T*IUm^>;*ySmjhLf5zsB|@~{X3qWB-Xcof}9O8Vfm_Xf|@2phR)thq)VG*jQv zLi&BIe^wo{4Vb6w-9|I{Ymne(vC}5n6rCuo05+!UXGq6Glmah@Z4}HU*YaJoNQ@@C z2-1BukGduH@w^n=hfkjj zSpou>I$|~nu5qB^6eQFahyfg ze9leBo2M7m$cX@i*yL<~A$Bu@>Ec(7st(k%aMb^fAt}5_k zVh&FC&c~kwkbXX%7VFu8UN@ih-s%7b77ADvJf%gi3RI2^5lu+1vWvpGyG4r`*-Lc< zi=<&(qiK~m4&_t%y{t16gr{+Wo_f){K=qO^c;VjfP0we6=KSG(kKzDQ8gsH^cfibQQf?hS|+|5Fuv-83e54bMIYx=5tJzw zj=^oglvYHM#zn2f(fICOE;?1!bg43RX$?^FJ9%(q6)fUVGMs#x&R@J%I4Bj|Qu9Yo zfup8~HMpkoC0iUit+cKoSj!fyinJmqajJ{J`$GhS_eHoypHdY2iSh-W znEDHHS5eYsOMZ)1{LwF^f%sg)jPMFi`P+k6c(TvACD>Uwo+Z0V0b)~cBR<5Cu)m(e z&zH5ZaF;#f7w{I;54g_?W=&q6SnXM%B!?B}FWvD8UaEuX&91y3;7;uJUSLDQe_zf= z*WzL9VzI@uKgCP;oX_#~T>d_Q!!by9@%~1?(ZJ zhjb+jTd0@ID&0eTFs7ZLSLzAdQHi9}Lk!l{cl!DI6$Xu;-aH4o?fD4?;!r}+Y~zF_ zk??1h2BCCX)C+Fu!ex9~`Mm6ztPt9k_f&BshAkHVXUN^;9^%ZbOO@A_epvO5(laEK zlTU`;&B!{T3df%7rFF4>70+!c`c)IGoO}uL~U^rS`#i&szT~A=it7LSmNy;HK{1z1mIpZxeFfd2JhFT-v?Na&4(N()Eg}#5mqqNP1FE%t(v~%F zE2pnh5<1ccYwC9yU435fxR9l*l)x?LM|@DDY{IuP*0MQiqFZX#N0k;@fiCsv&U=aO zwBuYFkMB+gnb;sMb`nUpIAIz%PU6HaSd>xU?m|)>Kvu``z|oVUC?wAu6sFi5GaDouPL=cq(%%f_@QRT@Y}^4L}gd{|sg)1ytF< zvGVQd;e~KFHA|~DhUmTwK*b6kB!BwYF?(9O(`xO$x1F{7-gegRchB)S;NS^dkO>(O zz=mK<2q-7`O#=uSIbr64UCOmUB$Redd+GlIvqc5)H@ zWbHq0Yq#62-QLpL?Jcdny9Pp-d#Q<{1P6KtP5|sOq}!1cNBx42d`}dIInq>KYP()) znMsV=KXC2`z%|FI3Enm5cVg`|-RqyM{r79_9ZDEMMU1FD;zOh*Mlx_jpSV)R*!X*s z?zQQy$+G#f4mM@kM!hR(u%2ndm7uIVlt>7omOzQ7lBQDn01~ZJLc;SRT7?YN2OaEX z)!ax2ZW_d})io9W#ZuD*+@~O<;P#Ue z+_Gy!?Bkld_TAon#!#NFKZH2JI~CeSm;;4$ay{AA5&s53S&?KFdZwAalVO%)aVgw__Ej+=rQL|f*o(vp9 z6&yMV!{J(J3r&e5FL4PKLsv2Jc%o!yKY^YKJ!582ZCufLQD)^`AFF^ybvR3a-4y$; zL^Ko@(#!p7)MOyy#075g0|;C+^s^6!Uj|FLb;mg9BGJWeGfE{aa6R}~nOHQJ@Ea%i2PIMDr*KWU&;}fiYS1$&QtI=c?$lDUkoW*7azk2r5FxiSxF3$E*D&~h7XaTu)$Mk z&`6Z85RN~4!*_K}KM^Iz$8TTpJ$rC;1s%nMi?fA;uw0YSI0Fy$4AvIV1g-3?p7j8` zvL0YpR%g3{2Wnw@Vz4xuDl1;WwqFd`2f4>4)L`>Ii3J{7kikUFi65Ehftpb zju0mWfe8q53<$1zK^i-ef%lW*+g1YcV))If)#d%fnYuJk zM(Wz-VKv5+Udw@!*!YU#3&_<>nS3hb9oU7hL?nawmG$DRl5BzpiIgmoLCC~NW(l-OSLDT!&cueZoS?>u+ zI(wt}ZA^w$>>imR(fprm`L!Z`Q4>XsOFp=sPm~3yq@?tpC^&tPbd`#-6Ul5Pl3HWd zj>qV<9aF2ygTIT29Z(1xeG%sOr)8&m6`NCor+R=pf2Ou17@+&Ii7Z1wQ+e(a?5yZn zGk=GwD(pqzglA88NeTH0smFwjn5iBy!fueZsSdqrP5=jGoBWxKcqERDRIZL0q{Wz4 z|JJ2Kf*KqlA;^p~&jA;0kBigFUg7Zac+cJgBoD!a7DC0MBN}US%};G?iffPUnj})s zE*WzQOGWQa$j&VxLjQRH=>SDXKiN)s6G9Phb0l7EoB~icQiEX1K3C#yjtoh)H?d}Qts6BCzr7|K6 zl50769zYr`_nicq#n`ty890Y9xsD4y0Lb$|U?mGc7YX4N8{vHCi%%>V{!IIKGqLNv z&v@`dAAGg&-5^CWS8-S3qJSlxa{QI|x#_4{Alsp4oz<#n;Y#4bZ1FzXXzyUAW01WA z0%AsVjV{%dmOawv0}3>tNLnJf`s^YksgUD5jlPY^;H0-IW5H^T4Oi_wIXgJGe5yj- zP^c3$g=)A8j7JRsPhm71SEmjc5RlnK1>ea%4Ht8PoYlP@N$~^kil`;=lLY4_%-J6` zB1pO-r4M8QFM5efN)k5jgfJX(iNkv+90k`Tdoi@Qtu_WPcdY@23aYmwgz${@w zB}kVrrx+o1p!kOanIh%+CX`eoJctl=qI&Oli-Pk+d{MMO=&i)giEBP$VfippH@yoC zDX>+03Jyeno!{|%0PRr7K!pSJjyS-nlKWtXM+y)ng*BK59eW0jcmPB$TNFeZh6ld& zG-1twjMq$$ja*)4Xt&5Rh|0i6ngzrAEL6x;3&6nV!2Vs&huS~K@u<=m?!}o3#gA0- zVyZ_KDWoYA8G>T&nTtGu6w6FCPb0@-M&!~(QC#qB=Wrt8#^3bc^|fnxaZkAAMgOed zdxFKleyYOqF=js(F=U|fvr1h5Sz)+xx57q`qX2If3s92_pH3jSi+ey6A5JF?5bc$9 z0W(!Re>oOai1XrHOm5)df0ng}n$CWAEu3psO@4bf zQo^9k2WW8tHoYv~w-G^aKCwGXS&k@|nmIcHD#o)QMrmft`YB*hur(w}C42 zK%SH|y#F_i*B6P18m*D`;Gy;qJk%b#Q$AZHg~!$n^pn_pg@Kv5Q1?x93#G|mj89{a zI*my7cm}Y281E1);Z|k={A74`YS-++_icA8dvG?{1N%`wQ}%{<3e0cr#A{cYdO5H`0Vzz7PAAjv%!;LhAcL3m=C2mr(o(Ob3$ ztR05=i1y&r6ybif_VD1n1>d8KH{ixi#`57SyA5(c80&W%?wZ|(xKCB*V|j~lSuKlH z6@_5uPBjX?(!*Z0i_5!=Z*?hb$J3uN#(g*jo&wGWL$-%xWxCN$6n`ixA`8a~+5R~~ z#f*O2dzUlRJE>FXE?%;Tg5}G4oAVB^41CWf22#*kxp@$1VF$vfwQZCb7F1QyrB?^* ziguL63_(i1I8#-A=n}z{&>~G86jTZ z);gTYkHq1H{sb9P)+^+!IJ^;SR12EE*W(e&*|LKvIqG-yr`HVAFCEqtDzjVJSsw5f zTwd)S@;a=y&>fl(N2WuyiwMXUNL7;uCGjDgWBH){bnq|rhodZaJ!Fg_!DB&w)=F$A zP0h%R>yQ_`U&+|{$d2Yr8ER`+0!6}M9Sytw}Ib=ed4sJK4L@kP57U#J*mCKbNG#^Pl-P$liG z-i21j?N}j!75bG4aHhoJ6G8h=NKz2=L`6N%V8y5~U+C`WC~6U3WN=e1jVP<)OckAA?pw7Ckl#cBW5Hj z&th?94gTXt=^9`aHjrOvi0ZCSGy_s33P?xu~lcsHwZn=q?Hifm~l( zj$ElU=qcj&7$Y{SSr)KtqUD9y&r%nvc6tN{9kt)1s(IoXz_%bTFe%ErM=Z*|%qDm9 zp{jj7nPeXZyhoKNQkKbb0&bytwu{8WDBZh7{Xd3Cquimh^$X4mOc=nK;ZO_wN?t+B zKl94^;e?4#2D0AC9!QOuT7U*>xQaCa_AZY8R5J*(NAlfm)V`7sgDClvql(lY||pK&T^N<5`)DPp3kB4 zUXXU-u+z~;Dormvk(HcBuXu@(-$=^SagGe;LX^GIx>9q+Av|J1i+!jvX?-`{>~ zr$YRI3yA7f<%z`5h?4hsqB)3?$>2`f(c3+ob68MYfTE=rBils=X*D1Gra16nEXkJz zBj1{u{^nz9WgyCxU`b4WFy62DM;o^92Ru*hm-Pu9cI@?&w%nwzoZbP!73}3wD2HuR z*ta*4ADc#pGLCwa%_PWXD=q1&1NUAWxaOTo%hBD*NK+s6{CO6jTEj{6#P)5=0@x=< zWpnJ4eH5Es6jZu7R3VTCLS!{c@*KE}AOQ+LUD}lZAml^val37a*P)DK84QwUU}erc z>_FVi@*zj*J3cl;ffIXq1_7RR+mhxI&gR<42M~~MTNu1#ELOX**jpKky_K6T-4-9%OEAGxnZksC)O3NkmG?;$|^TlO%UMvu-Q^`vs40 zEasntv6$_~Vr^wC)>g*S9X=QUS{D00@`xZ(HG=-5s!ek{tciGYJ##oF$5@T5(`Jgx1M`ZY#|BIz~BmFd+sPIS2kE`X^Znd6n ztMY5M2L`7XSI508)Q~Jx7?y(~M?x_so*eZ_o|W5qRRf!&>D>j9+(;nqNamn=IM2D& z0=^zG>0Mr@UyqBl++{xuRzf4+6&*l%Z+1t7)UB2R;r>Gr;ezM1Aen-EhGuy}M54G^ zmibQACK@2SC8VB=A&DvFUuHM=i`fv% zSHBDAA12|oV%UZ;{khzMm}E9ebKAFgmi{NHZx&FyF)kKAs#XXjZ=U&YcgST5oCyUjS zRcD3Sf_PO)S~2t~;9k*{Dh-mq7fZ2v5L8TcDr*eB9w=z|Cv|>yTX|W*6G4os@|sc_ zU5j_sGsTk`4R1>l5uxBHRxRR;F>6Cbsx4&I#S+YAW0le3U^8nWJ&87Ii|S8ZAqNZR zVE?RefjUGjR!`e9Zqd-dIz+)S5060jT!#`Y+KF$zzUEhKz70B1=>TB&BfR9Nm7cT% zXU)w8Z^3#X!IgflHUbCq#hT%@ZFtHSKkt3mhNEmhq4QQJfw&K1B_?X$DBEKAsR;<3 z@7aDz9z-L+b{GT~Sqzom!0Cwr8LF??z@#*vLlxin_ zy}AO~v#wBIE=*sl-r3a&;rEBor-WiC7hq(B`SqIsXF!<0i({<;+E~i|_7W$ZzBxO& zx;QR0cPZ0fVYD~lr_*kPVVL497W_btUV2&y?OL^gIDJr`RQr+aX$^485cjo?@i{n z>{OP5)dMQeUCmV8;qrq*D%@7FP@H6Gu+gvT;b;g{QaY^Pxi zWl7s~LO1av>`9Q!MS>2*9H74PlO(iaP+Um zYwcTSWr5-QB(_m+1}*_70k1@Wol$}}VD9-If{1{|`fHLe4<(m^_7SRuZ|`N0r^O1n z^kpp#V#rp?TT@PBwHNPaQ~S8SDjo}TMFmtAU^#?Y8J_<%Rth>q2J-Y3n%TCqUu7hc zBD#}V00i<7Y11{?T9ks3e?>f75u9QWN9n2Q8k2>{l?we!bLuom0WqSz{fcxx|KSI% z64}?cd0Oy}$i$|MuVU%-{a+^^nZn z-lwmpGt}86KeUjCntgw>=(f{kP4e3nx7*1>mrVV=+0GbRKd^hv1gfjR27~&CC`)bM zMcqUKj~Gw_4}^SR4>hp_p~TQLlN6$|hLZ(g4-H8$o|!(CWvl+L;W&=(ZL<(w{~~pwxc<$uCrlvosI6V1!xjrt6`C} z72KNGp!;bI@J|emA1?)^d!B`E1|Cf?CC13Y1J;2}i-+m}kT_+K`>oH0VTJA^&W2E< zn86pAjdr+1*V!<;&PI14p2PyLDNAir5G>&ZYz3KxH!#pwnTg;awum7#BtV{pd9W-8 zT&Drvi8(Y@a*$LeL2Wi{NJNK^IvYs&*8(M5&&I}*47~vVW_*Lq4x-&^7?Ib+E4{4G zSa;AWPyrne)~YevM(Z28Xmj~3eVAckAFw>Ma?onFNYYvOQ1M1qUfH6>rt@i)#w0zc zSo`^c%?2vrrr@mD1B{raxg{1H-3IcvkZsej`^Du&vI>&7*3V{Xnv*jZQ8kek z6Cpg5pkyMmCiFp~MtNUmmN6thjK{Y{3K5%3A}$~;AdlR%O5}MZZ2eSI)xFA z4`s$PR_X{`MY)N>L%FL`)YyVf7Qp1Ci?A4Wep|bYY5n%EO$&QsSZ*e@+)RCsrb3G3 z?&dL0`@il#6u-hyy@Qev{)IIAC5}1@`YLA~&r`IcRG9{GYmeWP_L)8prKnNAD zWre@2j)8b3PP2s~)v9j|q^(-r|zPG(Rt?D8-V zqw9sHpgK>UBZ3V+$Z3Mlr>dU(`s(C(Z#ugl>d}%};(c+&qkI8kC&(#(U>~(|E-Ul( z@JkCXYofws&t$cV1hOv&9%IP%l%k?Y8w=~I)+MK|1hV3q#8xdV)5^e_hVjn?s8t6P z)T|PlqmTBkrsZ7)87!952jc#}?AD5J-#vN#?k~N5J8Z3*Pu((6-qJ`1n7_HP_}%WZ z#cFfu*Pyf4IWF(l;I!AD*Im!aj}nkLI`HXzP&eg1y!a@AGE#zFRu-FX%mf5-Y*q^X zM}&+JG%|A^R6wA0F^y#pGXyx+H6O8Hy(tst`sfLEBOrRrJP0@;TW%p$;+jw$G()c` z_po8O9y22Uxe)H{Lx-De!Fr$#Z2{yC0?-^-vFk(d4xTN(>F(b=}9dp&C2XAG-~;26`T7L*@f*sQXSA^uU=F z#a57mBAS30(2d!i9Yn|`LdXCbpwM8jGk^hd%SpNMwNPv1hGyVl?FyHpK!NRph3;`} zXlcv%M`uIpH{noE>ceH4Pse?|+f;Rx!~2!3c&9^G*%QFmWv>aDhkKU2VME?~a;`VR z&-DeL*7xX7E>Di$UcPzSD~>F>w&W7ip%YhK>mn>hy1r4bUvQ}zam^w3jgs}Ae&&jp zd9kq)Pj)F>AiK%2;ubA-$2yvx4+PFh1_1b;%0m^Gx9Bg5leybd@&=i1ff!A0d9{Zg zDWECK;$;V=wGfCqKh4$7K^>430uhnA$Uw2=s!L?5Bh?NLQ5%aY3+^vI4YW49YDoma zMe88a?lEsODt%@7ea20I0(iqiz7>cz3*eIK@fVqG% z7F5ZE{L9tw-K=(R#WuR(&UF88rD{gDW`YyeTK474MCos^AsXohr;Dxv0G@IK4ZRHt z_c7%SFf+^3c^-)_w3*N+Pcj8Gp3OBEnoOu{iqR*q!hBSeXxDQk;Gw1o{ZMM}+*d8C z-FY7>Py*aIHA+SbI$j<&NLaw0DcQbzeWq~!E=?5Q9;KnzjPe;P4ZQC`SbVO}dA^uj zUp>^Uhc~`Ay{1DfXK{@6y%{zg<9Pa*(`v=M-1b0)m=v_TLL5?Jxr;GS#R=;9;qB;k zW%)**EN+fv=9)=mIKLfDpOynaertqv^fIRA{|P4XzPB&Td-GfT!93zqOwL=8$HeF; zxqJP%O{0(cVUr!IEHVhLP;pUeFuUfCG` zpg-u#v66t_lu@CXViLc@8&9TQ324gUQcxhr>17G`sQNBE2kEZLe9}54_M+nuQ}sRW zYZ6^&N(GZY4@UAh-(E+>A&X|q(2#y&iUG!G#aU4&Cih=HmAq-aGKmN%?_jY+FRgpA zyb81$WqZaJWm8n2VU%d)E%usq6vb0fsqH{5&tRz3aHfAQl~#yfo-baHJInE3t!fD> zk?Bl5y}DcKH7BvW>fzTCT}<^OjmYmVIXy4}MZi@KnDQKQ4Uu03w_)iR`$uxGPgT_D z3@g-FYHM2WjiFN1LrrfAzMR`de5~UAlrJB|cJ)hURxK?;WMKfZ6xGgBC#F)evG|N9 zhE)^_8x*nfVnQLOWFgh6L{Y))GgkFw2oc5Ps-KW0CKp;6VvOKYaLH#yoPM{{enq;p zCq-k?&fySa{^1_=lwT`XY@)eH`Z2!lPZddd5kHadaL@aA!V%re_S?_dIfDm=`k4`% zR?gJ>g6HJxIQsP4|6FhU`_Ue_fj;g1W*q6C_Uk9^@BDHcOGu25%wBn__9CM}9``@M zLqSd5KEMkMQ10W5_it#Q$gJJm{T@PLT{`KxuIk-}sM%$fyuBRnYj=6Ca7y>K4WV^t zXUlk6mlhX@uA`R+Ku5lM^Flyft92=A-g_iL0&tC#`EL&GrYTq%IVLFDDRmEpf` zV8jNHyS4}I%+xpI6iS9>8e2&Oxprt7Mo1km?WH}-4g&9y>AKZ^u1e1g+(!)Apv(u5 zI0?WnkJyoyW6z(tR%$lMw{#k}FT&YvwZ>QM&$1Ewvn-^0%-4ZjkBJD0Wn}^Wbs@-v zsILwF7CQpzC@=*k@aBlD08Rj0V3tL$hcBW4(H6rcS9ZldsB#J6-mUDzUTYtAEgbMb z`)CB5H^9qV;Nc5c_wUcpwhaY$GZgEy%&dD@7>aJF=<#h(ljf%B#CZy;Tcik(H}Fv4 zK$#}!K|_)kr4Cjo%%^eggFGI!J`jOk4(TDHhSG;%8#A-k>OZeUowhKuu5mNu`TN5; z{IlV0pQVf(M-?V(`ehYJ>QUdTi8~hAb#g#h`ZoHa=8AxNuJ-&`NEaD{G%plw` z9H)vj@kll)mYok&VlZNu)c8*E#xAVE(l6iv_!AR zKik=PbNI`XqboeyJEz3X3gTobdPisclin~Cw>c{C|IBP(E1}OKa;Fb1uh;J#vP624 z5k|xqj1&_n3s-X-;je%T-r39pe7}A5C-P~$`hytXj$iP$vSnjmE z+*J2?Jo;)@&Vxi)Dp3Pkm+tKp9e8nYc7mMywf`&K1K5H$!rFg;-U=oJjF0iEqfQ5-g)J%;lJKCMciv z|Ficl&2eN|n%Ew{BC5xxlkAEJ`1`@^>?{xfNmK$P3jmXqy}|esh(sU)6%T^Qg{;l= z*vL#;jLc*jlV(OT(?$y|^fQeEOuA0EdB|jFrIGr01VJr-;q@ zI}i^Y?H%tNusP5mfQ8_W3sQ)0ljs;r)BG)iy=Dk+CpX2A3G*g%_Yzenx*-F{{JImi zzH`t`av_g!(GbRoc4oZW(f--aK8In{dNRjIOL!!vR;IT%qQ=HHxQCAbH@-d>1Xdop z%;{Mf6UA^6o^obM6%vsVJ_TAWhrP@^j{cbo$MVy(oL!3Vn3**N6vy^nupms)$l=F8 zNKRfMHq%$o7uwy4{iT8}A^A2en6xs{S%l2<&Fk~iH~5$k08qfGxkOrv4@XgmFs}?# z2;~pt*IUko7~_^T)22$_j0SdZ|J4!f{^amA=$Q9jAK)w4J2*Uj{$}s=;H<}Q%b&e6 zj_7KHapvHKp#GWllE=UF5d+CIW3mZhpCx^YRZ?L*{LSm*qt}PMH>bTfFJ90;`FSy0 z*CY|azmUT~^z~#WmJKiP=^(h9{8_(`&)$zt{&6?Ewtnh&si2|v zV$;DNXA@1+I~@Mqp))k8z2@`3OY&!J`ST`s?n2$t;%%?HWt*%ygq1^3{i6gIQW7b| zxPZUV0%!%ezKIhY^p(v(1FGLod?SHauNH=y4_r3>+pP+<`WCbVbng*Q`niugA^Sz)F35zm^(LfkZ$jGkCZu&YA(Yfv zM(7)WfjO83)9`??@O;ez7{gAX`bVcTa4$rFhe15c$0-b`3zupTG|)5FO$dq=mi@RU zw5sj)H*G>FMOkY@w!XCu(Jfor5Z%(fgewMJ2asP@V%jJTbtBS?P7qKEbRT5pG2%!j zm|o-90=O##xJRe|V?}}P!Ga>UYD10<2|@R|EHdy8 zo-2v##q?PTrz4d%)bFJlFg6;=y9YNp>OoC#Ceh7*Dm|Bz`%mE2RwcsevY3_i$`Pgf zzNf>TQuVXyN`U|>VMnA>qL@Q}&4olr*PKtzS=;C)QLc$)Wj71rgEQ2brLyTX9DC>4 zllhSuu_xJ8s^Co1c|McHa-rJd2}l&RC@05jBPB1{=ZPirq8Q+EPN<59u=GJ}aFHzs z(>D5vmWw*qeAD`tAfURN4c#DLKL!|s)pOSBp($^pI5MqhMsGTdbW)7o61%%@{l3uG z@JF=Y)SbHslB!M|Mqv~&Gk*OV;6sGs%|y>wKUwCzHX2Hj%qZ=Z4}%Y8s}QBDv2O5ew8tf;I2gTGFB z+1!8oZujNecT~8Az@2gpgwFStjBUPPVb?pv-iogEv(05jgIRR0RD^oKUI>a=-yO;0 zW5CBKYBNCBZ_(c=g|fNm3-xBRYj#fnf4aTOC~677h`KkzfuZDX!jaV8Sr$NKx}e!~ zD#LVkHTr3Jy(Ds1iVOBmq)YU6U_z(=b-H)b=hx^<@6h*!Iy`?p< zwnvxsPJ_^i!)iZS9CMS^)5I|tf_X18RYu-Kqo#&X3SOD9gD~ z=k?wbgo;OLUceG8azP@YL}5LZ;!h-Ii!%ky$RE$%yk^@+G;%Zar?mZJcV!$HR z3`ta<4ER;{h#;3j*-V!^rOH$$>E+qPJix|dWslABKQ+!O82LSgaaVVc-ji$wCGMF| zfKvc^ZL>&ghcK+JwaX{A`FriV5D6o;xqs{L>JA0L zp(KRrDkNCJHibMZfY&}q_+pSSCYobfsS%`}<0UwGM^OlO3+|U?7=doU?=!33Akk|A zBl74h994x8z*8XY2!Ku)L;J+gV=VyTVFH*6=pMA3DzNM@b$9L7`2;^h%r?Tt94tTb zQ#pio0iJIzM81eJn)4!>+vVz&Tx?a!bk!R}7}#m^##FXY+O6ZlMYN5*4y<2Xahi9m z#~q_3jNBnr&nSs2Xu@Z`#>xiP^kjuGf!MBYiR@~xxHRW_$0Lf?1=R?4!z=hZLuSe1 z8+Y<1kouyo38s@f6Nqx%G*xa{j#}havp)4@&q2eB={&zUe?b*tl^0_&wht(U&T@bG zwR4Ui^M+dPUD3Tf7|Rb=Ri^u+1vdGy>H>p(i8w6Rd~azhrxD9S+68M^{rcsY|J`GP zy77p|I*v=Pmqn+u*@6d&GUBl`DK}K^{!T`ghl?l`R!5AJ&lxbgtr_YB-&UCml~^UG zYeEpv)j4TvFOzIn23bxs0AW3n8I!40KyIZ%e>3`+wDyv8+H=7`74ufkMCM17Z{>f> zh_@#`*A}4?T$FLc2)e(d06(1LmL0luY3GePW5So^1(}rDDB8^@ZjgP0gVJKqfmgqC zYrBKZbo(9Ht+h{Nx7OAKXu7Pt7wwyO_ZB*DLH@`_Hq-Jgp!Z0qy;i;HIJRSZkC{}>cb4)_PN7jVfV7$6>K+^o zu>C?dAV#l)BruzH@_nHAPxtyKd#@pPyef;ljaYKO>oh!c<0qZOLGc%a1wIx-^UdEG z-rq6KRsd7t{Z z_@DZ<_*>8y!nlFrgPNXg*|DGC-(ei5iQ`62Vn+b=p`zIU?;PUx2~@_QIiG^3%d&kd z@h!9Vu~=Xe5G3$AyFCWXV?qg{u>-RqOWAQ%qWAU~~7yzgndK!p5W02QHv1>xr z1vniK@pm9}EYtU?_}fFlw*ei15cyg^fi9U%>4LbL+CsL$v`xc$+>b>EH^5)zW69OL zzI-$f`e-&22;PiYstB>%5>;U7kE_`Rvt40KDVEx8=i3Hg@%P{(1F2ff*caioX9;#W z?6R-WpQH!0k?1(B*c?jMcR|+I&fHMDvO7ydq=Uyd>)ghHXMHU+nXG;uYHY-v7F zM5u2=H)0C{WtIl$W~e#X+^~H)UD-gIw*vm_$>G7#-o^Jv2ZwJ~6dqUZsIL>BXA>}u zHJd==tI!^>X=m{tZD;8WrOijua-9HB(0A(<)2i28nKE@c-VgTi&BQkQU)?WyK25Vp%6;l>kBDj|#JYs#{Q z)@FK0t&@hP*1A%wCR*yUD;9Dg7SdLb-EeL9h$%(D?zR44`G8r&NRu z4L6kXyHIz8yrUVr(A`WSu>gKY!_!d#9cb0{8tRSO%4+Scqg?aWuGaatS6Z!I!vWEn zgDu3@5r0Cy13d@LAmE?|+^VF#5PKUYJ*}146T5}qaoXt8jPi0TK_CE*g6ixttwO%07E?QMZ?9dN-I30PnSSqn{-sWGg8 z0GkShm(^rv+XnMT)ta3Nmoy%)Y0p4f%db6W)Fk5-auPJ^9zid%%s&&9kLHakXSq(q z4GH1pCD1RyCB5JcrFTdqf!O6rW6h}368smEjWY%TPv(j@yI-bz7ozqodH=JMJT>fL znN9ABO%7;QcQwJCUmv z<;~7{dj{aca(oTVA~jJR(`VWm!epbBR=a%`wXNs9JNUwKqcBZ@tVq((Npzea`A`cO z1_YiE?Tj=Ri7#*<2R7oMR*I5!ErkA4h!S_O*3{9KpW6rzPuo?ey=Y0*C!PfJx zZ$0nZP^P0U3hH!lfHkPl8*C}{E_r}((@?$$Zeef*>R8K=7`lLZCpuHve7|34`*xw#I$a zxh%2;?<&or5FUB9Xt`2^lwXiNc$KE0xr;;HSUKk(G2Lq-Mfh+~ax^VmmJcL#z_mpD z$9r~>lv=dnLxq;kWXWjIt0}8r?A@;iD8CjEgVaDQW zhe(S=&5Ct+MoNZTN|=NWnH9w01aka=$aKmRgSS{fPgiVRjiBZT68G9}Hcx|mN;{+_+wb25V>CB7vK{8w2Bi&d7GikQX z3*A_QENQ)`d!hssb-82~c^kO{#2&_@I)%SywD zP>LF|iSiS$Z+qtw@h8b}p(&4QV)2c-oQySIltjuDu4Y9Lby<~Njc2dNpE7D{P5 ziwGROrjY01Iio_dHB)_Um6Ffn(`?AXVrMnMNLt-J5sk96 zZA?L58+Foc+Bs?>!VfTo&pp`>q3mdxIdA*P?v5fOZB-jgvW{!Q!8J{2{ze`}DP!m} z;D5l60IJ6jNl`e>C_hKy1s^>KApymVV%w`vwr07HJ=rx!>90N6n?0mkFp|$`N@mkp z;TM(vS_@_|v#!y~#AvZzZ41kjG*^pbiIb9LAj*A&!#BwbIhCIhp>mTb;-orS7W`zI!b;eb>)+8k{+@WXM0xX1g*p4+sbDvmx})q>StD z!MXo6>Tgiye%0!=3O;g6zqQ`gPR?rmrY)7-R&G~(1@k81w!O)uJ-KFVtlzP^DX+DK z3amBzUwMVRu-5E))eF0agUm>ep`-?F@6ZUrPZBsWpbut@qqhwjs}#G5X=$cr2NC!Y zbPymKjtA&N=*P&}RBLe{pM|PhZ=2N2=BR*GuP%tl<+d!yse4)y!uN6z!2}f}{^nSQ z=CuQ+>*MsZa~eMyT)$m8%j{}QW>;hCF1v;jY!~sP5O{ivyr(p=5(JR_(21?oM%{`I z_e8Ti)Tp?K-+(B`C1EUGvyleGgjS8oL#j~IAJ>?SazXJIW6F$4+fs&Y#YT*-#?<}z z4c0u{g#3d>>0SfAH!`t-hLcw45W%VDMF#37LJy8Qz>HM(5dzWZ;lv)8x)u8sV*=^9 ztDD+Z#$;|*elXnMwlS^u`$yBE+K_+z3-<2GPzCzRr-1d>H1RWO)_UewXNxm|c%AYr zma|q2Gn~IwWeY;NI+_t9Pao8xr02sbSL8JwNNsHKAKT3e1=$S)&`2qlM#KZJEh0woh=

MY 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

$0iGszNZ+AZuT^5Ijq5|4yIP{u&%P)ms% z5#0n%L6@&zeqsY?6WaVN4d75;J1=I`Am(>n-gdRUjf>WbcgUUaxY`A;Wk`5-)R@f@ z&T120p@4Gc`I=r;JPWA~Xd!ELWkR)-+->pE-ftK@#P0zRDdi4Pd;rq*TR|bsrseoT zz(-Xp5$>0g4-~hRVcF-Y;J}1&w(4{o(j)h0ab`?2!s zGV?Sg=W{#|D&K|iK56az_UOME4xT8MUp+6x0?KyfcRj1(znLaL`SG!$<`LeU*+gk% zhI4e|u^UL;YQ>@^3Lx48HqbPV6l5O$Ddo3oY4R1SFOB(Iu_0+F z*m@^@g2M}C`j+-{x9I&o(rM)q&71geZfo=qnigu9gETF=m#%kl@6Xoyklr7z#6|Zx zPAz)wUF5~VZp4u!kHUcDu>}VOG$(P8`e`1!X%-Z@gE(1eQ>(~*TJjrYt)M-ED1KNK z3%wH8KU{P?6?oWLX~RbVYesCNWDr}v=ekJ}#JdOrI_bc9xGz zg;iQXjuNUJI;a~^>MLv~m9(r4fvle-Y33r`M;w?^*hMA=jM8S01s(!_cD7W@%?C`Rm3q<~>_28TC z_IaVbLG5d8a>!~)1?T})7FyK82DOXa^tTm%;(DMp17bCl)}0flddH|EVN;wKWd_CP zsR(rqO|6VUYs6*sZ<6RZNU-SY46ra_E=`)9zK&ILee)Cx?YB$aQQTaue!kJh@b{Vw zGBLMOk@l@}@U*v+0UnEAr1=~b`2jap-IF1-Fl2yiVYk!#7c?3b9jp*1D8C-Il0xuW zkF^I}G1Ka2;Ad zh?U>1^#?V9fO4{RyUS9yyO^>FdV65@catC&Daw$sdZ-b*9VG8?uA7Q<3GSD-7~fG1scBW4duB zngyY5`1HNpz&k3oH9uC*C%s!&3$)EvH>)^x6t4!SyoHqUy&^p&Rutw^%_T!TN2nqA z!$YYPYHL7iFtNS8Z#=PH%1+dc4CR#@*4K*tIJ`Y$bSjLjkK0wr-ls4U_N!$3hYLsT zTkUsAN9{khe`+nICY&PK9R>~!+&LgPC%5dZj7oHe?WnYpGAE-B0e?WM&4N4(?gJOH zF#5c$CQOQAv*!i}LC?J&O&^;PN{P&0n!>k6wl}A)?nu_R$b29}fp0r)HSN7Ep}#+S z^7Pelk4O7iLBacaA$s9kj22_jryPAS5~@%p8}DPs4y_%c{!XvM^1Ho`^ZLk}F2|Gc zLUAFf&qtLsl6;Fv$0+|`^!OO(ByAq=nQeypkkGK!q;yAiD@cDz{CmA))idY96L|2P0x}vw$Km zgJ?9t3a$m^nBOG|A+#>y?cT2yJZJ5c5VE^I_;<Zfl9wTn_c zPN_uJ*;-g&UfiMqX@FG!R4nE3E71TV&5_7bDt#ddzY?K2CBDBBW<;g=vRJbU$`&OD zo=AWSPwwPKg^Y;_#ASO;?ovQZM)_p*t575wri8CFK26=i=@~jQ!b^a=gai&{pn2E* zO^N+Q%RwkSlMfnahhJs-&b}*zGeg`P0c*(yA>GW^m?xoplmK1xQYz|8DEun_G+Xts z4UJ)PTL=c$_R3d`JzM$Vw<B5M? zOQP)6?NpQ9)-8-ntX>xirH$7`b7;^x`25xR`Cvs5t4&QVE-_)r(t6samo$uu69R;u+NB1Y6k5DwAV=<gQPweP80Gc0qv*=-g&Ef*)I!@5c`ZGtrdSR49ei0Q`NgHlb(#HpC4oD}e^r#fjMw3~f>S!0t zX#fnZ(kDr|36h|&8ibzrfR?Pgah=c5lK;$R`>@98e3WrIA7z~VvByppL^OH&39ua( zY&rZ$1--)wU8so_*`*8jDJ!u}pQ?@6E}tI~TW)SA#axc2Rn~nE>pp z&MKi%#r?MeP%5F0MKd@)t=;xY6CzRKky}GjqM(p2b#2^#ts9{f8{||AmF-^Kj<1Uf zbUM;V*TrPEtonSU@NKG~8YZh42#c|5Vj>hKte0^Iid0adGcDdY(8MPyz|`neuTsk+ z08!quG|8>*cDWMEH1V=jLF;#FeqK<(f2{zT%FDkjifbLL8Qg$KC*ChV|40it9`_?M zF?%2E?5}L@WYau)c>3*!ZoB!LUbr>#_PXX?5BfVZPP+hCWj!SgJ;OxRXNm*;XSkPx#0Pu5j{deM+ zhTJAMf%|c+zFW;F0gQe0_LjbC{`C?_`~1i$N>4hKy~z4VR^P{pOY8erFKbj?`yTlx zNScdLb-5Rwb#J4x?rl{4LpQi&#CP>FdP3YRuv3d{dg$0Rwb8j&csXhVAxv-{U|^b9 z@)GM8+cLMHXu)PR|#t= zd0TL4Jo7&)XX5Lt>Z_|#)r*akHccj%M|}9cQFyP_#};a2$(|{ZV^ zA0!%5MgmP8I#wD{mw?|?j#j>=T1|d*c&mic1RlIl?8W-Y?QfgdKBkKyB*~@M#LWIw zl1}?YaqlMTdFz3pA}SWBKo2MNzogdT1o6=d{JB=^6tjBBXUB28?a@^yFMN6zGm2{zSPM_A)-@T zO0KDZ44^pWQ%5e+hBuAKT3u4Vc=XYmSEo-NpOU>he|z@$ho2vcJamNu)1dl?Qwq>O zpFe&3_Uz?%S~IiV0{#XFMt5_PS)Bs0j%DBHEwu0R7WNMd&;}0etOyg3+O&4ijS|{% z@Sp(Hci1@3aof2S@NGZwAk#8B5CpE9kaq=mNZh z0LyIo4M3Jk$+qpfM$gu?W8WSf*-j`_DImWSVKW9`Gn-FoPu)ZFcZ?BeM* z?^QFNU8ds;K&`*KkblY89RaPBK=(0qc}@={RXt97u5(wE+PXZv^LE9kU5E5fna5F{ z+wk8IpQb3@L7Q0y(Drx`A<()5**2X-31ITf4q)1j%ODHf0H#U^b7IfVn{pPOXNM2? zRc@|^^1USso0b;T!(p5uwBf-64rMeNeO{J?z7_QSDw`jF;zL_~r_cBx|NiyM)kj&U zQ5wgYA8W_$`kWsmg0b(z{84noVknouM&Xg+JFZ?T0b5?9a5YO=ftyv+UHfj62!%tb zjCa;fz0O7>HMoFJU!y4h!i?U48=LQfE;y>zB6Bqrb zb(+OJ;}5?6i8fiKA4fj)Ii!`pelt=~-?>6CFKH^Jn|VCZG(Ojf<^)}6knIyW!#~aL zjt41Ha-|3~D5*LTY0zt`8tX(|@#@kaK?_g{)K^uF<sUc>!E@V@Sb{R-jRJpW6SplZVU$ z(mmHBIkRvdC}{EgMg;i=b5%mn^qrDi28TVo1j@S@-U>(M-fMYk(&IlaHZJI&Zd6Az zm3FEC6fwhjsrU9P(2lG6&()+|H-nYKjhcY8HjvH2sTn|@j=LnrWdgn6d<4d@~Ctdy2Qjp!} z?ASfg0-bUa`_TfszA^9Y{g{D0#~+}l7g5R0MKzCFt{;av{ZUo{iVU6_7yu*w?KmVU ziV)pkG`%u*$a{}1zLax}>N7|zGk+#@BQ3ezo(Ts>WEDM);Es_FGq!12O z_;3rzk#M`q)bpbVylt5U0r{vbc0x!rapW7Vo(_A_wfC^L2;RW`VvF|%yu=@8it)!; zVtfFB7r9SZyl5B=a)Lb%(R36>GgJ~FpYWpqvM$UtN>cP+iqIjBv~2;(L{w)&e-ruQ zfM$i{`2h`)qKVa1<@uX8#NC15@>aYGq&0pWC)Jfpuh(U9XVR3r_GkcQVnv|V#V)bl z+!^$VDBTZg+;+hRKqyJ6U2=tYS(yFl>PtS1FZ6At$^4rpxx}uUkitomUZPGLb-Jji zhc-_lQhqbVG*SGDzg6QQWy`tAPA-;rlN+w@Jh@uj;K`noj)rjJ^!&&3!M~WT#{)8- zqi@!nO{*EwvIT`9hSG#fW9$hZRk_f(x`rx>=pd_N7<0A4v30DeX0`jDsod_(a!Fq3 zP?n*4$norThM&l6{BW{0_dUGe_CxmrVjaogMiyZIy~tSG>f`Hf4tSWOvCAmC-HSa( z_2-X(ei0@fJN6x41aWX8Y$9(KJJDu9vUo7icB@(IhYAWzfO3K@;)=jT{K|_vUR2sOV|fK;t1I1o;@4)c-6uv7o`&=p6)h4pw2=!9pgvF@fFcA)SaHXb zWySWr=YC)CS=-2&we2lE05oS@tW{yIC36O-c6D=n9kdZ2OY-togk2J02cW}U$m|p< zb0PfY)lAJ5+x2XIB{b!OuOGdB@y%c+`7XKWJ#$o+5N*lK=R?V29yimNyYEJ&89CFE znNl1I%$v3lA_hqZs(zOBYkDmGCuYH4=++he6&-0daLr1nY1j>0VMQ3x z-N&vw$LW!g+ZdYN%y)6{wQ4B;rv6m!*LSi;v>qFba5a5zXS2!ic{Wzei6%ct_D(&B zp99w<5YM*dBg+v(~9n3?POLIV+*rGa|?%UzwhB`re;=Z8XX_EwmtTJ-70C-gy()!oT!@^ zR#wnM)wSigli|Kl)6nuC$Xs|3yMM3)IMQxIos5LL4(Z?807D#%4^9fAGea-6q~p@1 z1CF)JhM)xDZX!$Mot->pE0H7+jU+8IiYQU;_)eo@Vhh7ta}D!8Mqkyu!*zClSSeg| z;Q^DPMV8uVi|1uhWHudgSs50+WuSNE={`1bEH)4vNHDl#LjA*Spg+A}o(9k+IAD1p z6QrFMWUp@tXM$_Rr9)#a1vP3EXl^6_l@?a)6zHX}OM>EVDG_CQOdzdUmatE0hmY8)MTg+IcJFV4I%NY9L8Rp;;zKp{Zm* zkYboF??8$Ot)pg-xB?(aswNluNCjc1cQyf7H9JaC(UmNOy0%I%cQ&8ohV$=65jiqU zP5XI-<^UKM7%AwBMe%qh`VT50B2u+4v=?=+ zsZR>h*K#$YEjizo)NI>J)+5s4qNQTg=<{m(mD;|X-L1MY6p?B5Mm5U;x}$&JSgP}1 zMTG{UIlZ|`g=*9=0lm0>T-aku% z%uy7h4~w)o!U~Cp|5O6JaA9QPsYZ~Jk<*Xu0G3qLlISufil#q0g_6<*>5w)>7~qt$ z9&p}0fzdT0QoAR7 z7rBNZ&Yx-(L16GgpcaC18!s-!nPUv?LDqg)A1la*8yaTPLJeW`{Zu|evuSbB2(0VQ zEZO5|brK=YOpDbc5!{EsJd}Ljd^W`cUCR;_E1|_?a#S5rOMYx#KF<=~eeivPbCeT{ zP=b{57v{k&XEP055IZk&^>i0EB3&TGay^N|`XsLBP4R0UDJL3#8+CUBgjnq`(Ar&B z0%z2M3%6Qfs~B96oSN#VpdzmiJueI%!xF>Lu(csbAt5?>wkd)6tKLze#Vm63q zqHS6KEnf3vET{|3Ken0~4XObZGEx0YJixhJ`Q+}tG@nY(;(Gv1okCn~dwh(`ZQ9p| zReD3c8biIf+|vi|9`eCbCLa%+a2_NbvR=v#%7@es>-|YCA^gOpj>FV}kw5hFu!xET z!et!qNOIIpmfNDRg+`JHS7)#`hUiZtR?QI&Aw_&S$FUzaAF>6849U!-fvC!RVH#Kg zC4X@oGtbH)7F*Fo_x)Z1@6%d|6TvV{jNVZ5*Ty%|m3oTf>{b_Q7cmwK-24p6f|Rb5 zVRX*4#&9?qJWCcjmHMZD`#)7kE|R~CKmFVPbv#%r50<)-u0cyt4VXZBII9+UMTflN zw@6eHSh5%wsw}R+aDqmYEP)a&RvNWxDVjs~uv|<2@>M*QfuWiZN#NvKNv@#FGF`|h zgVPLb9_t?sql$C!>(l?Ay?0q|BuUrA_I!$H8%p5@IFEH~6`u^WN0)apT z5W!?tcXhL8P8I<~czC$~?*3oD@8W#_z(5)}{nyI>dq$b!)Jy&tNKW9h!SA&F!?S}E zZhC@!aq#K2{Dyx&Mfm*z=PwbRWZ{IadbfW-|2aADoxa-T|9*M}2A}>7U@NpYea{3M z!Ao3m_^oP(JFi)L_AVHOO>O_9tic?;u__Mk$ubCBZA+R z2httyUY)-zZa?9q()_OP4ke1F%&x=ei&!)G-AsC)kH@x>2-=KdeDxaVi*`=Qq%YnK zPBh&`aR3_MKj>N7i^ItXTEd}Zhs3u>V?Owjx}PI)fVek)ki^GS9N)Bo%Y{F`Th7BBelz%!{Ls=P379ZQwTKSJ7mh!#66|&Ygix z>5ur2PPHE=XBS7BrJFQM1F(VrxT}4f%%Wjdn%UQKmD0o!VR(E^AD}*c{0@KQumTdx zR6gRxr;~jQ9R4-=UnGmlF`~cUL2(>mZQz}MhmSAtkCb!#VdBe^L678_zQLbTf_4rh z;}xaN%FPz}(i!hz)|)f!Mc=xb(p&ByzkZERVFrD;e}8+&-woEk`as_-_|K2W^!MG6 zcbVbc@9-%Z-u*})U%&f^Pw9y7IeAYXZYT1?nN1(2Hhp;i<_$hfu5R&w$<-DA{w-g8 zbBC{PZ$fzsB-+F(z#4kdr_CDoU#N@3vnmX{per*=T754BfjH2yobCwn5Bf1nZp=o$cX;!C)9Tw zZ!c+h=!!mH(&uaX)r*vPsQFi-*he@Xzau8kzhmhS?z!!mqO9U!_yzJdGm_LU*{}C z1E~n$?*j_~K>xvNx0%&%3j{D*sGp0Dr;H%s(*b5~o@hTLmB4!yl1dE=3>jdwBfO;D1g2 z7g7fqH~3%Prr+57uSGX3P*p*5elp>{(tVY$%$l$Ix|B?M1#C66@qavXWFCH)&n@JD z4)M3&;XwFqpZ@usge_592c0gjPP7lm9+&OXDhgrK{-}Kzli5jUFCFHG+KUtTD_>%9 zy_J9S5;_^ivk58U+WEVq-O@*?ec@HqG-8_{(>E{f@Gf^2cjC@@S-wS`RCl=P&XgD6 zomO{6*(`R&kGv~>Z=BEZkh37#Q%VpXDa@=RR z2;&vd4>=Y#wRZ6l(~3Tkh>g^M@i41*x|4EP{zNSlOJD$2NO|ynJag$b6b;6dIhhMM zCQ0*q(QsZgRlVZm!@H5R?@dM4H{BEgaKobb5Kw=_)SRUy(Y!2oA@lqYP$y+)@VNOhDA^q{Y zL*7q}N8pA)o7~+1D_%{mAeg;+=n%=_s0=AsReLa|ko+MbQF=N2;Pw1=RPSgY$*nY? zK2S?}1I=EAcK5fb+-TX-5_$ae=+j`OlA}TmzBV*O7Bd^d115Mt{slwWG*N_GeQ$dy zr-cMGja8cYCRZLQ~5H#zQJ|TcztW1g&hV4#SfA+@!->ofuTPwnX@$iOI`~n2K|;5 z35OnraHeS>(3QHng9H>CF|F1Bs_#A=T>vWW?eZ_6rk$Q19bD97w6$ZyI^4h5zZ`sj zdeDL@tLBQq+0nr6>hQAE~mxO`vX&=w|*JAtgI&Obn z$L+6u)`V>*Hn1o4)I*Z4k2q-@*x+vhoqgm0lL_D?0s(Y00wSaiUulYjUL6~v0U#aJ z!Bs%qZQpQwuqixmf6v?B^Y-^wwEaEh-sg7Q``nIuU;E4<%W+cF)3~7y0-0D3Z2B4i zbqHC3yi^eQ7qT^gaTJGE0(3CiZFDd2f!>7LQfoDX#P)(f_igie?|a_+p7*}Lmc4H) zqwZsNK&Rsl=ycox+vm_hr8BVoxhd70ixh?MVGxBLD3ejnka&p`;ak|lpl5^%7-EM+ znw-G4;{d8Wh?B|=2zo8ccAs~^=N<5Q2mCAA0rPsxPJ5ufX%EypIso;K4nVzw12A;; zfbq{fD$v@S9k)NP!}jL~DF5_a9i)vqkgq(%IZ{9g01y(z zREUb05$Y!#fVPJK#kRoD7Mmh8u^U-7^QNvi08Mb$t>I!nZ-39*-}CnOH{$@@viG?i z_dd7d-sg5Oe+^LBhS2W<9o2#{NNi)EntKG13uOI4KJS2k;|@T#?=<&54GooY3xTpdrq<80SP4Vyhu;M$0|}FZu(9D6k%lMVgm|+3hlvL z7^3b5e*;XwOuUK~=o*2=Fv0Ejz-@#)6a;zR1E2T6=RNS};sD&b{dpa?Kdo&B3sY5(uR*)hd?CW;y66+Vdf2h=TQY1h}z79eg9msJN2cSby zVCQ-Jd*1$@x4*wl2jJGd&+WMPxgGbu_71=ZDKEkh;P~{xz(5=k9(ZQ~7zcVoY%d{p zI)GMqNQH*N2T;$z3>*o@4iIKQiB-*CP-^MVs^9b8_q_N0O*;U$?to6m9nk5x1GaYn z#&!^!AVc}}t0)*vGX68Kwk5Hjg9 zVEzc*kDj4$u+)Qr!Xwaa{zY9x;E!)FF3yO$5&V*2%=DoNQB@xVg?%r zPbH!L4Ce%KIA07IPHWl1Tq1aBk~wh)n1uuqo})Js0{DUu6ep7#D)(0O%Ad{9D2G`) zkncKbL;?5={>7Dv*;|?N7HENucW>vaB5b^mi0cYDxz>sJ)ulcMq9n0_uk8|SHi91pvR_ieMS-vnmO{&QfVz$!!F>S~W6+if0|eME(x_1> z5*nfHdq2el8|Gim1e;A0Y(8#+&Bsk}d*Dv+NQHJ3g0egUqZD8-h_u`aQ$gfXW=iat zQ5>39gls8;3WAUj0QPXAL=7C<_(>+Xf}Q`9P4H&jW;p4q(*r~xIMU)#|L%6G$fFv5 ztk7mMLcG~Xn_2u^qtBe2eapz2`#Ew=6%2Hk<>=Dt5{+;=37PYwB)!*8jt`FkwiS*i z+PC5%nh>|q3+iQGo|b6-O7w@5m+N?-%?*_Lq_DB+BJiaU;`Egm6N(cq0Tl^@IWw6F zUlJFJ0pl2}O96|VG4y2tr%a6MOuISO1ej82;Iu3U=H-^MhcPZB5u9l+2EdfQ0|wX8 z{@^8n7G{9d%vL%@>?LSdqLYrroKR7reC0eVz)G7FrnGi^#&^M(m`||qcD@K{b!Vk0 zS7Zt?Q(`xq?9o&fctTs1r@Voc+gd43wRbk6DWB2R+Pi=L4^#1bplyRB3<_+RE%B7a zE%vkjdWr_WfeH+xtt^e0pMSL{uXqF-is2Y-^sr{W6Kai-jqo08AJN+xehFNS#;&DB2XL<1~#3f2Vc?k`x=TMq!|*W@Yzw!HZli z;v_o2$8rT$_cz*?BRG8y7JNAc^cXfj+JgESL(27G!zWoBPGNg2xpmqeEqB<9u;pOs z5WHv~TJ6!F z^*kYO281!@<7$DS&sHA@%ID#D$c$NpjlX|cel}l5 z=c^!7p1uLFTi)#E3yFa`e}3(i_p>1%#ssNHZ$-3a+7#kTV8L)iyY8@A#Anv4xyq^-VmZO>ictQR1jrW6H(~yB0kL(62jv2QffXw&zniB<>m=1w#n(EHdkK;j^Gkm|Ag z{zrZ4*fY!hW=^<|xTv3q;zX_>6Hdd2X*lZNg8aL(1=V8_>V3a&Zeveq;Ym^L)DtK) zxl%f8p_gWpW2IXo?JM;H&v(EC4E_WZs0i>VfJDd+9auXd;Al{L2b&C55%6ntGofnb zfYQ5-z_%?Or7o+Kz#dFAyPBs%ECkI)nz8YiH){ya0IDNAgpg9y(pZsA%$U9)7m(+G z1454;LpNDF(M_tFX+On8+fOmkcKeBjUyP_P1=V2&C_w_Khk@KIRAMHWWiX3c7Emk6 z>P6N<z|)Tj{T>wbTQ4oNRc=R7^;tyuSuh73+6! zU<~DB>C%h{Rgy&ZW!XSVoMWV4#X@46%ZI@>8E5iZ#n8eK^irQg)M!E!R5FS+8p7E) zlbp4=3axGh^%kqqQtxD@EPCj!c7s>X3aXdf&6DBvK)NFP0fH;A&rBBcoY?C=1WDD- zFGurVMZdh7|LP=ET59nw8U41$Kkn@O=@0*&!%D+3!2ZFm7SEwc-V{JXE7NQ^l67v))ti1G^cyBR8g1K7w zI&PhRJD>IyaEh@XkrK*Z5Me-X zCctw6WO`=U9ZdiWjo~N8FnQCfT)33aj2O`7P++Cy6N5g5ScZwCOg;lv7&y>~zimiE zA!&X{=aT~~-+EUvry;PI$T=hSF*G|PNOp+IoQxg1{@qZK3|)bs02t@uK2sBPN5T)= zA)Vp*h3gLJ`d740r0A(d!&U$(mvGln@3@7xpmWotbxl-5*t&LQ2$T9k+{L_c$lm21 zNE)xD4N?fyD_%6KOSLRfa$zt%k{rat^OGKV*UPJk*9Y^*fzR*6ex)d5PHd0^fPRDi z+22*R%ZEW9E>egdKa(Yx2$toF%yOA$aV5j^S88dJI9!dfsq-bbk}r9770R#Vl2uEx z^meD<;{p2*rlh(`@C&Ri_)|&JAaOIbEa>|r%m0Kp5|axS=prF!YAi($HeK$?AT~TL zyo1iqR4+6{w0fr@hI}!-nqf6%`6yy915@@`llF=RUoKNefg9rVg9eu=7z~u5hIe?4 zb;2u#cge+-vh={JQe)nng^SUr3D|-%n0z;T{5WORe70})>4VdCy=`U1ZFtR%K=a$0 z0BxWk;=>he`P@m18?@yeRV%Pq1>a!XpNq)`8}Me*ONY`;j5aUt12 z7ytx7s@D;1C&p?oPH|V71V-S3_*VGQsy@g_fbNPx4@YqG9=|!oO_#6sFYv?nmv9#ypB(># zN`YIZo2|{%V|jER?K$3H=NCvz^)v| zmIENQ)G^XXL^kpTJRkqhaRgE}J!HsxQ3R$!Q(&sQBp*AViACO%hkm3x)QC@22k@)BW!-vp6BHToqqJ3E}uL~&zIcP44#G#zwO z(#23O!1B0_NA?UfY$ECT}fKrYlbY(b+F@j&&kOCOZiN1-B5h zkE%EL1-d0CazqXR4oWeF|l_-#c*ehfF%y@}&irmt2bMT%4@5 zlMnQAp%ZUh{?@DSveTV2?n~O>Z`7{1x3;6Vu)~{fZVaR%^&j&2YP6VD?dN=V{^p84 zQtORLP1aU!wl*0}o%4>H&X4Al;|+JTkBS6_g{Wo{Sb&(aqZCB!2!13e!G*?Sf}l-z zeH^JeGMXGaHc)dGx)ewOYnwv}sjGm@bagfQwK18=+Y^YJOZNrbh_K*^rnD z-g>uLr#V%PuBtPG>Es5dd)Zx^RRbXV<1ze`w0q`%=c>&jZ z?_$y$FblNy;^Kmc(Q(#H?kO#X_xy==GoIWdCV)M9)m^M*blm4#8tvnpE~B<`naV}G z=>faVk-$X$PCO#-1+l(fjgezSKK_`Rm8aljwbL1I)GFzQbB;*gLC>k_Ye}e`a=TK+ z$RuI+iwrL3H@GO@?~{+iBVjyBA}Nf}Z0Eir|2V1lS|X`82bM-MTGJSdTm4EjL*%U^;+6XgMb zYZs3|S<>~Gij*k72sEoW?mwQAdst@r<|ourSJ`Kca~OeJb^gF5SwmF1ODXStg$-Uwt?)5~8Aqo&4oUr20Hmlq5xw zp32LhbCiv>D%vDpG^ftXAszCRnI-+>m-!yHEpJ-hy|=QNw#k}azi(ZWs3ugEd%A}pp!gA{ z_H++=<@oXp+fkEe-RRqWdy6Jv*=;llZl_k4u1#7pX(W=mC~ejTUOVVVs%|9 zT`K$xJM!W<6eb8z0w;_OfWboQpnn7Knqo;NVp$>rIIM?!L)83%DkXqhAKrY+SoPyu zzGvu`@#HH-e_knn)hnebgkYInSBlwnrL>P91O#!1>`RIcfCMd!ZA1n{;G*c+x5LB$ zb+{S%DR?MN1K?3sg5UxmLCqLJT!^f-Rw`+&+ozwoRE#QR+VfKR884OXX1>xVZ^>6T zyPaIZY~xnsfJ{e=Y-#mxzqS(CKtRgwnVa({DZJO<1UN&B+MoXL$F2F4@6(iXz1C74 zmoEQ*gjbX79QI45qDVwq%bz@+vx9ch&}3ixw6)DVr|WCV^rPBL%Cf_tQIZ<|BW-wc zPSA%~eJeesux9d{G&qpwR2?M!2ARrD%Wf%lP)!FVBfhWg?7Uhak&di#=|n-~qLPQB zy|_ZLDhxC>P}AWogL`^Nj`-;Wy|^Y*k<#~Kk|x-A@WQdzK_oETmXm_{qy*Dc+*95n zDr!_L2A&0(iv~CpWOvRvt!(Gh+x?5L1{#+5>!b6d(+lnO`TG;?<9n?9ot+*h{K>a} zH@W&+rG92YE+U!FNHK_sgeJ)*g-e@!8|<5)hm%1}sf;QGj+#VnTDkQ>G2(J!=Bn2` z{X@aDte-<#l@V%|wc_#NFVVwmvWR7#ABR@Txq5}W2|g1sSLwUAvR{;0 zs-0qapxS7N*Z8`qFT0=7@TlZdZM=}Vx4c9sKX*(%B8YuPX_4qKx=F35>s2B$J#;>S zco*r9?9#`hKoCG$~grEEBlU+@nT>`Sy>q{jdzJjxqKJe+0U*oyR%Ad;u*MKW37 z$KI4n=eU6x)x^m!UZRb1Vs0ptT**mATOHMl$%a%m^=U631x{t8fDu}F4aWu*$qmn*8(D^+Ct-4crjuMDxjczdM1+8-Pr473-+F|vt=oa#!G z$twvNv50vMSyX75zU0NQ%wlC-Li$FvdK-)0Ld! z%E1WNsBR(U_j)8>n&4*sm)S~EIyu4R^$+!|DFQ2kcO)c4MM=uTIR3#x|Vl#wiu5~@n)I|L_Uz!+AnGxc{4_-qgLbf$xq*2e){ee+wxvCnnZiFW$&pW z>fa_9ulHw1r`q8ChjZF*5kJ%JEFeY6Y+Afgw}-SiWNQ0emqGf|2KTzX=T^7f#(P`1 z?>0?jx8&SSEp5utJyG{9gNs$4pJ{H-r3GqYD@+7)Lzb6pqtQ;=u6SOmMmE@P?Hg%t z&p9z5jMCT^Hj3wBfX9FkF+dKa1%_*z7#|RAL;hhWAVx+2@G| z4rJY{h&$Mh4g0CnAI;!d-2WWnZUmdey^*n}i=;Nv_;hQeJ;Ng)2P5IeiHAzs5Lk5Z z+kwH+1&XKuTA?ohR0z5YKlOb`Iuh6u5JCbD-f}}bjht1#w&&Y{q&egdz;0WcHjA1aoPJ@?BHCctE4M#oFgEfAv zCY8TgZLu&npd2mi`%)PsFyP1(C3}?(6;k_quv%`1F%lUmZ*P~-Y$%I>>U=38zr%hp zDnumvNM^#~2R1kJ1O+aN4QDFDcPNo!xUX!qQC%PG znQ&rfio=JzIC&kRa#6__A`cO#4&*7Muu>rzFxce#98$iCNZ7eZ?jEfu9ei=P|5|1j zzosHbxN9DIgX>{BFI~MXG<@+ISNDJNazT}Pu#~E$q){#17ULzqcCRdzEsU0$`?lsB z+~42#6_5)x5nl~^5lmF{5VrJh;@jlcms#&eB=g2M43l$B6@kg6^Z;FAiJc$*!~W}U zKYst+Z@+)N-;|(WQKrXdCpuai=r^0oP-@nAbDCr0_3cajLLfXisRQJe$TpH#Afk%g zK~zPd#4o`a=LiQOBNPJy<^`c9#{o`13IHR+a%|)=tOi^SoQb9#R8A&}Fh4$wq;{;* z$s{5mYzjbB*p!*+<3)lP@n3L*+Mvd>jhd6GbnCtU#TODOYsT&R9te)40d z85fSXxg8TtG7BR&lu!t0b1mHA&k3!>p=Lj}X!l55)QV z{X+#9czp2f_q$Dbx=Y#HC^IA}D-v^y;L2_nIwiB2O2p_m`$osv?*KrF1S?Ez;3Gd9 zpfHK&TQ=cBL)nJg8>+Qsd9LHRsGze=9QY`322HRT`>sGmXzHxZzUj1^eWU6c`FYR& zW+fF)@cs>scW6>HPt)7%GP_lw-TL1uN~+61oW_QHZj*7H>Xg*uv;J+S4NeAY)oGI{ z0WcPk1ep@e`O_%WA=kRP87_J40RDC8L1X?-%NaSU8f}4L@&1m|OT~Q&!Givr$^xmP zON^8=-rfV{{1@UTlew~Td}Y#V2z-=l1ma=}A<|Pq?HZB3U;pwS7nAv~irh=+oBb=! z#b!l_f{cQuE!$K1*T2D3u{9(w%1;1J-Yt428>QEfdEKlt{R=DjRVHiKCvKW$cy%gkfg_;h2mPt#hbLnxL$%RW)nHt%N&6 z=#Yrr@cZvAMyq3S9?EEtsFRm{6)mMVQpib6z@bDVWUwWq#S<=PNJutvi89FLg0x&@ zS<5o0P}yUU1qpN{ahGl<5k-SlC0cc|_^%IRTs~ir>N`ScdQK_hWa8XX0U2RZ^Ajkl zT9h{+C;pn^cS8~j7!teGQ<{-R5vApiH`;@6-e!~-ycD+bn#t$qfZ;)xQbvo3K&NJ- z2Wc+Kp~0#}g#$(2F$Bo%ruSxU5~{t+ZU`(XnoaIAjxtkaUMOqii0g!GNg77NMv}Oz zv7n(XUXp^utK(NYe0le|{!winxUq30=>7B2SPp`EjuP!jlq9-Tj| zicM5FKpJIbB@_owix{cnbWa|qJqS}z7ONKVxTQ>;t8XCV@w`T=u(uB9Zx<1dUdGOc z+F3uG#Hi;S&o@px9SY0y0AoO$zalB$vN~icI{hT?tisad+S_}3^bYGVS-!sdf>R#0 zoLBFp2c^Y{u6!*?2`sAiO_X;c6Z)>~$~YwWs>@VrHOq zU3qQ;-9VGus$S~{cR-Whs`-Z2wjPW*7yc$k2+-t$Qotl4$N+H^Fi0ZZ8R3=yBrHfQ zHbMGi8Njb~Kme71QQUxAK&(bo3_b9`+t{tB#@UwtY*x`}eM6Yw@kLTL5VTOu5LhVy zOHrtc*na3oqHRd!fRmQlPgUdCDq<&1^o!v5yVyUt*ht2BcK-hL@w*NQ(DhULh4P%wX8BC}01i9sk0GPEdl!vMttAm0e=Fiit^LV`G? zI&#lTfvfB4R_NL>O4oH40-8Wf#oEF#5R82Cg=0J~oWFsEqi@y^B_o zz^jK^&V(w^Vk7W~WR6l-kg(+h33zN!frB4YxRNc`Fa_vsk`&m~0sPB}^Aju_qYBye zyl{T*3un787M`0sAVu`)pJ*~)I$|q7tQXX*Wb~~U)Od{rH8(?KZ_n4D$d%(W=YJ*3 znpwnF(ZFiD*7tP$Zw(9V1pcUO71Bz%`10U;k<$+q`<6(KUUHr_LxEA)U#3YT`5fjv zu2Q+**-PX{ACs+57OP0|oNyrfBA@*9H5hNhD{eGLOd>#PG=j#sJ^;qRJOSeY@&)trjK@?z0fz_YpO-t4zXEq+pEAtJ+fklz@=w5Q zL-zjH@&Y926GHVsq95R|bLrBWQC|Fec406a!LoLO+nJKLi!10=?s?_%SsD{9Tz>kmGhmq_d@oc48_@}Hr0wRp`hsdhGHQzIL*lZf;6 z9Ymb3Z=cG5v;`3n$bpD$j6tfHBGWGtpix97P73B(9sl3;fO8D~*&t3F z0|*Hf5!W3cwt7!1;%*fS;m;-Fe@oJ&0lGRVp`Klk7}XMCD5;_DDoKVA`VaB1Iar zp^QqR&~q5e@>*%8sGu$BG6RBgPQ$wil}4}3jtb-Q zMf?V;%lKTAib9Q!fklwN*Na3^MgB>0s5Y2i`8~}B*ovGcoLjV=wtID^ z_U`Vcm%Maam}2#}cC^SE8rf91THOwEE378*e0@+BG>q6_@Oiw~NF>v@w(`iRO<;38 z??O;ggGky0^wdM^ISTWKHKlE??^2ifF4dU2*?doXqb@OtZ3d8cz7aW59N0*pbU-|V zFHnXASygC*HUJoKCNsSnUur7?!1a1~w>G^52>R@pKNqI ztI>MI$!I;|Wb6QoiLzUe+UjuvkXRe6%WfQEF%qVG>QERGM`L7ANpj*iNdT8# zGQC*XX)N5I=48~LSL5@^*!g7KycQpEGFp!~89M-P`hcj2(-@Hl&kO<05`YieuwCFb zI|1tB6V!}>9~-G=z-O^RjHrXN%Rp9*4Iaf5n(a?>GCr@x=aaGf$!IpBL}Qh}ncMkf zq&8}! z_>KoS>c|Vs#1P;mLQ<)Ll+zFkG!SWEx_;^)24qBlfEJaL(FDTv^ICjfi$DFzxE<>H z4mD;7EluwGs;+~E6-i@)c=?tn*BdXZM6?X9if+uuD}JtxXi=NmA#nm8gd@~vhA8c- zhPLuw2**d>AbYVIq}tl;zEPX2*U$+tJdLKAf?3X=Pa%+U9~fem$c$!;%rh+29Oh`y zp#&-(QcW%ezGUoVGz3IIRuDXjAy@rvco4^vh+hc=LIlYT_DqbC#3ZKr-v!bD8FiW3 z0OIj@PH4Xf9bJWsD;dTC``%VsWq3@Ht_Pv}_XVDNGuZ@Qwgg(Cys-^Niu3Hl9NI3Uc& zlj^R${Dr7t?}>sC^u~Y~%Xgcc^;RbOE_n*;Huk3X0O?8XeqMg=3aRj){_wwLOR%+S zpCn@TkWdD0m-WD-dxCJg8_EiF+8f85EnU39l46lISl&F#o!ph2%@XXM4iu{Umv!EZ zYC(|Kln+iv2$!9skneqkc6W?>=+Q2P|lRZ2p(VC;PY^TyF}#i%6Zqapj#AN~ow&LlXUL^zOb za~-+_@|I5+X6hk$fqv29{|V-4yos;#rU&xZ?nPSOPoE1&m4F$~X! z=s|W%iQc1vzS`=0+DGkA|J}dQK!c-wluX5pGC*^ejwE5=4~tuz>BZYg3<)8hPrqYO z810>_r*Ej79xRiBc_50n_&0yh_U}lZ7Gv=v*eWRlKD%BpfPV1~C(G%}dyo2jsZJ1M z1LObwNEtBG332+}Gv*(CE7`26!Hh|q<;f2k&U|e9+FNKQih2OW>eQ7CZ~-(dvfJEF z!OFbgj1SosM#0kmr5gL`p{wpt;U&2c( zf7_fal(dogY$23fN)&uhd@}GvP1_~Nc}e1kF6@O1a4K!6Vsb5?Jj-RPPL88D9lEcBw=?aM?{Bd}@xXoW?SJ6LC z(m7MF%bJWxq!Tq~vH>i72Bn~t#2XKew13&xUOki-bJ1i$Nj(Oz=T(W7I;Tw~X#QI) zXel6^-2|0L1kedVf|TEWx!C>}?dUEXEw~heh(x7gurk*9jd3VRDe__aj%YeC zrAH z3L%nCzHOm0kh1BJ>S`#RYnn?*zD*Fv3T@t{?f;c(ve58{8NmRp}_BFsbX*b0~C1VdjQE=ijSp(sXjxlnOJGnVBEX)|?7+3O`qS2A$Dj_qPlDweE z2I5nUE0L_K9_*_+rNYwd`+QYF#uaJ&HRds%vO_7g$`mDUUje=vbO zq)1khwM3a>t|iF<^~OAZEZ9+G`q&X?2NI;{*IJ&2R?ye-%j0!f_|NxGF#EhE|{%H(Z_NznH-Xt8vCq}fuu(m3+Ph5F)rr1!hh^SkRuG2dYtLtqN4_Ik&oOJT1srfy8?pXsM=*FnBm|tE(Zk|KJ9=eX z1tkaU11pDH9!?&?z@fI5&7`^NqRtQ{N9f&NL}v9vZXwB?t5rT?|x?9K@TrZ<|bj%T4^Z%{R3V)cB~9_KnnV5hM-m7u11A#1N zNY_Q6qdE}u$q^hD0b+njisVw=4ICpyH6UW{h)4)9Eqfr!gg9b?4bMRtmHUOojNyzc z;1oFXkS!HS+fM}H}@@2EDPQU>fF>D3WMY&>Hew|#WEUVCYc|e^fu=$mM z1GeT2TpC}{I{%K;a54l{cXFyrsbhXk;g`{_Hd9oFrP>&n%I&eI@I_9&H+py}?;6|^)#m=$w;C-c`Q5u3>cI8|HgawnwA0FuZIx3s{(JJ} zF4fpys-R{o4Rv!99V=(HG*(+dQ!R6Gw+2@%bGuq=Zm4nH%)s3!(p!kQH{I7h`2hqz zv14H?b&x^?n(Ndv0P>UA$osS1&^01AMUtS6QzH@}urh7LRj61ybP}8sZUr3(Am&E3 zTHA0ooe282P>NqAv@+A!bv=<<0vU^h`DG?L>Td!Q#6w8nNH^p@l=k?`v-iNa{JuRE zchixu3P?aRlZW_oaFueb|M(P%7pS5gyv1Q~@ZqApra?)kO$<|J@Hhc8gU1P&8MMc3 z({*h|-sp9j{4tyhqB8_ek0C+FM1$ivfu-0O4oD(y^jA z@UORZrjp z1H269Z#Y}1c_n>I>YNQfu9v_&n@ix`My@Z`sE)bKRZUa77{V)AAbC+iIFl7za|lDJ zV)aqms+!sG7JX;E)SaIB;=jSEck}Ay;~p+a!W0%e6MoI_MR=3t!1xv289vVa(u_d5 zi4J=O#EV;`&=NSm1iSMkYJ33-mN3!uK$U`X&~%@p2FeqQXIxCwt zGSJ^K`^5eYM>ho*C<}{51&-`cCN_a?4c_Zy!topV&1Bk}u~H4Dx3QR&Tb7-gsfH-5 zt2)%Dh{-j%#F_1aJ@NeJXUC@$Hbxf_%0>isj93R~rDYH$M>qQ#yby~0ZL(q1`J453 zl%@BJnUWhk8rdEgf53~ z3;@W912ANxf}927JKuZSsa-j?|6EhM-8FVI5<<OUgV z`6bSa9|&#)?ihOUy_Jz`!@k*YbK}YJTXlo>E$*nIRr1*9O@0GC3{iFZp&d8E599eY zI?@E7V)XsKxh-usXmc$2jj=3ali^>LtV?5)xnFa2dm|rI%M^41fV2rhbX&v`P}yU| zsce{Q35wkiM(~9P`zr+KI~e6m@-G3B+KyrF$E!TiW&rX#y%WTAo7&RVxPpnKUz5BL zZj8b`@Q0?U6`-_7$8l@Q;Nd{B5&|oS)&j|jQMU|>KLslQ=FTSW3_{)UYyk{BBdE$0 z2wv@{l_??we3q$_OnHsLP{ZvcQ*I}ja@#wU3`*v(fy!if5lHJJ5C?~V+66v7@b3Xs zVHxm93OyjVcLaZ>fly;)k*XCLaU4}-3NVYd@w75!^$hoKSEjZrcMqT#yjnb{$AVW7 zj)tXzSNH3>`^hRt6xNkIn~N=*l)Qmzv=Zs$Zz(lS=5k9`NLId-2S=vtW$X0w%SL5d z>WKUk_kz6?xld^^mrerqD=kyW)m>xGS!{kLd30tElAVvp1fdqnTXPyZYV4LDNa>E0 z152vqmGoQu0tE^={~nV4@S`LhOc{^87d`Z*oSvVzNI;42C7+>M5qc9s1 zwBUp@gv%Mvb9yqnkTm)TO)MeILvn_}8zV6jRfStd&ysH7L!z$H@ImFR4Hl z`#bwL%^m@HjV98K9ftcozeLy^;!?DUe zm-E?M&e}b(CoVmFXhKG$!2nA0q$C5Jf(r(W9F94ARRU@$E>WF8>DuVpz;@a!b1Pg8=L`_0AM4Ch9oeH@DVH!@i=}Q5;}H`%8^&{Hx1YqQ8)ER<=)lKr`c5i+Gkh* z)XeZh7|3P{`!^w4Kd703g)CtJI03>b3_!z_Mm`{!JOH2>!0m>@7yAbC^a9JO3V>lq zc!#GHfC}Zq&qn|nZP=~?V00A#qeBLSMiWrVZ1hoN; zhcw3k)OBVI^h)dq!u0@+XI3!fk(Y^Fbn|H?z^dlh{6$K@cI)8x4_CwN<`ShQ>e0Vj z+6L>t)nT(6{$}^D(`*_1u3h>gMPFAHs;W(}FRQ-<6&bNc1JyHTMWoVHp{bFAlG_UF z?XRGi|DF0#YCT`)shd(ESLH(LF$Mm5!T(%Q1W{{=I({^nYhJHqKl5UqqKpWD@Pu2@ zZ_vn?^IT(1L#1Cr&nAMYISO8PR34->iN2PRehRXuC@$lut26?D3NtC!aaAg@{!T@i zSsnU1=P%Cbkrk^k*opg{nTx-KE|-nNscG*Og4#lhTfL=Ge>(uY2h_5v{uaxx`&b^| zT&=mls~ViK6&bvh2G^WCze$5zU&tHvx4O&QAKnoHazJ83<%JOo@GaTCmnM3QU>tIe z05Kdnh6{`d9Z=ukyfI<`LfOKJT_2JNw#7;Yl8cP_Zb3P_g=2d)do4~y8izWv`GISJ ztU6&Nae&hH7^O;-S68!j*ALT|sg`I4&g?ejX16JCudl(m97aHEcP$GUXBHyr#6jdH z0L#?D{Q?Bq0P|}Snr0MPQDk_Kh5+!j@X$GNm7y6^?sN>X+bz(*?4Qw;KTb=lnM0%7 zl(*N=bkK~7z;a?g3C%FHJ$Ql*V2DHiA`tONcU;rJq@!2{sBjj>7I<*bi;@5n4^Wxa zvKSWl3{85IC-f&f!74p0h*cNkE9L);X&jc+?~8{Qvx~AVmSOlat*T&P$Eu{ z*YtkE3Qzs(aqHxSB<`3UYM8IX6>$1Zi#FhR1Uy&{2|zA9%)(F>wB!x0D4o#0j!5kz zg*Fx|BZ=PP*ManUl8%|fZ;ThW5$=GWd8LbSIGfQO$8&|VQS^{q;&XD0=C2{!em`4Y z1MlrDq$|Hl7~yQg3v^GGC}Y^wDuXo_R0wR!U}7vldn-#|8}?bvEp3p`)^2Om9xou> zNIE^CJIb^(Rc=q~^VLJWF=cB5obTopc)0=8ciWxqYb-rK_QDvO5b7692Z|$#9k5LU z3ma z1yG0`F9xV-NPY)DGEK1BndGxv11GDQEVH9pw|&UXM^9FV@+TjJJ;*Eo*#PQWlf;d| zIZh`(b|;je7``46gGh*z7C>{zw1ZO?*=_<#U2H{Zg&Z9&T)29kda^3|edo#A<~E`F zRR*)k$M7y(jOMxHr2f~6>qPGvW|>s8;yN+mU~CMk%G3CxY_-D7t?|PoNY7}L$2Qez zK*!V7FeMDlS(5eFbL;B6-Z$&=>q@;SVSh@DOxgqK99rwbZt3~dot@F-0q8#usz=Kc z5KfPHv`7S>gbHnkuudQpIB-ZL6+#7khXJBEow$4iU_LkEegHJpFsptA!`;wQ{R8 zw9 zQDNg9?K5}+Y{_8ChIa)cBpB!DBA0P0i*T|wiIzKxHGWQarpklDKp+@aO>dR8o=*;muOO4-I zYW(&(Sr7-Fi&zF~A`DRY#=v5YVd*=Tp4w2#LeKL7r;gJfiC(Fbc!o$q+#12Drz0>| zl^VnGTpR7h4ojJla2r&{Q%||0;y04UYW-uADLGMj5t#`E6~{X|OrA z*8p+Mjjoq<@Yf9Pr!t--fTLz<)B#PZCo}0_7`(nX+tv7=!O1I0OZ-Vhhx;Fq*~Z#n zJ)ZsYeX!NmXsxmj%<9ZEzC8u8$UV4a9Fo~eaFBZwErz3c4_f7hZpk#LU3sm%ZT&^* z`rnE0mC9_xOY*xLSTry zTFkI3FS*6zTZWjuhJR=b1xnV+j`%S{{z+o7(4_ukP!t+3=&8Lq zm5AmYllNde@ci4!Toj|g9iBpc#mxZXMD|xSfok33SZ2rw!75mboA4{6@&i_nK`l93 z3`vr}4h@%uq?CR~k6U}1?8M2dy@TV|1GvfN++j&#z0y%NQ|mV$U?6#JxHOhW11>uR zPzlhV$0<|e!gSMl3sl)FW@LLH{}Jz7WDCkx4*?-e2(J0%vR!Hc#lDGxuUuje^F1fn za);B>YRH7#BT92dEvP2x3kA88<6X+@Rpl#Kw7FICXbK5{I~$i?N2MFUXG*?4_^Aem zOXE!YFnG21{^HH9Mv014s7zU%85Lg3ko2-4eu}9t7YZZqWF@N%NY>NoB94cE_daEr z7)$^ujjbJjzL_oVqREfD+W92==l@$fL*#o`yO`WOOte#qVatW~mVk&k`i?zE&YJ`k z;)mt?0{^go^8Ve?p%#t8I7b2Er9}ysYz5Ca?hpF!TQVQ{$5%*UB@-cALx){i84Rz= zSfO-3|D=#o<@~A~(3Q!|&@Yui3RksUsC26BvMIXD$LP!DEj^Xe%2P^CyLH=WrZ=fr3yHM7E4W1XuU-?35 z3Tb(57mC+*p|p2Oq_L-aj*~i}?OKK~($poFR)F9lsu`^aM+=h8Jpe<1UewV65DXYK zI}{;unG!EHD+>h|8lL^+3+4Gl`DY59MI)S!gMhi{(irl^{*wJBT2;_JW-NIDj)i_H1Zbq-(U4% z*vL>trD0-(shB!Qly?`H zc55j&?bcFmwueZIke&rGcaicOI)J$YGz=vH;Ms{7e=XT zK<-mqX$8@ml>4Utg_*jarTi~c%A2gapb=ARU341(wdyWypX1^HL0ES^zz7?0kOJ}% zZ1k|@kc|i?TV#+D@{8~jWQKxO*3lyX(YPV`4^e93ht~RDZag_3z40vNe_>L-*)oen z|MuZ(lJxJU<7*(|f*JNQ1$KtC(3*Z-Cki$;qdBuSPV1Em zbUN#w$jI*?f7q8)|HBktGeQM7=U00&(>N zhKHbjB8In-g&2T4rsq&EF;FGkNNV{wp|ExOz~#9mN=P#EA_9M+4=)yD!VbbfYw8Ft~6&m^!bEB zma|`>eS%Zs7DfvSs}hJbi5|nn6b-?EC!_q9cw$#brHd%?>;3OCWiY;7L|#Y^otH*=R8~?ZuaL zPlLL#@tzJgB$Cv82gMHwShhfR=y_n=f;$;`ZUFQ2eWbUURQ*g;3_60Ep$WuAK$Msk zs-u(DAdIUAC{0|`uKmaB9mVld(mMdG;{(6XG7=qJW;XO9AomF!kk<$&q1w8w-f{4D zA8BosFS~=~73#}lD8-s4@rksU9>%?ATAlpUYOD_4@1GC2m;5d@+RSG(j)cNTHEnNb zdhq?=;^^dZ|J~8~#pS2t)5G_lzEq$H%iM3Yn6doKM=W#m5zD*-2_OoaklG3)As?$J zf;|mXOw_gmWficyurKt~M^Q6SsRW>D!o-YRtn}DJ(kqZC%~ebTRJ*|}aXKw?XWKGg zQ8J&G`SUXW$(Q+-6yz8sNeIh~79cl5$ziB+kgH!;%qP17LtV#wqt^oroga`rk!QhC z85ihQ#%s2ex=zx|tNbvI9F24k#)zV9bJaM@;I`7YF)`bplC`p>?oBS##!Xt=z+ z+uO%s=c9f^#DSTD=gbn&$`dz$-_H|{?GrMQXM3P(bOh1nA!RC!Jc29&cu`P+*9VET z?XT6md7le~UO^!Ojj(|C5}52rSwRw41cxR12uu}#RvV)2iT#rt&gVzpeK^9!$8S#2 z_T{Vn3pDZl<@>XXD`1{?q~`6`^)5lz2X^P&-we zglD1tGYPc`x(cVMYEakN0BI@I^77U~EiZ2`)Nq@Gz=n!UWOb(o2-TA?p*`CMdwOI; zNRY5+`3TbkHVh5m?7DUa5`hb)jrUPA5^*>DYp?Y zJ(Ie^#hSkMkr1%PpGg|M;yjqorRx3zRSE&V0+8}ZvO}0a#xe>b077y?zo;|s-krb* zm@L31z*GRFesh=5mh4-o z@c;^%h*^ef?|G6`Lo@>qTv>*Stm~m*JTsdT*!mY)Kga0i`dQU0{6G=g(xr($Yc29&#h| zs?1qA4#&lco-!2%Aj)RNWNSGJ9MINi7Qy8Z6^W;0YbGm$N3GC%Qm*Hy;K8LE z)yQKS5Ap>vT~N*{H5LuW@)dJDw$-sy??(N+rTwC2VeBe^JVkxWlfY5U*`26g#ydN5 z9TU|2J|lerYGcCU{^e-?t55^6Z2D_S@KIwSpXb}(x7hyn_#yJzUtZ0B zHDQkhI)O;@B%=W11GTvq(vQ?*WI}@Om7|8z8Nrd#0@s+ruXDwL zQ@pm==9!pxwvg-gF${qF#~QgXb~y={H;)D+c7;*wI1punCE-k%TZGn7Wl z3Y3HQ=ZBKsbTvz9RIWRzQ;`0G%q2IoDmGjTfh--Ax^_hgsrBGHi@@JuZN^|LM z@q5wFqe2ovqJ#yr{$m9}nB$Di2*MsiGSx%Eplv3er#4rOCK+x?EB_=h-LY>KmE7=zJdQ&22pV3fBJu^qU8om_v+Dro|uK>Oij+te5GGpUBe1# z4t2p#Q`Jm!B&sd0u2(Y2lsyBOz&r$U@e!(2feyk^k7bXw(#%0!VSP(wU#Sbu0E|K^ z-goE7Uqccf1zC2A*QvaW_5%GC3?&TJ3?U@0270-h=lDrD41|n8H>c>-=Do#~p^OnL zLK@zPFd!s?a}}a^AWckGhUF+_6%LG2R-8(kg7B0piAr5XgGw6mA%|jdMW&Wa>_c{- z9J0N|lt&J_oJbg@F=1OOzl|YQhHM@Wp>)lMB3mLRFHu-1fA1d`0)WstGzX{%C9pBu z(A*d$2{M!p3`hju@?cq}M2Uy+Hplr;s4NgwDC*eU=&6dBM5y5~&Y5;Lv6-lBKAxj!^O9fTR-nqc*D!ZR-p5;R`X7=EXf$(QkQzb6M zcp<-1ur$<1S;DuBH9`)k`i#0YEjqAKnb*k|yo+ zeorlrODX1;Z>W`}8Bks#<$x?dY-temL8&s^DS=b7r(??1QtStj_DNHpQmPeOc1nKy zA)AK3;XjWLe+_`)9qP-nXZ%a42cYninB$6r5&ig$b|!m3(~mK39FsfR(pDdz?U|+r zl(L@M_PP0|ggq6Rqy#K=s?Lrvw?dTWo7ApqUUA=y8Uw5O4Nc{CZRUKd0Yvxn zKWZq5Xfhw2dP``1X1OS^ryk#vkMAAudTI{N-rwIZTj1X?w|fzM8q@3PGEPe{%c!FT zkns=R-M3f2jfOvd_xc~){kx;r_vWkX`M&+_8}G=lfBO&rYA?TbEv?esnpNqWn*+NS z3fE_ASXVu)R(tg~v5eH5$Cv&lo{{<+T5GQeeT627COoiQ1Kq+-V&q?;OvwiDvlRsa z0=~cycaZml@Ro1JjzOr;0w9*C^tVYW%ZXTodEr(gV4GqE=Egj%r2qpy_}i1C!{hzZS6ec# z>Z>T)Q<<23rx_E%?M3JV8hk2cQoidTc*VL8ue!tCeY{ zV_Tp^cuK)qr?UKu7OakPv)Lq0hy5FrERFkjVS$*P6ip z{C~;zR?Xi}IF%Vwp6U5!+TdQGaHwkgRytJ+$S>PRv??S+Mm0;2rDzfE2^j;Km!MB` zAddS|7buvp!#{FZg-y7@$=hRz;c!Uboj~26KnH}mLXoC@LJ~wgNgJ*510EdoUVRT$ zm{ib+G|<9uWTlk9A`|kRLa?N$;+wNU<(pIFBa)VNtG$46K|}YF3^Hk;5Cb~7uan{x z5QP|1F&VBo2es2o!kaStjiGB4z|612SaUtuqw9dw#C5Fy$rVX8k4mQ+l=zReqsAON7Ds?o`J%CL1|#V z3EDj{n}Hff5F#Qf790oHcur`96nD_QH~~K!;4C5w(uD;Qo zTkMCW^Ci)PBnjZ^g?Bl&O|Y>D#6W;smzs%#oVMokWhEhgcUv`dod(iH!DvnjY(!mO zn@F$ijJMBh1rBD2D3S?M;edihK)wvz$WP562x2<2DSg#6p?6pW!j5bx2kZ`R5(7HS zOYQi}&$v-7b^m$HIDw)z;_N$ZXWVH!-0)4GpIS?!0I&Pw83qhaow-R5&t4*e0zK({Gopz z_Q?f{)Q>*B&ukeVsrhle*xxGW3a%IXTf8*ZmBUZ_@UbTAnECipBQ&y%aLv@Cv!ZfB zF$xwJD7fa_xt?GDiz+U_hcPD|O>(Udkkq1&Y#Z_zUm=AD!GWSpf>iKHuJM%je11r^%9+%OOL2s&M4yC zAWHnpn^e;zP^46vjw1bSluf8G2&fIz#(YV2Ddiz2r!1mFxd^Ff3#KzvwNL!8Jme*B zj*z?ei;c6BTNicH@n8PkAOH8IL=SQb_&@#OkJ|B=Yp-%V^02hpXe4A%goIimvWr3_ zA>ZZqDvijW{_Xz>v!$26NYS8r6sP>OdA1t8D@F<&queiVvHaX$ti0uaj|410+8^W_ z1wG|(n1K-OK}wvwwb)3A>{OG zz)xdDK#KiI%#G59r9dfqloF+>)KsN$ueAQh|Mfrr$A3+`A;npb5XD)2o%dsb7}I3h zLynxZn9D%@Ys-Kd+T~_;xlQ~oe#8qgD#t`dRPV$u9>H8HAJ9ZwTKTnYW>-`Yk z-y_6HIfxmyj!X3_m+Hi6gEqLWm1Ye3-&UpijNJW_$6~?x*=CTj&QwBFpQ3Z)PZsxG;FZ-bOa<2 z{L({ks|Nw{1_KlR**Hxsc#ny}3jC}gz6Wx2;`qJ~z-xGJfg)WMB-jD#`GlV*N zzivU=Zct>mK*U1Ee*b<*fUQ|}C?ezp;Pe?VRPNHc*mmVQ0<^gS0oo?u7h+1QGc^uh z4XSpAd`6y90o~@4>V;NxBR{M)mEex7YezI5+&)!o<=9!{_ zlB!2mfF=*6$&pOhP$<=gka9dM)V(}yC|6d_4wRz#?cq_5NJjS4XgFokET*z5_x435 zRx((=s!;+4EKt3py#r`so;1Gs ziVF09{ndB^&MJ&9G*Qx4#q2AZ0d@B>&EzHPa(g`ck5Z}_jqMKVJyG(wZ&k)NjbJN1Y~vI(sbKOZ?Wn4V8ZDW_4^6j_Z+@6UNAkB7S$3S)V5~_%{k^o%sjxM}3^<5XtrZzBo;7=1exFNg< zAy^lb4<7vIMpR{@1YUq?S6Rx*5!!rKZe%H6?$2ePnXo)NL?#QkRs#pXLc#}BFOWg? z)P)x}d3@ZprdSj;p(p~~fA{Y4Z2#a}gissnM+XOQj}AY)qo6B}VBE5Qe!{et<F&M3i%+3BWH~VT<`Eo_@6{J<%|K}Rw>xCVL{xA5z$rTG1*)8Q(;Mvf?4}%+ zK`imYWIe`FvQWh)TxBqlqc49uEWKT=?%mMTBVccf>Fgz9Pri8%;?Ucg=+dQ?My>k{ zROf^;O|D_g&w$XCXV)+Xo2WRg7F3*;sXrm|Rx@QMKc{x{uURISxVmAtZ@9Yk_HIJ| zaFCsCfV9l_ZO{!OB^!<^fo&6@_TZlg?bNf~0BH!8fy!8DF-S>q5GqaypKCRV7Ene^ zEYN{y&EDpqa@H6L!SbG_IC!Ff0@bFKgf^v2q~sm*4ZkxAf6W*^);qNkvdU}1nP0GtY3pA?6`8HbKT z#IIjo8mua4>d#ghnqFldm6yqdh?BeUM=M!smZh<=zqLn+v%INkM=AArY#=cTjp zf#q0tr55mJ(nC@o`FV@Wn~+*zwOf66D@THq@iwkm*<1sFdfrUK&R<RRB^~m$X1e>ewB>+iD;b)EU8+=$c(e~P*?%4>S z`>RQ-7V?>0U4LpU6x!Cx$AE)s7ZZeMI!t~mp#EDw{gZ_+XYcmE ze+8p|3-n)6DD&}n@IitFo};CA0DwS$zefkFbyqce>l4OKyKqHsm0+W@1RI?txI^?k ziHy(>TonA-0RFcFkcYXk9S~Fse6Tt=#Uf zOYoQ6g}0|%O$d00%{HoRK=+0-U=vR6m#J1wS5z!vS-a*nS;7W`Jvj9NgyHOBa*AXb zaIW7CW&ci5DsiQpPUeY?Z9Nt z2t=x{A?3Zp8{vtwNvN!CzI|sPdxiegq}$xWB!!u`R}BBojx3wGV_FqtIA!vKbP0SI zP@esaN+p*3f|*bW?f7<1VoC@?bgQh<9V9Y$)>UVT} zUnYR@%O~Wn*rD;1kisF?Ru91XA?7C`igC!g2SFtvw*VdHM&UV+kur~7|5unXRJzhg)_T5r_u=RQ2|I6>e_evC_4srf#dRx8kZoMj=9eI-HlLb?-D!;UIGcEC zHp{2}Si$bXT_ZxvHvpLABedtaPUOL~OaU#Urv_@(;S@w_kQE5e@sJ2a;ttLw(>94V z6_xIO6?aw#r=k9|f^AfpQlAC;rxR?uZ)`|7HVoZ*jBN9bkCAP@u|0*2mjFZoaci)^ zpbB3A-w9+j$i=jD7t~{}3$$;~0L2)-3{b@5M3Di1cmxa%7c#Yq@#Ok2iH)a}?W!U3 zEZbjHwzr$8kTaCjQU=)4C|!QHhB!s6TE>Qetko2dr6y&Is|M%0VKgI#j}a-T#0SIZ zz-+|`Ch1W6-+D%yYA~muY1hn3ZBC(X!=$+pz={n?rAIOw7sFF%bCO8=L4*>>mzXF* z3C~*)(#Qw&VYMj!4|M1T3^6&)3OxbVn8F>VRustv5Nu&Z2re#0F}(a6IxATy4E{R? zqA(~on4Uxik^g`8-lR8@Bu&%2^H*4+Fd6PyTy$SNt0#j_r+cu{w{+4oI%+WR)|Iqy z+rk#7GY*41%xMnYgRa5=)zgi_^kL9IUj}okQy=E1#2@JT3G;j+s%C0xre>yedPHUf z5erE(RZ&q<5tVOwzxORXR#LE$vNsha^B}7ylMC_##gp|^ZsMt`KbGFbLj212qghWW zwcxM-l$%y1?ho4+%RK3luKkLVYP@bun>^k>*FKd-V-0QVD7qO%y~BQXq?<^aWi`$1Rm=dP40F39Qnu9u)G{`e zmo8oj@6@3iHnRGmyKh|)c`NFUfz`l1XuIfJ3k1iLoB(snO13n~xLP-qe4N#)cGIy4? z)*=B${rdT(uAi{E_RRDT z&CR-_#(JObefD;AUNl;x5*FB~7T46tj!VsvD{e`D*jK_R=aAYAAXpEf;#Ai=51s&MGi&(i_fFNka15On|Zacf?2_ zEbi8IYD8|bWZzPQ^uwS2LAV*_1Er)Xk)a&zba_RJCUa2hL~3BL{2}xY71FYa$Pg9| zsR-R1>YluYeGN$O3vnf-BLcnc(E~jsDeTIMGE1D%N@LxEt!t#u#(j;WOHJ~|jopZt zXMWf@kAzz;Ry|SWyny00U)QD; z0xROjmNH5TDy;^Bdlc!&;(`u$p8&;J?pcTB1lu~Xp7U^YR7QH&r#rltCnbU5zDPGs3t;|W zsFu0SNir6y84)Kw>@2;D#`2hn?i6g{<;++}gQ}%33xUNy9MndnSX}aTF-eOhsTLuF ze7ukuDmRqQ0~zD#@RFeuBE5&j3+RN01DU$I>2x+$SI`S%$mE;~Pao&Uok+H+fw0ub z0FNS76e?#S%2m8$p1gb_Pa+#g`>AT=yeF|Vk&x9ej6`3JN0rGMnjuA{-aWFfPGn&& z-)X+|KA61k@`!rF!Q}ztCxq%tM^Ch7aUaf_M2*p4p4ZlQ%4RE-#42`GJ~L@o{jCPz zTJuuQwb8hnq&TZst(DFZ(J5}~N&;^^6*ucVC?`sS?$ty;%nw8))j1DUDrZVN7Hnu_K~$d(lgZJ^SH~}(oj!l{tbv$bT3MmoncF~H`RI9MTMImt>9kAC z&}f9l7fm!1J*>Cd-f63C1INRr^LEeiBF1T&c5ErH&O#-}g|Wm7;7x$PfdaZDMr71B zh=-&}3@B1XL;zumMoN&?iK@-OU&(_>vg{LLiC(?pGXpD%Q%RDHuEDLBgbq1q(LPOJ z5Au^W&fHquBn%CFdiLxk_{(>Xk1sBSu;JC~i?bJJe^(r|08ehRuRjx$-OiX`nSD;< z>~n58`hnh<=dzxf!RPlXw;~JYHl5W(x6B5JfhX z$JA=rNm?TpLAcp5EbFdkUrpKe7PD_R%fwG%fAOU4i1>TP^UzcJO z_w)5O^L2YjDIVO6hxD=68%3k4-aze(6#`IEU9dKXkj(WVB>aG-RQ# zkj^v7d7+NAp2@zZleHL{$76sLN_GKn zq|{p#&k3R48D1arE2$iQqHNtU-#(2#h#E5fK0y#c1-raUQFNh{6Wk9OU*gK**;LntQM@DxW#`1D^s}`1XcT7ZPv!ti9X}7JtAKp2ew%FB!ci> zI8;s!HRq~~tU_1;_?V23ScuN}6Sz4q>cZrD+-lSc^7BA4WYfOhl1a3FEV zOoTuyz$QfKD@~-V5j}x4@{s_GLhz@d1F9hUF=lVgi@Be@Kj!RhM8gDaW^d4D_U^8B zYvG%~_z#fZK&Bgfm}}=?V314}BpFaMidjqumIEgb2>zfh8~Opb;{=LZ4Hl=dB8(uDqzu@j;Mj89R(BgHnUdQ z>%1WuC;+;bD~bqGi>QFZoHOa>QqPE8-IFY%aOG>uU(qX(m%?;fl|eWb(Wo3BU?L!6 zIq9G~)r?G9Yq!)P>qn{1OccTeJVtRQ$nMRKsuUic6mJX zNR>{I)?2$7BoqC#3W#=j`C@b=qyG`Gy695L1(bA^h&Jdp_Y_N*K6E^^8WiOpCzyWh56arE5#@#dS;jRx7>fN_$;-?^jJ}h6^$)Q zq~*$cWuMfO(DZ4AbJ=-we%>kWKrPye1oY$K05>>a@8B+cR~ac%zJu=fg$^`%v#dxM z7BjjKw^LW(+=>_68Fdq#h?Nyp+`j6Nc9AclO9j@iP9I6;ll$Y_^HG+uDCq&Mi>ft8 z_Eg4nuGUe`wOcsFkCD$3FKj`#_D1)s!8P;V=pCmu>Ah|_ZTGZBf+>|p@L@`UWD*C7 z1;`MFN}!O~_m-K25M}~8_Cv~oky=9h*+$y}k`b3Qo3#v)3|yb=M?0I!-+_2)*`~s% z3w$b>(J+JI*GWmr5FUnlD0NKN%hOM=sn!IKpB!HtzdQft<;h*JpR7S$uC?77kd~Vs z`)9&k{y9~_eGZ?;I&>Q|JeFe5P&x#?Bg%yq{QzR)$a@}#_ zAmrhBmSKdwmpD4BA$h&ulY<5(7$GBg%_sjM(g({??yK%b> zZ?$a0TdmsNgKrrG1TfKuU{g_pvPpJQ7EQ&rO|Z?dDS`8#uSReORVgDNaBOmn7qCIY zIp!t#a`4S|9E!->v2Sb)UzkMRi~}q9ENk}jiK@fTJN&jg)rRV5v`BE)zF5V`J5^nw zRhQmZgbnKxoX67MvPXY{rCI7)^a~8`a*lH@Ne$LGN()-8=td4&%d1tp0jX761;(qs zlm*iZBybfHFOEuFCiUiBnrL6Fw50Jz%IgS{adIdWY~nI!m232G1Y9^#fYimk;S_*H z$B9c-QZ!RC$oQv;jL3eW3a+u_Dr>2(%K$8+=ns~V%<5o`gqa$(Q? zv2FRv^1vpKY~54ZURJH=(S#twI8r%8kt{jhCVaAM3ynP3PxM(-4ajMA^y5;SPOudM z$qRpL8q&POG~{8kted$h4_RBa@61hk$kl^)&qIRQ@liHNJrh(9(zk%ws1pr^+sTQ1 z*e6pDI1eZSIL&>$V9190M>Dfkjx0Tg?o$&@qF6mR^K?XwgcMdzO z8~H@-MVG_3vs-K?yA`r62e~&?vDII>e?g^eR&dv(IkE(rl9^$P6%e*q0J|Gg78Emt zEOL)bP~c)(Q7-BD8F?T$prA5?t5q~$Ttql?+wuZostFU}enR^^HtVI)EFu4nnFX8! z%e|lff7$cD$-@X*%>SUp{NFtn>IHDaXCC}!ejEgOY*8%i8eU{1B5dd-2CM-QtlCH) zIAX)Y6z_tZ&+P!>KR;Y%+ctghmYSC7F6VzuxAT7f|2fb99mnOU5^fowca7ER}9Z zTMj8wEXyg^x?Rh6bLiljul2hmpIH!2iKY!5FPlZUWlUC6cP^jj*{EvlPQ^=#aaW4Y;B~0Hl)6}7 z0Hi{n+*P%l6)LEro_A4=TNLras7yuP#t z?OeRNIDX!F{pReC$0y%(9=v+`^f^3koyXsFo}8Y8KX|xTbaL)U!6W zMW6d3VIlSCZDv@*ks*+0G<4lEr0_nCA@Zbp6i;LX#MPG}n5!(w3jEHegG4$~8166Kk$ zU9F5TI2a zXS68C-d9zmBC0j7$W}LSDL9D!a6BSM-|u7^7>q}*>500JzyE(xK_&Lok!|7hqKMnP{{3riqjUcDwY=f? zPBn3xt^_T41aOPx1wwBNwrd^<4kwHDywZ!q(R(>W%2TsR6<;8!;bDohh=&ZaN-C@7 z_N9`3G`}@fFj_XR^XQQTZW)H*$V*!jboPNL>O8M z(Q>*s63`+HS{w#bdNk08n66@eNV1Y)P|OCon}7e`%Da&i!`s*Adncd-cpWV+rU!+R z?!)W&{X=O$BQz9kfPPk0WJEbhRNXjwZG6U!=r>R?zDsWvI@$1AU=i|a?9V?aqji)KrT0xzt_5xW!LZPS**q=vK=gb@>fJDy-Iz<0} z&#f=wj0bwhI8F~F67)!-ix57@W>CCTma3{1<5htg{fmj`<)bUqGSHgg+UMZXT6HBn%oN0_D!6!77t1>Wdb4DG#mYd2!#2(`!9( z^zF_K5;fT*f-4xc3JpT#?Jkd()QDaJ{jP$}DC)|7R;V^iWryXh!gF3cm}XtUMu5v} zd8L+S;o1P|x+8X_pDYZ&(D9E-OLm9Y=*n4*@z8U9WUIoAMeEJte^k_N1F(n@yNmFj zZfbq;g}sp^dD&`yr>y1;C622{?;iL8t!||;h!mleG~pJ8XV?o|H#Tz0FuWup7-Rtn zCR61lrU<6NWgk(e8)R{qE|YqpJ{5*a;QDT*(rT%1hajvM0{;ztc*B zPAb8u70R9&Y6qgkGR-95&?F3&5j>XRhk@n#_mgY~xvx8%W|f+*+BCCT?Uq)n-O_5YTgE23 z=#Vd`p@lpg)^$-ebmy0ZPFAjOjP91Oiy{prUUt8eYb=K^R7i1 zsaJIFcgtJMFss!Jvs%rt7P6lXgx;thMh*`w@FQDxw4!bAdrCeBvp+qr#YG!Sq ziE&DH+A*R2&H^j7YrCa^__6{F-_NigZMWPs$;?)h%xpEucHb?1%r7!T3cH${aO^mg ztis=98Yo{0{rYZ6x;MO^WIy(988j;JwAwAr zR=Z^zsU6Qr4U_H9Ol{PRLz1UCT0H=(1GKqg5!oTniq>nKYjIb^r6@xcHDe<%(wfwc z<$#$4z%(~w#Z!yEHRx`|MHN#r$mIA2t9UHWXmB4>zTTzl{MgX_B@1{9|Wf0pz zfMh2==30zPL2VB3uH1|Klu$=~wxD38{JZIIe|Rc6V2ND zCKK}FdeM682I^RdRR%7Vp(wev6th-AyRK8&MFLXSKvyy6QWShXKX3ijIpl?)JHQN( zFo6|ZvP~*SsGC!V>OA0@D;HJA6{vhNC(BX<0Ayr9a;O7eNPfC9CI~H;CC_~OT;)!_ zUVzS>9;z&b2TUQ zDn4L987N`i2AZNJrO6b}B>6w;ii%rLI`?X%bHY+8e+@`uUNEnR=W^Uli&2)U>l-E? zX*J39Wt|7}Coha^)o0gNPpD-0&jug5fIDvwEAP6+orwW|= zm^|FO9k72Jo`3cGU6WC2Vawl9|EOGzM>{2?)}ET z2;WltMKBk@C2&nM$ekc#(<2t*Q|ASXjb}hr)|;Z&`a*tF>pfT6?>PIhd`1$vR7O2SSn9 z&B=4(&JpZLMR`5JtD_8!d^)HNL-GTbLGU)pD0BAHSmM!3yPoN>L*B2w`?Yt6Yws33 zAsd#R*=pIDt(IMjz)u?cu`qfX$inBubP&jZA~7;u2ic4qQA5_5o0AOA-Ow{Bpo??g za}z%Z9drkmql>oBAx3b&?CzJ{-7dQpYtCur<}q41ct$G+&uHP`2{jf%AU0fCo(ZKV z>YaWXMr?YS3%6FpB0~uTr_QhwsCO|gO)qmfi+`-h2T5=L2a@H_*etoxOBzw&-^2XEurvs$e^tJT`u-NCag=;Q#a23CaBS^z<$lOz_^ zWO z-NCaFZ2-56uf|Si!U5@GX&Ph{Y?;p#!8oxSrXax)Yw%$K5|BBQ9CBAmU;vocmYpve z5ckXOe%bwm4qg+*5W00O96ULC_cbR+@192>U}7RQ>Kh=IEfRNv0z^dx1hX6FjP3)6(ZKat}GcyAW&$|w)*axO}zvk{8yp|5$hLvZxT6uP>mAAWt zm$R*9l-0#v=#i+iJXpR+oujqIH(@NcCLHAk@@OkJ?vQaDg|@F=FcUv~G)?niR)+5*`R zN4?pk3C*s)T!FO1QEz+-q`mW6wfc#;b`3@!#@`BK&VuTv0IUAxquw>kQ3bd@9*#zW z>(*3t!@+^R&gu}1(4-z~_Nj|ZNNnU$f>xhGAlK{yj?n9;N@YN9n8mpdD0yaY~C4V5~3K4^v8^u~QR3qe%?V2_SNzK$F~cwjAxFSeSB8_Z~L<*)1@6&_9J4 z_U8_;pRWMB{i4}DP?sfC&Xe(lXg@Y6n-3x4cg@6u0VHzaB5mvCVS+$d zN(h^Pc4Wb;6ACGlDEB!~QiNVag*4UBtf$yF>U0x6(E|5=+5DoH%})hupHF|Y{fgN= zJQkW3DwnAxlzE;FIRJ{ZLU!OIg+~%La~IKqGK(4CP~Z`Ot8fQeEd8RQk0N5Kn`h` zlf^{Qe8`Q^0gBLL1{Of9IbU&RC>RRwm(9I1^Ao_8=hNS8zhYWcrjo%7;ns^FS8%8_ zO?(j*CmfIxN3)|eL?Seg;ByvHBQ_<=r9u@&5QlPA(9Y?KGh@4!cfVrpotfJrjZ3iI zhwrhCTE*XXBB`ax1k}RA5D(x2q7GN^TWt*lPRp_5cRI9m!G0 z`U!PRyP~l=1nQ|IVLA2RteuztrlQ=fTf~*>xwbO08<>c5isOa}#lI7$1Gb zKpvfZb&PDl^H@ ztrZY%Fab8a*xrR3?0}8u?QRUXb`p~`GaKq3{#lvu_1&Uu_U z<{DdI%Xr<6bEgTHZc#ahMDHya;EvWoN4~fGtEq)x5O7mt3bGKQYD4hiNGp#<; znmiltmNU(}DUV;&f6G4^_AV9+F#)GiJ0|wC>TNt5OQGWXXz*=U*`^;TQHy6i`t`fclc73SRq@Hu zldP93ds^&r)r^#eQJOZtXFlM-AO7@@sj6Z8U8PqSrE7ED<>F;6StRXs^YT&4C`H|Y zYI|kKbk*f>pgg5&?;lVuIEp5dEPYt%zb->mE<{%u=mT{(qp|WKu-YeLNXtM@vaHip zO?IUIu``z9zQfKN`O%^6YpUHY_m$bcdIJMpoVauw>d{9Xu94E?dH;D9>aOzc1_1s`WLDo{66ZHtAWIE&7-ISN-=Es!o^@|z7r>faFn$qq~XGk$% zzx%!Jlq#y!w}6ZD!=L_h=jBlDU#5s0CZ(bInrq4f&pt*{C_2luSE^g5*ilYmgeP)r zckVP*@8)%nuW`+~V<|NiU+X)p{-Sl}RnA0LZmtw8UP*Vf)2Cgk`OK&Dv;HI1Zq(tB z{9pb0yV-F1+tO2D`S)j38s4Z?&vfo8uVH#ryo?UYmqFiOI%QS;ZP2nc4mW=XaDTw+YTP$2fI^v zg^xOoL&Xl!i?mZU;q6Ow<~knU9F_fDz13|e%)8p`v<`YV4GwG4RZHX7QGfK?&MD=r zYUVZhty_-X=E!ckjg5}%rW1E`WH&akgCkoMj-JcuofCEbqM9`BPLp95>+E&f&$4G9 zA6wn`(+~aW`)A)>zkS*N<~#p+I30Za_WJa1j>=EPaCI=H(#mk3U|SscW`FbI;t z>_g$KVM69=_z{^i;Z?TCG(-8y^9M+aJatM*t?Ro!?~(lTPY1K^T>e>4hwYvqmiqu3 zG`Ttyc~&9B$QEAUnlEb}peOFO;Wc@H^RHJuz)FkDF5vu&f_>#|05gm5cR)Kb;I4!$ z1Jl2cpQN$?qfy=eTeKj4(xzsn!(mU%<$m;DJc#lL>}-6V^HG*S;lbpAhlI~LsS|e# zQywB4{f%Tl;2r5Rtcmg`0ELLlv3FfMj8}?1MLU2|?>f4fblwl(Y^kQACR1^sN@t9# zCGYVpN=I+;)F#U9soZ;!!l5~FN~GglZ;nRlQT#L^(XUg z9(K;Ai(HTDxbzarp59(zf?!xr#W~ekqRIQ|a8&K$KAxo9j&kg>URPc)l2l3~mEw^f zINKE_o=1BJopWKhi6dnQ=}!LPO-KL1Ip58?$$JJ?7(FC~r*1{g(qu0B&fwX3@%klR z-FPU8D6TG_tJlj!IiC2DLJ`xM$WzUQsfDj-<&WL$S|1B}4+#wMzLMTdIpsKkQ(^=& zVO`nBQRpUJ@m}>P3ipnpp-~`kXQcESq#4MUlB+rgO~FL<<>EpOSuumq?=}y?G`#J3v7({%=on*@y|?Dw z{*E&?NwKd#W9QACEJOqZIWkOOW3df_EQm!0FDE-;c(xOg2n$ibBUh19S7rwA%t%?Y z8-iU%USWB2_dx&KE9o%N?)n5d_NBc{_H7sL!6=bFiVi!9eRMn`&|;HJ3GKk%b$2f` z+UGlZ!+73}CcB36?ymz{eGXxEVxdUOk24}SH_7fy%!v8d z>jKGTgUeRLbKbqO0mc&JowB_buhpUh{8_ge^md|xe!Z>(*kUA^VTc@`i@jEA)5{n;>3w|zCXV&%jb8$cV!HAe}i-TMkBXZ@?% z8_ZO=IJfByW+}qXu;C3>wz&KX`-9lMoe494niNcqlX=pKQNidXACSuXM|lmTWAbb) zq?1t}8&SQ%COu5V!}_ake#tWUEpEkkcXm0lAq7<>)~yg1M&xkt-sPj1;}1GfuI%KF zuxLt>Wj37(XQt$n_I2x5lDZJL;7VtlI-{=gaw45T)0rcQmv<#xE>A@T&(&YjcRoo~ zB87ZRkwC-`>h|f5dO?q8iro=MM9z!lw6DY`Q@KFZl=^IM@9Q$#B_?BMF>+-NDDRFZ zD(jP}p34Bvdg(kjm2DZUf=N_b>JG&4RDQsCDBPcb?<7$*E(b_C5qKwKl?RfMAlI({ zP}wNmGOmQ#p%df%j%mnE&*r3=X7Q)a=ZR@Q&yI>$r!t_t=rZMc%C*JcR3O6g5%EsX zdYqwC8|UH*%G}U@(zl5ai9)WSx&F1}ArBa~qAhu7YRpR5 zr~XurI?e7c##Z(GT6}jId0Zepuk?uXev%znIq8c*SJQ#lV3Fl45RU2!s$!beFn#^8o4qIBQFzP&N5~68%FLx zQ{bx5$p`r*(hj_Wv(on2U{&(^jTNBol4@{FRGcXT9G1G1k_=_IE)MS1V?% z#&JYy92alk>LV;B`TVhb=*D9`*u`eYno>(rX3NTo?B;sy=!LG=ki0=BCBP(=A(SIk zFN$8NiOILA04}Cqmf~SdWu#e=mbC_mNN`ZmHg_acrlx6Bh7OBPF5XlM*q~4j*e6$q zw}@9AtC2oY@1}E2#;m=kud=8&z1rUx&#IiV@zeuf&)jHmdBRq!=WDfkzE+F! zx;Tj7qslDbmb@|eQ6C5aYT|)SwKj-mICw4ibWJmb%MGk5{xWBn=zmyQ9DnxbYk(l| zKlSzv}1lk9JxkkX*k4^y*5wP3b%klmVNu5dev?YWK5*q3ux&LFDB|Tm}JpdOFoeK9p(!QRXV29pW-?x zr$pshr(FpMtN4K&id}Ho6)^?^CnRZULu`_w#%`X0RghTxeYt?XIl{=-;uMrUma}x{ z3i*wtjObF*55Fi8+v`SJtMrMQJuPt}Zh3XkIQwgUs4u?mKJ7~S6YqL58cz2y4|V*5 zW9JD#juRU$OF8qR?7HAfWtA@Oq0GZd-?`0h??xp(lT7S5EKuBz0-UO>C;4(V(1I6Y z2$qAQqB7(}8MLe?Y23;j(6D5M08b@@FW4ySzcq%@Gn88=hLW)|+2zH5E`*+4=P2`5gE|zuJn8yu` zhHZN73;ms((6CcCpRn_0ZHT%y)Hw!>?6yU1qXDZZ8=sHGjrEP@0oW%}x-r#eaUe9i zwi6ksl4in!SW^ZGLZ5tZ*eVO*ZYgRkHmJ&GokJ`acrch4Nh}KB!XIzal?2-Xd(qf6 zi(7p7H9Q4nURPm^^(QahKEHs!04jr@m=0kp0m9l@@Ry|CW`%dgc(q@l@sX7IeoKB5 z^Dt<{+rp6Jwp_V^+j8XwyQ3>}q=Nz4#+K~$ag^E-g?WjAz^Uyzfg4eA9N`1|=qFPD z3H*XZo7!UMB0Zd&d2Qu7aQXOmyK;@H<$1qy?^o{UUb#;5%5~bVT&L~I-JSChemXcF z$@V)&?#Iw^$N0#W5eY$EOp@QC;1-bw#{@VpSb9QUpa23N%10!LYYuV%c);=8yI#5G z{mQ*xxu0d_ZjN{0r^tv-`podd(G@us9qd^BycX$j72eO@KxJe%Rxj&j>*0*@^SPT} z(7G(eFgnVCIXgS+WO&so3et~o5w#cmyc<7z@~=CW z63oa3L@Bbvx*vGF1F9Fdi9c>S&uFI!e75$O3gk>feD%ol7>>64gxvQ;0t95$j>@}! zF-%X@eeC8GZfv5hSGc*cwQgaNb*Y}Zf)B2I_vNJ~&S_MrEGl<4x|Z&4=(e^6K&wlq zvF>Nxp}Xtl<&kN5h<*|C8U=~DmnSC``UcxX`s4l z*nS#zm(|B`r%>X%d|#(vjc=&3?@Es+m)TXpL$~_*D%%AAh8y13fy%wCdtXmF(qQL! zH2PirrTwIcuS!rc$w&+DtBQ@AiJ~cprelCz)P1lIHT_-bv- z1<7)??j?{aAd{)igvS~}kWhRf9}{H+<~Y6LjHg7O6-+}NeK40ho+`!=g;M6I6J9}M ziN=oA`$WKU<*S-+UkoRL2vBirUDu+(6hx@|`bk8wl!7pmuk>a1D`hWoaRz~y@sL3f zeBe;ypD8MNKPl-2z-dy2^gZ`*R0J#~m@9ySQY(6ntC{N9I8{fz6s$s7kE6q;d_9&( z^7HZ*Pt|P;fSb zh&N6QIWzDjx-YmV=0gu$-Sr@Ov4lN;g`kw6+BC-fr{6e1|3Z`)1Tr&iG;Iprf1`X z-}O)Zd?zZT)x0$KnIGyabVk1x@5j&XyZaq#NYLA25jd5wiFiyR| zhD}Pe7~E_*5gmxC;@!r!i!H8VkEEFqq&YYj3pQ@c73TWLA{vm}Nfw4$K6t%7a?JLB zF1En4*XO01ee&w~&AD=9l@nZk{PgVQ>W7un>R&aOI;#)o(^38FW>_QI!E7VlNu%f{ajlv+c8cZC5vZAC$$TjicSEt+^6kjyVPl26y{(1=qP{=p8CFiFHWQ*bv({oh7%k?*?%6a#@kHwgk0I9LIslfEgebg? z?l&QMKjc3vjl2}T10<0hmwcHMAo9Y@q2ebFmnlL`$2M%=YBL`!ZyV#mlFj`WI3IQ> z`|0ucro)(W0Sx^}9^(WcMOvx;X*gnq99sxg!;R$FLLaf#Ls1QO1iK-q0U#)y*X4&A zJ)p2U%f8Er#&FLDF~7W5wTq+TBxPI*;20C6?w_3If}#a*F~LW;)Qx8fThhxuR$36t zP5v5PjV0Vs^6=@e_r;CL1%)9-nBZXqOH$T6U~fqUI!Jnf5d#)J0T>Mx!S@=Djg5Ud zRpBmy`eK|+1sF1$Xe4m~O)op7>*>3&omKiap$xYGqfnoKwb8o)L-YIWrc=Su{AOMX zpM%`jC7D>dkF>`%RV)bC8Y`oZ`)vU?my&u#Pc4q2@*cQf#U-a~^yp)^KkEx4z=tT| zy1ywMpG9{b%?FC{NhG$aD9Y*ba8Qo9CT1w^XkQuMLNrm7X!Q5h$clCS5&NwpBy|c> zq%I(QX?uIZJ6E|6h#ug zV;%2&(P}@!dV0KnUOcY`Z({X~X5IvS5jHzyEY*S42SigQd zkBj$jJ+3-`FBk9WHL+D{FBsB!zedr`2(HL}w%jeAvOccnC)tPAp|QnLvUVsnF4|cS zu-?RD-n0R|vo^7qZ#b(3Xuu6o_5sBM6$2t#u5ZL>lA@LE;Qo+zbVHmXONf6lR}zkK z`NWM0hsH2W*9hB?47Pp8YULB{Kw@6wWQv?L_ro*^vncmX6j^*9309E9u*xMAmNr4I z8;0w1hEm%ktnCei(Smro#q0@gHG6{HXOEeI);7a9hYlKJ7K|9;oH299%~Kf2ZTx66 z_hJ5n^DhvN4cm|G9ETdUDa)wO9+Kv!bGMmm0Q#i0za5kih^>gM2y32kT%3?Uyk&D``dU=gUq+F9go9)yPg6BFUz5y8e%~eWK>?x!+)E+^#e<;2 z)qR13UuGpI^+byAr^>37n$fZDDo2068611 zwJ3#tgwK)-1pZq14d(rj0&OMpGms8us@$^kuT=?V?S!}IkM#gostB;~6(HgIN)Y1n zM!Tx2gVt(IsKw31N7aQq>1bjChYa|I-t7?Em~= zjTQRQw4)>a{lkZg+Hvg!;xS9*1c{oVDi)zgMDy9HCtj(;J?y-lsIq^R@lcr;sLm(v zZ=^BhH}C1$Doqj?k}~?iyb4`CSVf62DlVi3Qe8wm_M|6F93@>FN0&AGlt1+5+DsIT z@N8n8h|vy4N4Sbq0Xa;0(Ug%mSJ}1ZkcN%Mnp0MxWt>>t$D#pQDVpC=XZ<*FJ&25xg|(tP(XWTjx5KzBdCp4O zKV9@w7E6sYscfMrMb|A!5{B{@rdmO7eqNNM9PT|Y$;?$k>^5n#%}(sf*SnU3sA*Wf z+IR3tE62BVK&xkN@;24ntGk$T@|UCjp#YxNyOeT7sTtB87LMf6!lNw7!sEpJ&i|MV zK7RLs3wiIOvHRV({r3@eP0vg6bo^mD9ensO8;2VR#$A=ewV$bD5?pUs?C zs|W60WkP|a<)?092X0K2HbjF4G{1mvl01pPyBehFA`6@=1){J$Epne%7`^Uu~vTEI%Wm0ETi@Y)pi{U^j=c+HwSc>jYM6yK5}JHE`7i zeCbf=-9qKpNyGU%3^;Carr$|% |y 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 z%0D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

qXv~KnyWRAV)&M8Bd_xBFjpahL=#2F|&q^1xW z9xp+@AqaW|$byFb{qh)nFW*dFwbK~)+2d6^d%S9Ak5Xbk$lPAYw}uMg#18?gLGzVB zz(Q3!**uZ14k7gN5|V<@eWe@@sFg%zxGEEW%l$M?ab?>wo!2>~TtNkxsRz&ThvHY*AdI@~J* z&XGG!n!`Z4Q>H3xTwlu%HgwkuZ7AiDy|)z5F#=3ic%)M5rc79Hb}K8$XRDuP z&^%k@rS?;wV_TmR9Ll7|`Q=(0%pa-Doru)%j9*HNzKtrRKFAeB*Rqk(Aa&n}D`%=L ztfB0S(fhJ4h1S+7!Za1|Qe};GpP!ow-qe;DnvO^aRioX?1(m9fw<=w;wNOSUZA*mz zWM0(uY^I3qX5~EWyS|}bi}LVY`-O#b_uerq8=|r9df{!cb!fhP&sgP_hPFj3zm!+< zm0r}m8*z6l2m5U{Bd*`573?>7ElpoFgna!5=VjX^t$mmjsv9JQ*qXRm<(7_z>QI3a zo1gTeUI0yJUKQ}qaxg`24Q9_2>Ji*knC=h)j*=xGCJsY)ePiO@3cPN<(o5iVijbV7 z@PjIW8W�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®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 SAs+yfclN0(J#0Ivr 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 literal 0 HcmV?d00001 diff --git a/crates/getter-operations/tests/onboarding.rs b/crates/getter-operations/tests/onboarding.rs new file mode 100644 index 0000000..1259f9c --- /dev/null +++ b/crates/getter-operations/tests/onboarding.rs @@ -0,0 +1,290 @@ +use getter_core::autogen::{InstalledInventory, InstalledInventoryItem}; +use getter_operations::onboarding::{ + apply_setup_preview, preview_setup_with_transport, FdroidCatalogTransport, SetupAcceptance, + SetupCandidateCategory, SetupReadiness, +}; +use getter_storage::{CacheDb, MainDb}; +use std::cell::Cell; + +const INDEX_XML: &str = r#" + + + + F-Droid App + 1.2.312fdroid.apkaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa123 + +"#; + +struct MockTransport { + calls: Cell, + result: Result, +} + +impl FdroidCatalogTransport for MockTransport { + fn fetch_official_index_xml(&self) -> Result { + self.calls.set(self.calls.get() + 1); + self.result.clone() + } +} + +fn inventory() -> InstalledInventory { + InstalledInventory::new(vec![ + InstalledInventoryItem::AndroidPackage { + package_name: "org.fdroid.app".into(), + version_name: Some("1.0".into()), + version_code: Some(10), + label: Some("F-Droid App".into()), + }, + InstalledInventoryItem::AndroidPackage { + package_name: "com.example.fallback".into(), + version_name: Some("2.0".into()), + version_code: Some(20), + label: Some("Fallback".into()), + }, + ]) +} + +#[test] +fn preview_refreshes_once_classifies_fdroid_first_and_deduplicates_fallback() { + let temp = tempfile::tempdir().unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockTransport { + calls: Cell::new(0), + result: Ok(INDEX_XML.into()), + }; + + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &transport).unwrap(); + + assert_eq!(transport.calls.get(), 1); + assert_eq!(preview.candidates.len(), 2); + assert_eq!( + preview.candidates[0].package_id, + "android/app/com.example.fallback" + ); + assert_eq!( + preview.candidates[0].category, + SetupCandidateCategory::InstalledFallback + ); + assert_eq!( + preview.candidates[1].package_id, + "android/f-droid/app/org.fdroid.app" + ); + assert_eq!( + preview.candidates[1].category, + SetupCandidateCategory::Fdroid + ); + assert!(!preview + .diagnostics + .iter() + .any(|diagnostic| diagnostic.code == "setup.fdroid_unavailable")); +} + +#[test] +fn refresh_failure_without_cache_keeps_fallback_and_stable_diagnostic() { + let temp = tempfile::tempdir().unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockTransport { + calls: Cell::new(0), + result: Err("offline".into()), + }; + + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &transport).unwrap(); + + assert_eq!(preview.candidates.len(), 2); + assert!(preview + .candidates + .iter() + .all(|candidate| candidate.category == SetupCandidateCategory::InstalledFallback)); + assert!(preview + .diagnostics + .iter() + .any(|diagnostic| diagnostic.code == "setup.fdroid_unavailable")); +} + +#[test] +fn refresh_failure_with_cache_uses_stale_fdroid_match_and_excludes_fallback_duplicate() { + let temp = tempfile::tempdir().unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let warm = MockTransport { + calls: Cell::new(0), + result: Ok(INDEX_XML.into()), + }; + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &warm).unwrap(); + + let unavailable = MockTransport { + calls: Cell::new(0), + result: Err("offline".into()), + }; + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &unavailable) + .unwrap(); + + assert_eq!(unavailable.calls.get(), 1); + assert_eq!( + preview + .candidates + .iter() + .filter(|candidate| candidate.package_id == "android/f-droid/app/org.fdroid.app") + .count(), + 1 + ); + assert!(!preview.candidates.iter().any(|candidate| { + candidate.package_id == "android/app/org.fdroid.app" + && candidate.category == SetupCandidateCategory::InstalledFallback + })); + assert!(preview + .diagnostics + .iter() + .any(|diagnostic| diagnostic.code == "cache.refresh_failed")); + assert!(preview + .diagnostics + .iter() + .any(|diagnostic| diagnostic.code == "used_stale_cache")); +} + +#[test] +fn covered_fdroid_app_is_not_reintroduced_as_installed_fallback() { + let temp = tempfile::tempdir().unwrap(); + let official = temp.path().join("repo/official"); + let covered = official.join("android/f-droid/app/org.fdroid.app"); + std::fs::create_dir_all(&covered).unwrap(); + std::fs::write( + covered.join("metadata.jsonc"), + r#"{"type":"android:app","android":{"package_name":"org.fdroid.app"}}"#, + ) + .unwrap(); + std::fs::write(covered.join("1.lua"), "return { version = '1' }").unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + assert!( + getter_core::repository::RepositoryPackageDirectoryLayout::load(&official) + .unwrap() + .package(&"android/f-droid/app/org.fdroid.app".parse().unwrap()) + .is_some() + ); + main.upsert_repository( + &getter_core::repository::RepositoryMetadata { + id: "official".parse().unwrap(), + name: "Official".into(), + priority: getter_core::RepositoryPriority::new(1), + api_version: "1".into(), + }, + Some(&official), + None, + ) + .unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockTransport { + calls: Cell::new(0), + result: Ok(INDEX_XML.into()), + }; + + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &transport).unwrap(); + assert!( + !preview + .candidates + .iter() + .any(|candidate| candidate.package_id.ends_with("org.fdroid.app")), + "{:?}", + preview.candidates + ); +} + +#[test] +fn apply_rejects_tampered_or_unknown_selection_before_writes() { + let temp = tempfile::tempdir().unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockTransport { + calls: Cell::new(0), + result: Ok(INDEX_XML.into()), + }; + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &transport).unwrap(); + + let error = apply_setup_preview( + temp.path(), + &main, + &preview, + &SetupAcceptance::Accept(vec!["android/unknown".parse().unwrap()]), + ) + .unwrap_err(); + assert!(error.to_string().contains("unknown setup candidate")); + assert!(!temp.path().join("repo/autogen/android").exists()); + + let mut tampered = preview.clone(); + tampered.candidates.clear(); + apply_setup_preview(temp.path(), &main, &tampered, &SetupAcceptance::AcceptAll).unwrap(); + assert!(temp.path().join("repo/autogen/android").exists()); + + let error = + apply_setup_preview(temp.path(), &main, &preview, &SetupAcceptance::AcceptAll).unwrap_err(); + assert!(error + .to_string() + .contains("unknown, consumed, or already applying")); +} + +#[test] +fn apply_rejects_preview_after_tracked_state_changes_without_writes() { + let temp = tempfile::tempdir().unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockTransport { + calls: Cell::new(0), + result: Ok(INDEX_XML.into()), + }; + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &transport).unwrap(); + main.upsert_tracked_package(&getter_storage::TrackedPackageUpsert { + package_id: "android/app/other".parse().unwrap(), + enabled: true, + favorite: false, + pin_version: None, + repository_id: None, + package_resolution: getter_storage::StoredPackageResolution::MissingPackageDefinition, + }) + .unwrap(); + + let error = + apply_setup_preview(temp.path(), &main, &preview, &SetupAcceptance::AcceptAll).unwrap_err(); + assert!(error.to_string().contains("setup preview is stale")); + assert!(!temp.path().join("repo/autogen/android").exists()); +} + +#[test] +fn apply_delegates_both_categories_and_returns_ready() { + let temp = tempfile::tempdir().unwrap(); + let main = MainDb::open(temp.path().join("main.db")).unwrap(); + let cache = CacheDb::open(temp.path().join("cache.db")).unwrap(); + let transport = MockTransport { + calls: Cell::new(0), + result: Ok(INDEX_XML.into()), + }; + let preview = + preview_setup_with_transport(temp.path(), &main, &cache, inventory(), &transport).unwrap(); + + let result = + apply_setup_preview(temp.path(), &main, &preview, &SetupAcceptance::AcceptAll).unwrap(); + + assert_eq!(result.readiness, SetupReadiness::Ready); + assert_eq!( + result.applied_package_ids, + vec![ + "android/app/com.example.fallback", + "android/f-droid/app/org.fdroid.app" + ] + ); + assert!(temp + .path() + .join("repo/autogen/android/app/com.example.fallback/.autogen.jsonc") + .is_file()); + assert!(temp + .path() + .join("repo/autogen/android/f-droid/app/org.fdroid.app/.autogen.jsonc") + .is_file()); +} diff --git a/crates/getter-operations/tests/startup.rs b/crates/getter-operations/tests/startup.rs index 4c19f93..c5f5843 100644 --- a/crates/getter-operations/tests/startup.rs +++ b/crates/getter-operations/tests/startup.rs @@ -3,6 +3,7 @@ use getter_core::autogen::{InstalledInventory, InstalledInventoryItem}; use getter_core::repository::RepositoryMetadata; use getter_core::{RepositoryId, RepositoryPriority}; +use getter_operations::onboarding::SetupReadiness; use getter_operations::startup::{startup, UpdateStatus}; use getter_storage::{ CacheDb, MainDb, ProviderResponseUpsert, StoredPackageResolution, TrackedPackageUpsert, @@ -10,6 +11,37 @@ use getter_storage::{ use serde_json::json; use std::fs; +#[test] +fn startup_derives_setup_readiness_from_current_inventory_and_tracked_state() { + let temp = tempfile::tempdir().unwrap(); + let actionable = InstalledInventory::new(vec![InstalledInventoryItem::AndroidPackage { + package_name: "com.example.app".into(), + version_name: Some("1.0".into()), + version_code: Some(1), + label: Some("Example".into()), + }]); + + let fresh = startup(temp.path(), actionable.clone()).unwrap(); + assert_eq!(fresh.setup.state, SetupReadiness::NeedsPackageSetup); + + let db = MainDb::open(temp.path().join("main.db")).unwrap(); + db.upsert_tracked_package(&TrackedPackageUpsert { + package_id: "android/tracked".parse().unwrap(), + repository_id: None, + enabled: true, + favorite: false, + pin_version: None, + package_resolution: StoredPackageResolution::OfficialRepositoryPackage, + }) + .unwrap(); + let tracked = startup(temp.path(), actionable).unwrap(); + assert_eq!(tracked.setup.state, SetupReadiness::Ready); + + let empty = tempfile::tempdir().unwrap(); + let no_candidates = startup(empty.path(), InstalledInventory::new(vec![])).unwrap(); + assert_eq!(no_candidates.setup.state, SetupReadiness::Ready); +} + #[test] fn startup_bootstraps_idempotently_and_joins_static_updates() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-storage/src/lib.rs b/crates/getter-storage/src/lib.rs index ebe139e..cbb180d 100644 --- a/crates/getter-storage/src/lib.rs +++ b/crates/getter-storage/src/lib.rs @@ -14,9 +14,10 @@ use serde_json::Value; use std::path::Path; use std::str::FromStr; -pub const MAIN_STORAGE_CONTRACT_VERSION: u32 = 1; +pub const MAIN_STORAGE_CONTRACT_VERSION: u32 = 2; pub const CACHE_STORAGE_CONTRACT_VERSION: u32 = 1; -pub const MAIN_APPLIED_MIGRATION_IDS: &[&str] = &["main-v1", "main-task-v1"]; +pub const MAIN_APPLIED_MIGRATION_IDS: &[&str] = + &["main-v1", "main-task-v1", "main-autogen-transaction-v1"]; pub const CACHE_APPLIED_MIGRATION_IDS: &[&str] = &["cache-v1", "cache-provider-provenance-v1"]; #[derive(Debug, thiserror::Error)] @@ -112,6 +113,11 @@ CREATE TABLE IF NOT EXISTS migration_records ( report_json TEXT NOT NULL DEFAULT '{}' ); +CREATE TABLE IF NOT EXISTS autogen_transaction_commits ( + transaction_id TEXT PRIMARY KEY, + committed_at_unix INTEGER NOT NULL DEFAULT (unixepoch()) +); + CREATE TABLE IF NOT EXISTS download_tasks ( id INTEGER PRIMARY KEY AUTOINCREMENT, task_id TEXT UNIQUE, @@ -237,6 +243,91 @@ ON CONFLICT(id) DO UPDATE SET Ok(()) } + pub fn apply_generated_repository_batch_with_marker( + &self, + metadata: &RepositoryMetadata, + path: &Path, + package_ids: &[PackageId], + transaction_id: Option<&str>, + ) -> Result<(), StorageError> { + let transaction = self.conn.unchecked_transaction()?; + transaction.execute( + r#" +INSERT INTO repositories(id, name, priority, api_version, path, revision) +VALUES (?1, ?2, ?3, ?4, ?5, NULL) +ON CONFLICT(id) DO UPDATE SET + name = excluded.name, + priority = excluded.priority, + api_version = excluded.api_version, + path = excluded.path, + revision = excluded.revision +"#, + params![ + metadata.id.as_str(), + metadata.name, + metadata.priority.value(), + metadata.api_version, + path.to_string_lossy().to_string(), + ], + )?; + for package_id in package_ids { + transaction.execute( + r#" +INSERT INTO tracked_packages( + package_id, enabled, favorite, pin_version, repository_id, package_resolution +) +VALUES (?1, 1, 0, NULL, ?2, ?3) +ON CONFLICT(package_id) DO UPDATE SET + repository_id = CASE + WHEN tracked_packages.package_resolution = 'missing_package_definition' + THEN excluded.repository_id + ELSE tracked_packages.repository_id + END, + package_resolution = CASE + WHEN tracked_packages.package_resolution = 'missing_package_definition' + THEN excluded.package_resolution + ELSE tracked_packages.package_resolution + END +"#, + params![ + package_id.to_string(), + metadata.id.as_str(), + StoredPackageResolution::GenerateLocalPackage.as_str(), + ], + )?; + } + if let Some(transaction_id) = transaction_id { + transaction.execute( + "INSERT OR REPLACE INTO autogen_transaction_commits (transaction_id) VALUES (?1)", + [transaction_id], + )?; + } + transaction.commit()?; + Ok(()) + } + + pub fn has_autogen_transaction_commit( + &self, + transaction_id: &str, + ) -> Result { + Ok(self.conn.query_row( + "SELECT EXISTS(SELECT 1 FROM autogen_transaction_commits WHERE transaction_id = ?1)", + [transaction_id], + |row| row.get(0), + )?) + } + + pub fn delete_autogen_transaction_commit( + &self, + transaction_id: &str, + ) -> Result<(), StorageError> { + self.conn.execute( + "DELETE FROM autogen_transaction_commits WHERE transaction_id = ?1", + [transaction_id], + )?; + Ok(()) + } + pub fn repositories(&self) -> Result, StorageError> { let mut stmt = self.conn.prepare( "SELECT id, name, priority, api_version, path, revision FROM repositories ORDER BY priority DESC, id ASC", From d1b2c148fb1ca96c74ef2d8ada3491a70771c067 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Fri, 17 Jul 2026 20:54:09 +0800 Subject: [PATCH 71/71] feat(install): prepare Android APK handoffs --- crates/getter-cli/tests/app_cli.rs | 38 ++++ crates/getter-core/src/lib.rs | 81 ++++++++- crates/getter-core/src/lua.rs | 28 +++ crates/getter-core/src/luaclass/installer.lua | 5 + crates/getter-operations/src/app.rs | 162 +++++++++++++++++- crates/getter-operations/tests/installer.rs | 162 +++++++++++++++++- 6 files changed, 463 insertions(+), 13 deletions(-) diff --git a/crates/getter-cli/tests/app_cli.rs b/crates/getter-cli/tests/app_cli.rs index 9991497..f4d0a1a 100644 --- a/crates/getter-cli/tests/app_cli.rs +++ b/crates/getter-cli/tests/app_cli.rs @@ -170,6 +170,44 @@ fn app_install_rejects_product_command_controls() { assert_eq!(output.exit_code, ExitCode::Usage); } +#[test] +fn app_install_rejects_android_apk_platform_declaration_without_execution() { + let bytes = b"apk"; + let digest = "dd37c2d7274f7ea982cb83390c36918fee9ce8889073c44b68cdc00bdb8c3e04"; + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let server = std::thread::spawn(move || { + let (mut stream, _) = listener.accept().unwrap(); + let mut request = [0; 1024]; + let _ = stream.read(&mut request).unwrap(); + write!( + stream, + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", + bytes.len() + ) + .unwrap(); + stream.write_all(bytes).unwrap(); + }); + let temp = fixture("2"); + let package = temp.path().join("repo/official/android/com.example"); + fs::write(package.join("Manifest"), format!("{digest} app.apk\n")).unwrap(); + fs::write(package.join("1.lua"), format!(r#"#!/bin/upa-lua v1 +return package_version {{ updates = {{{{ version="2", artifacts={{{{name="app",file_name="app.apk",url="http://{address}/app.apk"}}}}, install={{kind="android_apk",artifact={{artifact="app"}}}} }}}} }}"#)).unwrap(); + + let output = run([ + "getter", + "--data-dir", + temp.path().to_str().unwrap(), + "app", + "install", + "android/com.example", + ]); + server.join().unwrap(); + assert_eq!(output.exit_code, ExitCode::GenericFailure); + let value: Value = serde_json::from_str(&output.stdout).unwrap(); + assert_eq!(value["error"]["code"], "installer.target_unsupported"); +} + #[test] fn app_install_executes_declared_command_and_reports_resolved_argv() { let bytes = b"install artifact"; diff --git a/crates/getter-core/src/lib.rs b/crates/getter-core/src/lib.rs index ee7d976..7f4b3f0 100644 --- a/crates/getter-core/src/lib.rs +++ b/crates/getter-core/src/lib.rs @@ -277,6 +277,12 @@ pub struct PackagePermissions { pub free_network: bool, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Installer { + AndroidApk(AndroidApkInstaller), + Command(InstallerCommand), +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct InstallerCommand { @@ -285,6 +291,11 @@ pub struct InstallerCommand { pub args: Vec, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AndroidApkInstaller { + pub artifact: InstallerArtifactReference, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum InstallerArg { @@ -313,7 +324,7 @@ impl InstallerDeclaration { &self.0 } - pub fn parse(&self) -> Result { + pub fn parse(&self) -> Result { let mut value = self.0.clone(); if let Some(args) = value .as_object_mut() @@ -323,7 +334,27 @@ impl InstallerDeclaration { *args = serde_json::Value::Array(Vec::new()); } } - serde_json::from_value(value) + if value.get("kind").is_some() { + #[derive(Deserialize)] + #[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] + enum TaggedInstaller { + AndroidApk { + artifact: InstallerArtifactReference, + }, + } + + return serde_json::from_value(value).and_then(|installer| match installer { + TaggedInstaller::AndroidApk { artifact } if artifact.artifact.trim().is_empty() => { + Err(::custom( + "android_apk artifact reference must not be empty", + )) + } + TaggedInstaller::AndroidApk { artifact } => { + Ok(Installer::AndroidApk(AndroidApkInstaller { artifact })) + } + }); + } + serde_json::from_value(value).map(Installer::Command) } } @@ -470,6 +501,52 @@ mod tests { assert_eq!(RepositoryPriority::GENERATED_FALLBACK.value(), -1); } + #[test] + fn parses_typed_installers_without_changing_legacy_command_shape() { + let android = InstallerDeclaration::new(serde_json::json!({ + "kind": "android_apk", + "artifact": { "artifact": "app.apk" } + })); + assert_eq!( + android.parse().unwrap(), + Installer::AndroidApk(AndroidApkInstaller { + artifact: InstallerArtifactReference { + artifact: "app.apk".into(), + }, + }) + ); + + let command = InstallerDeclaration::new(serde_json::json!({ + "executable": "installer", + "args": {} + })); + assert_eq!( + command.parse().unwrap(), + Installer::Command(InstallerCommand { + executable: "installer".into(), + args: Vec::new(), + }) + ); + } + + #[test] + fn rejects_incomplete_or_extended_android_apk_installers() { + for value in [ + serde_json::json!({"kind": "android_apk"}), + serde_json::json!({ + "kind": "android_apk", + "artifact": {"artifact": "app.apk"}, + "surprise": true + }), + serde_json::json!({ + "kind": "android_apk", + "artifact": {"artifact": "app.apk", "surprise": true} + }), + ] { + assert!(InstallerDeclaration::new(value).parse().is_err()); + } + } + #[test] fn repository_id_accepts_named_repositories() { assert_eq!(RepositoryId::new("autogen").unwrap().as_str(), "autogen"); diff --git a/crates/getter-core/src/lua.rs b/crates/getter-core/src/lua.rs index bb54f43..4d54955 100644 --- a/crates/getter-core/src/lua.rs +++ b/crates/getter-core/src/lua.rs @@ -1016,6 +1016,34 @@ mod tests { fs::write(package_dir.join("9999.lua"), script_source).unwrap(); } + #[test] + fn android_apk_helper_emits_exact_tagged_artifact_boundary() { + let temp = tempfile::tempdir().unwrap(); + write_simple_android_package( + temp.path(), + r#"#!/bin/upa-lua v1 +local installer = require("luaclass.installer") +return package_version { + updates = {{ + version = "2", + artifacts = {{ name = "app.apk", url = "https://example.invalid/app.apk" }}, + install = installer.android_apk { + artifact = installer.artifact("app.apk"), + }, + }}, +}"#, + ); + + let package = evaluate_single_package_directory(temp.path(), "official").unwrap(); + assert_eq!( + package.updates[0].install.as_ref().unwrap().as_json(), + &serde_json::json!({ + "kind": "android_apk", + "artifact": {"artifact": "app.apk"} + }) + ); + } + #[test] fn preserves_chosen_installer_table_declaration_without_eager_validation() { let temp = tempfile::tempdir().unwrap(); diff --git a/crates/getter-core/src/luaclass/installer.lua b/crates/getter-core/src/luaclass/installer.lua index 7f3aff9..0dba5f1 100644 --- a/crates/getter-core/src/luaclass/installer.lua +++ b/crates/getter-core/src/luaclass/installer.lua @@ -8,4 +8,9 @@ function installer.command(spec) return spec end +function installer.android_apk(spec) + spec.kind = "android_apk" + return spec +end + return installer diff --git a/crates/getter-operations/src/app.rs b/crates/getter-operations/src/app.rs index 0a18e60..c8000fb 100644 --- a/crates/getter-operations/src/app.rs +++ b/crates/getter-operations/src/app.rs @@ -15,7 +15,7 @@ use getter_core::runtime::IssuedAction; use getter_core::update::compare_versions; use getter_core::PackageId; #[cfg(feature = "lua")] -use getter_core::{InstallerArg, InstallerCommand, UpdateArtifact}; +use getter_core::{InstalledTarget, Installer, InstallerArg, InstallerCommand, UpdateArtifact}; use getter_storage::{MainDb, StorageError, StoredTrackedPackage}; use serde::{Deserialize, Serialize}; #[cfg(feature = "lua")] @@ -88,6 +88,12 @@ pub enum AppOperationError { #[error("installer artifact name '{0}' is duplicated")] InstallerArtifactDuplicate(String), #[cfg(feature = "lua")] + #[error("installer target is unsupported")] + InstallerTargetUnsupported, + #[cfg(feature = "lua")] + #[error("installer artifact '{0}' is unsupported")] + InstallerArtifactUnsupported(String), + #[cfg(feature = "lua")] #[error("installer executable '{0}' was not found")] InstallerCommandNotFound(String), #[cfg(feature = "lua")] @@ -144,6 +150,10 @@ impl AppOperationError { #[cfg(feature = "lua")] Self::InstallerArtifactDuplicate(_) => "installer.artifact_duplicate", #[cfg(feature = "lua")] + Self::InstallerTargetUnsupported => "installer.target_unsupported", + #[cfg(feature = "lua")] + Self::InstallerArtifactUnsupported(_) => "installer.artifact_unsupported", + #[cfg(feature = "lua")] Self::InstallerCommandNotFound(_) => "installer.command_not_found", #[cfg(feature = "lua")] Self::InstallerCommandSpawnFailed(_) => "installer.command_spawn_failed", @@ -190,6 +200,10 @@ impl AppOperationError { #[cfg(feature = "lua")] Self::InstallerArtifactDuplicate(_) => "Getter installer artifact name is duplicated", #[cfg(feature = "lua")] + Self::InstallerTargetUnsupported => "Getter installer target is unsupported", + #[cfg(feature = "lua")] + Self::InstallerArtifactUnsupported(_) => "Getter installer artifact is unsupported", + #[cfg(feature = "lua")] Self::InstallerCommandNotFound(_) => "Getter installer command was not found", #[cfg(feature = "lua")] Self::InstallerCommandSpawnFailed(_) => "Getter installer command could not be started", @@ -222,6 +236,8 @@ impl AppOperationError { | Self::InstallerSchema(_) | Self::InstallerArtifactUnknown(_) | Self::InstallerArtifactDuplicate(_) + | Self::InstallerTargetUnsupported + | Self::InstallerArtifactUnsupported(_) | Self::InstallerCommandNotFound(_) | Self::InstallerCommandSpawnFailed(_) | Self::InstallerCommandFailed(_) => self.to_string(), @@ -245,6 +261,39 @@ pub struct AppDownloadResult { pub artifacts: Vec, } +#[cfg(feature = "lua")] +pub const PLATFORM_INSTALL_HANDOFF_FORMAT: &str = "getter-platform-install-handoff"; +#[cfg(feature = "lua")] +pub const PLATFORM_INSTALL_HANDOFF_VERSION: u32 = 1; + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PlatformInstallHandoff { + pub format: String, + pub version: u32, + pub package_id: PackageId, + pub repository_id: String, + pub package_version: String, + pub request: PlatformInstallRequest, +} + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum PlatformInstallRequest { + AndroidApk { + target: AndroidInstallTarget, + artifact: StagedArtifact, + }, +} + +#[cfg(feature = "lua")] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AndroidInstallTarget { + pub kind: String, + pub package_name: String, +} + #[cfg(feature = "lua")] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ResolvedInstallerCommand { @@ -442,7 +491,7 @@ pub fn download_app_with_transports( github_release_transport, download_transport, ) - .map(|(result, _)| result) + .map(|(result, _, _)| result) } #[cfg(feature = "lua")] @@ -451,7 +500,14 @@ fn prepare_app_with_transports( package_id: &PackageId, github_release_transport: Option>, download_transport: Rc, -) -> Result<(AppDownloadResult, getter_core::UpdateCandidate), AppOperationError> { +) -> Result< + ( + AppDownloadResult, + getter_core::UpdateCandidate, + Vec, + ), + AppOperationError, +> { startup::bootstrap_data_dir(data_dir)?; let db = MainDb::open(data_dir.join("main.db"))?; let tracked = tracked_package(&db, package_id)?; @@ -514,6 +570,7 @@ fn prepare_app_with_transports( fs::create_dir_all(&downloads)?; } + let installed_targets = evaluation.package.installed.clone(); let artifacts = prepared .into_iter() .map(|artifact| stage_artifact(artifact, download_transport.as_ref())) @@ -526,9 +583,86 @@ fn prepare_app_with_transports( artifacts, }, candidate, + installed_targets, )) } +#[cfg(feature = "lua")] +pub fn prepare_platform_install( + data_dir: &Path, + package_id: &PackageId, +) -> Result { + prepare_platform_install_with_transports( + data_dir, + package_id, + Some(Rc::new( + crate::github_releases::UreqGithubReleaseTransport::new(), + )), + Rc::new(crate::download::UreqRuntimeDownloadTransport::new()), + ) +} + +#[cfg(feature = "lua")] +pub fn prepare_platform_install_with_transports( + data_dir: &Path, + package_id: &PackageId, + github_release_transport: Option>, + download_transport: Rc, +) -> Result { + let (download, candidate, installed_targets) = prepare_app_with_transports( + data_dir, + package_id, + github_release_transport, + download_transport, + )?; + let installer = candidate + .install + .ok_or(AppOperationError::InstallerMissing)? + .parse() + .map_err(|error| AppOperationError::InstallerSchema(error.to_string()))?; + let Installer::AndroidApk(installer) = installer else { + return Err(AppOperationError::InstallerTargetUnsupported); + }; + let [InstalledTarget::AndroidPackage { package_name }] = installed_targets.as_slice() else { + return Err(AppOperationError::InstallerTargetUnsupported); + }; + if package_name.trim().is_empty() { + return Err(AppOperationError::InstallerTargetUnsupported); + } + let declared_artifact = candidate + .artifacts + .iter() + .find(|artifact| artifact.name == installer.artifact.artifact) + .ok_or_else(|| { + AppOperationError::InstallerArtifactUnknown(installer.artifact.artifact.clone()) + })?; + let declared_file_name = declared_artifact + .file_name + .as_deref() + .unwrap_or(&declared_artifact.name); + if !declared_file_name.to_ascii_lowercase().ends_with(".apk") { + return Err(AppOperationError::InstallerArtifactUnsupported( + installer.artifact.artifact, + )); + } + let artifact = unique_staged_artifact(&download.artifacts, &declared_artifact.name)?; + + Ok(PlatformInstallHandoff { + format: PLATFORM_INSTALL_HANDOFF_FORMAT.into(), + version: PLATFORM_INSTALL_HANDOFF_VERSION, + package_id: download.package_id, + repository_id: download.repository_id, + package_version: download.version, + request: PlatformInstallRequest::AndroidApk { + target: AndroidInstallTarget { + kind: "android".into(), + package_name: package_name.clone(), + }, + artifact: artifact.clone(), + }, + }) +} + #[cfg(feature = "lua")] pub fn install_app( data_dir: &Path, @@ -586,7 +720,7 @@ pub fn install_app_with_dependencies_and_observer( runner: &dyn CommandRunner, observer: &dyn CommandObserver, ) -> Result { - let (download, candidate) = prepare_app_with_transports( + let (download, candidate, _) = prepare_app_with_transports( data_dir, package_id, github_release_transport, @@ -597,6 +731,9 @@ pub fn install_app_with_dependencies_and_observer( .ok_or(AppOperationError::InstallerMissing)? .parse() .map_err(|error| AppOperationError::InstallerSchema(error.to_string()))?; + let Installer::Command(installer) = installer else { + return Err(AppOperationError::InstallerTargetUnsupported); + }; validate_installer_command(&installer)?; let command = resolve_installer(&installer, &download.artifacts, resolver)?; observer @@ -623,6 +760,23 @@ pub fn install_app_with_dependencies_and_observer( }) } +#[cfg(feature = "lua")] +fn unique_staged_artifact<'a>( + artifacts: &'a [StagedArtifact], + name: &str, +) -> Result<&'a StagedArtifact, AppOperationError> { + let mut matches = artifacts.iter().filter(|artifact| artifact.name == name); + let artifact = matches + .next() + .ok_or_else(|| AppOperationError::InstallerArtifactUnknown(name.to_owned()))?; + if matches.next().is_some() { + return Err(AppOperationError::InstallerArtifactDuplicate( + name.to_owned(), + )); + } + Ok(artifact) +} + #[cfg(feature = "lua")] fn resolve_installer( installer: &InstallerCommand, diff --git a/crates/getter-operations/tests/installer.rs b/crates/getter-operations/tests/installer.rs index 7a48659..32c173b 100644 --- a/crates/getter-operations/tests/installer.rs +++ b/crates/getter-operations/tests/installer.rs @@ -4,7 +4,8 @@ use getter_core::repository::RepositoryMetadata; use getter_core::{RepositoryId, RepositoryPriority}; use getter_operations::app::{ download_app_with_transports, install_app_with_dependencies, - install_app_with_dependencies_and_observer, CommandObserver, CommandResolver, CommandRunner, + install_app_with_dependencies_and_observer, prepare_platform_install_with_transports, + CommandObserver, CommandResolver, CommandRunner, PlatformInstallRequest, ResolvedInstallerCommand, RunnerOutput, }; use getter_operations::download::{ @@ -79,16 +80,12 @@ fn fake_installer(temp: &Path) -> PathBuf { }"#, ) } -fn fixture(install: &str, bytes: &[u8]) -> tempfile::TempDir { +fn fixture_with_metadata(install: &str, bytes: &[u8], metadata: &str) -> tempfile::TempDir { let temp = tempfile::tempdir().unwrap(); getter_operations::startup::bootstrap_data_dir(temp.path()).unwrap(); let package = temp.path().join("repo/official/android/com.example"); fs::create_dir_all(&package).unwrap(); - fs::write( - package.join("metadata.jsonc"), - r#"{"type":"android:app","android":{"package_name":"com.example"}}"#, - ) - .unwrap(); + fs::write(package.join("metadata.jsonc"), metadata).unwrap(); fs::write( package.join("Manifest"), format!("{} app.apk\n", digest(bytes)), @@ -120,6 +117,157 @@ return package_version {{ updates = {{{{ version="2", artifacts={{{{name="app",f temp } +fn fixture(install: &str, bytes: &[u8]) -> tempfile::TempDir { + fixture_with_metadata( + install, + bytes, + r#"{"type":"android:app","android":{"package_name":"com.example"}}"#, + ) +} + +#[test] +fn prepares_android_apk_handoff_from_verified_staging() { + let bytes = b"apk"; + let temp = fixture(r#"{kind="android_apk",artifact={artifact="app"}}"#, bytes); + + let handoff = prepare_platform_install_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + ) + .unwrap(); + + assert_eq!(handoff.format, "getter-platform-install-handoff"); + assert_eq!(handoff.version, 1); + assert_eq!(handoff.package_id.to_string(), "android/com.example"); + assert_eq!(handoff.repository_id.as_str(), "official"); + assert_eq!(handoff.package_version, "2"); + let artifact_path = match &handoff.request { + PlatformInstallRequest::AndroidApk { artifact, .. } => artifact.path.clone(), + }; + assert_eq!( + serde_json::to_value(&handoff).unwrap(), + serde_json::json!({ + "format": "getter-platform-install-handoff", + "version": 1, + "package_id": "android/com.example", + "repository_id": "official", + "package_version": "2", + "request": { + "kind": "android_apk", + "target": { + "kind": "android", + "package_name": "com.example" + }, + "artifact": { + "name": "app", + "path": artifact_path, + "sha256": digest(bytes), + "status": "downloaded" + } + } + }) + ); + let PlatformInstallRequest::AndroidApk { target, artifact } = handoff.request; + assert_eq!(target.kind, "android"); + assert_eq!(target.package_name, "com.example"); + assert_eq!(artifact.name, "app"); + assert!(artifact.path.is_absolute()); + assert_eq!(artifact.sha256, digest(bytes)); + assert_eq!(artifact.status, "downloaded"); +} + +#[test] +fn platform_prepare_rejects_command_unknown_non_apk_and_malformed_declarations_stably() { + let cases = [ + ( + r#"{executable="fake-installer",args={}}"#, + "installer.target_unsupported", + ), + ( + r#"{kind="android_apk",artifact={artifact="missing"}}"#, + "installer.artifact_unknown", + ), + ( + r#"{kind="android_apk",artifact={artifact="app"},surprise=true}"#, + "installer.schema_invalid", + ), + ]; + for (install, expected_code) in cases { + let bytes = b"apk"; + let temp = fixture(install, bytes); + let error = prepare_platform_install_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + ) + .unwrap_err(); + assert_eq!(error.code(), expected_code); + } + + let bytes = b"not apk"; + let temp = fixture(r#"{kind="android_apk",artifact={artifact="app"}}"#, bytes); + let package = temp.path().join("repo/official/android/com.example"); + fs::write( + package.join("Manifest"), + format!("{} payload.zip\n", digest(bytes)), + ) + .unwrap(); + fs::write(package.join("1.lua"), r#"#!/bin/upa-lua v1 +return package_version { updates = {{ version="2", artifacts={{name="app",file_name="payload.zip",url="mock://app"}}, install={kind="android_apk",artifact={artifact="app"}} }} }"#).unwrap(); + let error = prepare_platform_install_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.artifact_unsupported"); +} + +#[test] +fn platform_prepare_requires_exactly_one_nonempty_android_package_target() { + let bytes = b"apk"; + for metadata in [ + r#"{"type":"generic","generic":{"id":"com.example"}}"#, + r#"{"type":"android:app","android":{"package_name":""}}"#, + ] { + let temp = fixture_with_metadata( + r#"{kind="android_apk",artifact={artifact="app"}}"#, + bytes, + metadata, + ); + let error = prepare_platform_install_with_transports( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.target_unsupported"); + } +} + +#[test] +fn direct_command_install_rejects_android_apk_without_resolving_or_running() { + let bytes = b"apk"; + let temp = fixture(r#"{kind="android_apk",artifact={artifact="app"}}"#, bytes); + let runner = Runner::default(); + let error = install_app_with_dependencies( + temp.path(), + &"android/com.example".parse().unwrap(), + None, + Rc::new(Bytes(bytes.to_vec())), + &Resolver(PathBuf::from("/fake/bin/installer")), + &runner, + ) + .unwrap_err(); + assert_eq!(error.code(), "installer.target_unsupported"); + assert!(runner.seen.borrow().is_none()); +} + #[test] fn malformed_installer_does_not_block_download_but_fails_install_after_staging() { let bytes = b"apk";