From e009df1508a85c4f9cdee57ee86ce7dbe156f3e5 Mon Sep 17 00:00:00 2001 From: Seokju Na Date: Sun, 11 Oct 2026 20:27:11 +0900 Subject: [PATCH] feat: align Android SDK with core update document API --- .ffi-version | 4 +- README.md | 5 +- e2e/README.md | 23 +- e2e/appium.ts | 2 +- e2e/context.ts | 7 +- e2e/device.ts | 4 +- e2e/fixtures/0.4.1.wvb | Bin 0 -> 99825 bytes e2e/fixtures/0.4.2.wvb | Bin 0 -> 99822 bytes e2e/fixtures/webview-testbed-0.4.0.tgz | Bin 0 -> 4873 bytes e2e/package.json | 2 +- e2e/remote.ts | 77 + e2e/testbed.spec.ts | 134 +- e2e/yarn.lock | 29 +- lib/build.gradle.kts | 7 + lib/consumer-rules.pro | 5 +- lib/lint.xml | 4 +- lib/src/androidTest/AndroidManifest.xml | 3 + .../kotlin/dev/wvb/AndroidTlsTest.kt | 48 + lib/src/main/kotlin/dev/wvb/Bridge.kt | 2 +- lib/src/main/kotlin/dev/wvb/BridgeCodec.kt | 287 +- lib/src/main/kotlin/dev/wvb/Source.kt | 20 +- lib/src/main/kotlin/dev/wvb/WebViewBundle.kt | 27 +- .../kotlin/dev/wvb/WebViewBundleBridge.kt | 152 +- .../kotlin/dev/wvb/{wvb_ffi.kt => WvbFFI.kt} | 10892 +++++++++------- .../dev/wvb/WvbRustlsPlatformVerififer.kt | 22 + scripts/install.mjs | 29 +- settings.gradle.kts | 4 + testapp/src/main/assets/bundles/manifest.json | 10 +- .../src/main/assets/bundles/testbed/0.4.0.wvb | Bin 0 -> 99816 bytes .../assets/bundles/testbed/testbed_0.2.0.wvb | Bin 100846 -> 0 bytes .../kotlin/dev/wvb/testapp/MainActivity.kt | 6 +- 31 files changed, 6980 insertions(+), 4825 deletions(-) create mode 100644 e2e/fixtures/0.4.1.wvb create mode 100644 e2e/fixtures/0.4.2.wvb create mode 100644 e2e/fixtures/webview-testbed-0.4.0.tgz create mode 100644 e2e/remote.ts create mode 100644 lib/src/androidTest/AndroidManifest.xml create mode 100644 lib/src/androidTest/kotlin/dev/wvb/AndroidTlsTest.kt rename lib/src/main/kotlin/dev/wvb/{wvb_ffi.kt => WvbFFI.kt} (78%) create mode 100644 lib/src/main/kotlin/dev/wvb/WvbRustlsPlatformVerififer.kt create mode 100644 testapp/src/main/assets/bundles/testbed/0.4.0.wvb delete mode 100644 testapp/src/main/assets/bundles/testbed/testbed_0.2.0.wvb diff --git a/.ffi-version b/.ffi-version index a05707f..b316b8f 100644 --- a/.ffi-version +++ b/.ffi-version @@ -1,4 +1,4 @@ # Bump this when upgrading the FFI, then run `node scripts/install.mjs`. # (`ffi/` for releases, `prerelease/` for prereleases). -# See releases from https://github.com/webview-bundle/webview-bundles. -prerelease/76b4cbf +# See releases from https://github.com/webview-bundle/webview-bundle. +prerelease/f62e1f4 diff --git a/README.md b/README.md index f0f63e4..194bc73 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,9 @@ mise install ## Installing / updating the FFI module `scripts/install.mjs` fetches a release from the upstream repository and wires -it into the `lib` module. From the `android.zip` asset it installs the Android -variant (`lib-android`): +it into the `lib` module. -- the Kotlin bindings into `lib/src/main/kotlin/` +- `WvbFFI.kt` and `WvbRustlsPlatformVerififer.kt` into `lib/src/main/kotlin/dev/wvb/` - the JNI libraries (a `libwvb_ffi.so` per ABI) into `lib/src/main/jniLibs/` The Kotlin bindings are overwritten in place; `jniLibs` is replaced wholesale, diff --git a/e2e/README.md b/e2e/README.md index 422e2da..e77e9b5 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -5,12 +5,13 @@ Appium-driven end-to-end tests for the `testapp` module, mirroring the iOS e2e. ## How it works The `testapp` (`MainActivity`) is a minimal WebView host: it serves the builtin -`testbed` bundle (0.2.0) over `https://testbed.wvb/` and loads it on launch, and -wires its updater to the live playground remote -(`https://playground-remote.wvb.dev`, which hosts 0.3.0). It carries **no** test -logic of its own. The scenarios live in the shared -`@wvb-playground/webview-testbed` package and are expressed against the -platform-agnostic `WebviewDriver` from `@wvb-playground/testdriver`. +`testbed` bundle (0.4.0) over `https://testbed.wvb/` and loads it on launch, and +wires its updater to the playground remote for manual use. E2E supplies a local +remote endpoint through the test app's `wvbRemoteUrl` launch argument. The shared +suite fills the testbed's inputs, clicks its buttons and checks rendered results. +It downloads 0.4.1 and 0.4.2, verifies the page version after each installation, +and checks that rollback loads the previous UI. Downloads use the Android +emulator's `10.0.2.2` host alias. The Appium session is switched into the testapp's **WEBVIEW** context, then the suite runs through `createAppiumDriver(driver, { baseURL: "https://testbed.wvb" })`, @@ -24,17 +25,16 @@ which drives the live DOM (CSS selectors, in-app navigation via `goto`). (or reuses) an emulator, starts an Appium server (with `chromedriver_autodownload` enabled), opens a session that installs and launches the testapp, and switches into its WEBVIEW context. -- **testbed.spec.ts** registers the `@wvb-playground/webview-testbed/testing` - suite (`defineTestbedSuite`) — one case per `@wvb/bridge` method — plus a strict - end-to-end check that downloads the real remote bundle and updates the source - from the builtin 0.2.0 to the remote 0.3.0. +- **testbed.spec.ts** runs the shared UI suite for all 26 bridge methods, plus + update-document, manifest, download/install/rollback and invalid-input scenarios. ## Prerequisites - The Android SDK, with `platform-tools` (adb) and `emulator` available. Set `ANDROID_HOME` (or `ANDROID_SDK_ROOT`) if it is not at the platform default. - At least one AVD. The first one is used unless `ANDROID_AVD` is set; an - already-booted device/emulator is reused. + already-booted emulator is reused. Physical devices are skipped because the + fixture remote uses the emulator's `10.0.2.2` host alias. - Node + a package manager (the repo pins toolchain via `mise`). - Appium is installed as a dev dependency; the `uiautomator2` driver is installed into `~/.appium` on first run. @@ -52,3 +52,4 @@ Environment knobs: - `ANDROID_AVD` — AVD to boot when no device is already running. - `WVB_E2E_HEADLESS=1` — boot the emulator with `-no-window` (default on `CI`). - `WVB_E2E_KEEP=1` — leave a self-booted emulator running after the test. +- `APPIUM_PORT` — server port (default `4723`); choose different ports when running iOS and Android suites together. diff --git a/e2e/appium.ts b/e2e/appium.ts index 52a5e70..0a3b337 100644 --- a/e2e/appium.ts +++ b/e2e/appium.ts @@ -3,7 +3,7 @@ import path from "node:path"; import { setTimeout as delay } from "node:timers/promises"; import { execa, type ResultPromise } from "execa"; -export const APPIUM_PORT = 4723; +export const APPIUM_PORT = Number(process.env.APPIUM_PORT ?? 4723); if (!process.env.APPIUM_HOME) { process.env.APPIUM_HOME = path.join(os.homedir(), ".appium"); diff --git a/e2e/context.ts b/e2e/context.ts index 7c9f7e5..4286631 100644 --- a/e2e/context.ts +++ b/e2e/context.ts @@ -1,3 +1,4 @@ +import { startFixtureRemote, type FixtureRemote } from "./remote.js"; import fs from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; @@ -22,6 +23,7 @@ export type Driver = Awaited>; export interface TestContext { server: AppiumServer; + fixtureRemote: FixtureRemote; device: AndroidDevice; driver: Driver; } @@ -69,6 +71,7 @@ export async function createTestContext(): Promise { const device = await ensureAndroidDevice(); const server = await startAppiumServer(APPIUM_PORT); + const fixtureRemote = await startFixtureRemote("10.0.2.2"); const sessionTimeout = process.env.CI ? 720_000 : 360_000; @@ -90,6 +93,7 @@ export async function createTestContext(): Promise { "appium:appPackage": APP_PACKAGE, "appium:appActivity": APP_ACTIVITY, "appium:appWaitActivity": "*", + "appium:optionalIntentArguments": `--es wvbRemoteUrl ${fixtureRemote.endpoint}`, // The testapp's versionCode is constant, so without a full reset Appium // skips reinstalling a rebuilt APK and keeps stale bundles/assets. "appium:fullReset": true, @@ -108,7 +112,7 @@ export async function createTestContext(): Promise { await switchToWebview(driver); - return { server, device, driver }; + return { server, device, driver, fixtureRemote }; } /** ms to wait for the testapp's WebView to expose a debuggable WEBVIEW context. */ @@ -145,6 +149,7 @@ export async function disposeTestContext( } await context.driver.deleteSession().catch(() => {}); await context.server.stop(); + await context.fixtureRemote.close(); if (context.device.bootedByUs && !process.env.WVB_E2E_KEEP) { await context.device.shutdown(); } diff --git a/e2e/device.ts b/e2e/device.ts index 1e6415c..05100cf 100644 --- a/e2e/device.ts +++ b/e2e/device.ts @@ -154,7 +154,7 @@ async function bootEmulator(avd: string): Promise { } /** - * Reuses an already-booted device/emulator, or boots an AVD and waits for it to + * Reuses an already-booted emulator, or boots an AVD and waits for it to * finish booting. */ export async function ensureAndroidDevice( @@ -163,6 +163,8 @@ export async function ensureAndroidDevice( await adb(["start-server"]); for (const serial of await listSerials()) { + // The fixture remote uses 10.0.2.2, which is only reachable from an emulator. + if (!serial.startsWith("emulator-")) continue; if (await isBooted(serial)) { console.log(`[device] reusing running device: ${serial}`); return { diff --git a/e2e/fixtures/0.4.1.wvb b/e2e/fixtures/0.4.1.wvb new file mode 100644 index 0000000000000000000000000000000000000000..2c0dcd9e883eb8e2ec22c58cda21545eaae3399e GIT binary patch literal 99825 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 IS6*NAKS0Bk!T6;JN6O5| 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 vzvV794J` + [ + version, + await readFile( + new URL(`./fixtures/${version}.wvb`, import.meta.url), + ), + ] as const, + ), + ), + ); + let version = "0.4.1"; + const server = createServer((request, response) => { + const path = new URL(request.url ?? "/", "http://localhost").pathname; + if (path === "/update") { + const etag = `"testbed-${version}"`; + if (request.headers["if-none-match"] === etag) { + response.writeHead(304).end(); + return; + } + const bytes = bundles.get(version)!; + response.writeHead(200, { "content-type": "application/json", etag }); + response.end( + JSON.stringify({ + id: `testbed-${version}`, + createdAt: "2026-10-09T00:00:00Z", + runtimeVersion: 1, + bundles: [ + { + name: "testbed", + version, + integrity: `sha256:${createHash("sha256").update(bytes).digest("base64")}`, + }, + ], + metadata: { channel: "stable" }, + }), + ); + return; + } + const match = /^\/bundles\/testbed\/([^/]+)$/.exec(path); + const bytes = match ? bundles.get(match[1]!) : undefined; + if (!bytes) { + response.writeHead(404).end(); + return; + } + response.writeHead(200, { + "content-type": "application/octet-stream", + "content-length": bytes.length, + }); + response.end(bytes); + }); + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "0.0.0.0", resolve); + }); + return { + endpoint: `http://${host}:${(server.address() as AddressInfo).port}`, + setVersion(next: string) { + if (!bundles.has(next)) + throw new Error(`Unknown fixture version: ${next}`); + version = next; + }, + close: () => + new Promise((resolve, reject) => + server.close((error) => (error ? reject(error) : resolve())), + ), + }; +} +export type FixtureRemote = Awaited>; diff --git a/e2e/testbed.spec.ts b/e2e/testbed.spec.ts index 0afbd0a..314d6c5 100644 --- a/e2e/testbed.spec.ts +++ b/e2e/testbed.spec.ts @@ -1,134 +1,24 @@ import { createAppiumDriver } from "@wvb-playground/testdriver/appium"; import { - methodSel, - sel, - testCases, + defineTestbedSuite, + defineTestbedUpdateSuite, type WebviewDriver, } from "@wvb-playground/webview-testbed/testing"; -import { beforeAll, describe, expect, test } from "vitest"; -import { type Driver, getTestContext } from "./context.js"; +import { beforeAll, describe } from "vitest"; +import { getTestContext } from "./context.js"; -// Drives the `@wvb-playground/webview-testbed` bundle inside the Android WebView. The -// testapp ships the builtin `testbed` bundle and points its updater at the live -// playground remote (https://playground-remote.wvb.dev). -describe("testbed (android)", () => { - const BASE_URL = "https://testbed.wvb"; - const BUNDLE = "testbed"; - const BUILTIN_VERSION = "0.2.0"; - const REMOTE_VERSION = "0.3.0"; - - // Loading the app shell (a full navigation) and the network download need more - // headroom than the driver's default. - const NAV_TIMEOUT = 30_000; - const RESULT_TIMEOUT = 90_000; - - let browser: Driver; +describe("testbed UI (android)", () => { let driver: WebviewDriver; - beforeAll(() => { - browser = getTestContext().driver; - driver = createAppiumDriver(browser, { - baseURL: BASE_URL, - defaultTimeoutMs: NAV_TIMEOUT, + driver = createAppiumDriver(getTestContext().driver, { + baseURL: "https://testbed.wvb", + defaultTimeoutMs: 30_000, }); }); - async function open(): Promise { - await driver.goto("/"); - await driver.waitForVisible(sel.appShell, { timeoutMs: NAV_TIMEOUT }); - } - - /** - * Runs one testbed method card on a freshly-loaded page and returns its terminal - * outcome. Reloading first guarantees the card starts idle (its result element is - * only rendered once a call settles), so waiting for the result can never observe a - * stale value left by an earlier run of the same method. - */ - async function runMethod( - id: string, - params: Record = {}, - ): Promise<{ status: string | null; text: string }> { - await open(); - for (const [name, value] of Object.entries(params)) { - await driver.fill(methodSel.param(id, name), value); - } - await driver.click(methodSel.run(id)); - await driver.waitForVisible(methodSel.result(id), { timeoutMs: RESULT_TIMEOUT }); - const outcome = { - status: await driver.getAttribute(methodSel.result(id), "data-status"), - text: await driver.text(methodSel.result(id)), - }; - console.log(`[testbed] ${id} -> ${outcome.status}: ${outcome.text}`); - return outcome; - } - - function parseJson(text: string): Record { - try { - return JSON.parse(text) as Record; - } catch { - throw new Error(`expected a JSON result, got: ${text}`); - } - } - - // The only test that *asserts* the real remote download succeeds. Skipped on CI: - // this fill + reload + download sequence hangs on the GitHub Actions Android emulator - // (the network is reachable — the generic suite below downloads over it — but this - // specific flow stalls there). It passes locally, and the generic suite still - // exercises the download (as ok|error) on CI. - test.skipIf(!!process.env.CI)( - "downloads + installs the real remote update (builtin 0.2.0 -> remote 0.3.0)", - async () => { - await open(); - expect(await driver.text(sel.platformType)).toBe("android"); - - const before = parseJson( - (await runMethod("source.loadVersion", { bundleName: BUNDLE })).text, - ); - expect(before).toEqual({ type: "builtin", version: BUILTIN_VERSION }); - - const update = await runMethod("updater.getUpdate", { bundleName: BUNDLE }); - expect(update.status).toBe("ok"); - const info = parseJson(update.text); - expect(info.name).toBe(BUNDLE); - expect(info.version).toBe(REMOTE_VERSION); - expect(info.isAvailable).toBe(true); - expect(info.localVersion).toBe(BUILTIN_VERSION); - - const download = await runMethod("updater.download", { bundleName: BUNDLE }); - expect(download.status).toBe("ok"); - expect(parseJson(download.text).version).toBe(REMOTE_VERSION); - - const install = await runMethod("updater.install", { - bundleName: BUNDLE, - version: REMOTE_VERSION, - }); - expect(install.status).toBe("ok"); - - const after = parseJson( - (await runMethod("source.loadVersion", { bundleName: BUNDLE })).text, - ); - expect(after).toEqual({ type: "remote", version: REMOTE_VERSION }); - - // Drop the cached descriptor so the next request loads the freshly-installed - // version, and cache-bust so the WebView can't serve stale HTML. - await runMethod("source.unloadDescriptor", { bundleName: BUNDLE }); - await driver.goto(`/?updated=${Date.now()}`); - await driver.waitForVisible(sel.appShell, { timeoutMs: NAV_TIMEOUT }); - expect(await driver.text(".app__version")).toContain(REMOTE_VERSION); - }, - 240_000, + defineTestbedUpdateSuite( + () => driver, + () => getTestContext().fixtureRemote, ); - - // The platform-agnostic suite the testbed package ships (each `@wvb/bridge` method - // asserted to render an ok|error outcome). The "raw invoke" case is the only one that - // fills a text input, which hangs on the CI Android emulator; skip it there (its - // coverage overlaps source.listBundles). Runs after the strict update test so it - // can't perturb that test's clean starting state. - for (const testCase of testCases) { - const register = - process.env.CI && testCase.name.startsWith("raw invoke") ? test.skip : test; - register(testCase.name, async () => { - await testCase.run(driver); - }); - } + defineTestbedSuite(() => driver); }); diff --git a/e2e/yarn.lock b/e2e/yarn.lock index a063c42..8e60395 100644 --- a/e2e/yarn.lock +++ b/e2e/yarn.lock @@ -698,13 +698,6 @@ __metadata: languageName: node linkType: hard -"@tauri-apps/api@npm:^2": - version: 2.11.1 - resolution: "@tauri-apps/api@npm:2.11.1" - checksum: 10c0/78a08a64ee7eab9d34bd4ba1bdc3a2440c7e9cc5737b3f1d77bd80fa38b0cb5e9601efe04da40c947c5da2ca38dc702bec21b4ae96b18ee5f3e82980d1f0b325 - languageName: node - linkType: hard - "@tootallnate/quickjs-emscripten@npm:^0.23.0": version: 0.23.0 resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" @@ -991,24 +984,14 @@ __metadata: languageName: node linkType: hard -"@wvb-playground/webview-testbed@npm:0.3.0": - version: 0.3.0 - resolution: "@wvb-playground/webview-testbed@npm:0.3.0" +"@wvb-playground/webview-testbed@file:./fixtures/webview-testbed-0.4.0.tgz::locator=webview-bundle-android-e2e%40workspace%3A.": + version: 0.4.0 + resolution: "@wvb-playground/webview-testbed@file:./fixtures/webview-testbed-0.4.0.tgz#./fixtures/webview-testbed-0.4.0.tgz::hash=7cded0&locator=webview-bundle-android-e2e%40workspace%3A." dependencies: "@wvb-playground/testdriver": "npm:^0.1.0" - "@wvb/bridge": "npm:0.1.0-next.fe10f84" peerDependencies: - vitest: ^2.0.0 || ^3.0.0 || ^4.0.0 - checksum: 10c0/b4243848ce25c46480269003d8b8edc25a052643d82db9bb51ae4bfe4407b73fa58503ca0aac06f2d17b7e63fb68bcddb5aa96f891f117f81129d2f58bbffd90 - languageName: node - linkType: hard - -"@wvb/bridge@npm:0.1.0-next.fe10f84": - version: 0.1.0-next.fe10f84 - resolution: "@wvb/bridge@npm:0.1.0-next.fe10f84" - dependencies: - "@tauri-apps/api": "npm:^2" - checksum: 10c0/dda6994a2826e07e93463576e5e50c7a24a2c4943af47f550862c26f4a95169e8b5fba1e7636aa3e9b245243e32f23c13e18c849d57f028833df749af6368aea + vitest: ^4.0.0 + checksum: 10c0/8ad4d176b228df9f8c1da9fff2a5ede0b64ebc553bf25ae4eea2b7cadce529fc90f289cc82a5ccf33c9e033842a4bee80f9dba47ee412c83349ed8089c1aa10d languageName: node linkType: hard @@ -5554,7 +5537,7 @@ __metadata: dependencies: "@types/node": "npm:^25" "@wvb-playground/testdriver": "npm:0.1.0" - "@wvb-playground/webview-testbed": "npm:0.3.0" + "@wvb-playground/webview-testbed": "file:./fixtures/webview-testbed-0.4.0.tgz" appium: "npm:^3.5.0" execa: "npm:^9.5.2" tinyglobby: "npm:^0.2.10" diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 9c802ce..5980998 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -28,6 +28,7 @@ android { defaultConfig { minSdk = 24 + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" // Keep the @JavascriptInterface bridge methods in minified consumer apps. consumerProguardFiles("consumer-rules.pro") } @@ -57,6 +58,12 @@ android { } dependencies { + // Match the rustls-platform-verifier-android crate used by the installed FFI. + implementation("org.rustls:rustls-platform-verifier:0.2.0") + androidTestImplementation("androidx.test:runner:1.6.2") + androidTestImplementation("junit:junit:4.13.2") + testImplementation("junit:junit:4.13.2") + testImplementation("org.json:json:20240303") implementation("net.java.dev.jna:jna:${libs.versions.jna.get()}@aar") implementation(libs.kotlinx.coroutines.core) implementation(libs.androidx.annotation) diff --git a/lib/consumer-rules.pro b/lib/consumer-rules.pro index 21f8119..b220e7b 100644 --- a/lib/consumer-rules.pro +++ b/lib/consumer-rules.pro @@ -1,4 +1,4 @@ -# WebViewBundle's native FFI bindings (lib/src/main/kotlin/dev/wvb/wvb_ffi.kt) +# WebViewBundle's native FFI bindings (lib/src/main/kotlin/dev/wvb/WvbFFI.kt) # are UniFFI-generated and call into the native library through JNA. JNA maps the # Rust structs/callbacks by reflection and reads the field names from # @Structure.FieldOrder annotations, so R8 in a minified consumer app must not @@ -14,3 +14,6 @@ -keep class com.sun.jna.* { *; } -keep class * extends com.sun.jna.* { *; } -keepclassmembers class * extends com.sun.jna.* { public *; } + +# Called from Rust through JNI. +-keep, includedescriptorclasses class org.rustls.platformverifier.** { *; } diff --git a/lib/lint.xml b/lib/lint.xml index c512edb..8145043 100644 --- a/lib/lint.xml +++ b/lib/lint.xml @@ -1,7 +1,7 @@ - + diff --git a/lib/src/androidTest/AndroidManifest.xml b/lib/src/androidTest/AndroidManifest.xml new file mode 100644 index 0000000..fbb2ef8 --- /dev/null +++ b/lib/src/androidTest/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + diff --git a/lib/src/androidTest/kotlin/dev/wvb/AndroidTlsTest.kt b/lib/src/androidTest/kotlin/dev/wvb/AndroidTlsTest.kt new file mode 100644 index 0000000..97ba3b8 --- /dev/null +++ b/lib/src/androidTest/kotlin/dev/wvb/AndroidTlsTest.kt @@ -0,0 +1,48 @@ +package dev.wvb + +import androidx.test.platform.app.InstrumentationRegistry +import java.io.File +import kotlinx.coroutines.runBlocking +import org.junit.Assert.* +import org.junit.Test + +/** Network integration tests; require access to example.com and badssl.com. */ +class AndroidTlsTest { + @Test + fun httpsWorksAfterAutomaticInitializationAndRecreation() = runBlocking { + val context = InstrumentationRegistry.getInstrumentation().targetContext + val config = WebViewBundleConfig( + protocols = emptyList(), + updater = WebViewBundleUpdaterConfig(WebViewBundleRemoteConfig("https://example.com")), + ) + + repeat(2) { + WebViewBundle.getInstance(context, config).use { bundle -> + val destination = File(context.cacheDir, "tls-download.html") + try { + bundle.remote!!.download("https://example.com/", destination.absolutePath) + assertTrue(destination.readText().contains("Example Domain")) + } finally { + destination.delete() + } + + ProxyProtocolHandler(mapOf("tls-test" to "https://example.com")).use { proxy -> + val response = proxy.handle(HttpMethod.GET, "https://tls-test/") + assertEquals(200, response.status.toInt()) + assertTrue(response.body.toString(Charsets.UTF_8).contains("Example Domain")) + } + } + } + + WvbAndroid.initialize(context) + WvbAndroid.initialize(context) + ProxyProtocolHandler(mapOf("tls-test" to "https://self-signed.badssl.com")).use { proxy -> + try { + proxy.handle(HttpMethod.GET, "https://tls-test/") + fail("A self-signed certificate must be rejected") + } catch (_: WebviewBundleException.CoreHttpClient) { + // A verifier panic would surface as InternalException, not this typed error. + } + } + } +} diff --git a/lib/src/main/kotlin/dev/wvb/Bridge.kt b/lib/src/main/kotlin/dev/wvb/Bridge.kt index ff88c2b..787393f 100644 --- a/lib/src/main/kotlin/dev/wvb/Bridge.kt +++ b/lib/src/main/kotlin/dev/wvb/Bridge.kt @@ -143,7 +143,7 @@ class Bridge internal constructor() : AutoCloseable { fun encodeError(error: Throwable): String { val json = JSONObject().put("message", error.message ?: error.toString()) - (error as? BridgeFailure)?.code?.let { json.put("code", it) } + ((error as? BridgeFailure)?.code ?: (error as? WebviewBundleException)?.bridgeCode)?.let { json.put("code", it) } return json.toString() } diff --git a/lib/src/main/kotlin/dev/wvb/BridgeCodec.kt b/lib/src/main/kotlin/dev/wvb/BridgeCodec.kt index d2ed43b..b6f9404 100644 --- a/lib/src/main/kotlin/dev/wvb/BridgeCodec.kt +++ b/lib/src/main/kotlin/dev/wvb/BridgeCodec.kt @@ -1,71 +1,242 @@ package dev.wvb +import org.json.JSONArray import org.json.JSONObject -/** Decodes `invoke()` params into typed values. */ +/** The JSON contract is shared with @wvb/bridge. */ internal object BridgeCodec { - /** The params object, or an empty one when absent. */ - fun params(raw: Any?): JSONObject = raw as? JSONObject ?: JSONObject() -} - -/** A required string param; throws [BridgeError] if missing. */ -internal fun JSONObject.requireString(key: String): String { - if (!has(key) || isNull(key)) { - throw BridgeError(code = "invalid_params", message = "expected string param \"$key\"") + fun params(raw: Any?): JSONObject = when (raw) { + null, JSONObject.NULL -> JSONObject() + is JSONObject -> raw + else -> throw invalidParam("params") } - return getString(key) } -/** An optional string param; `null` when absent or JSON `null`. */ +private fun invalidParam(key: String) = + BridgeError(code = "invalid_params", message = "invalid param \"$key\"") + +internal fun JSONObject.requireString(key: String): String = + opt(key) as? String ?: throw invalidParam(key) + internal fun JSONObject.optionalString(key: String): String? = - if (!has(key) || isNull(key)) null else getString(key) + if (!has(key) || isNull(key)) null else requireString(key) + +internal fun JSONObject.requireObject(key: String): JSONObject = + opt(key) as? JSONObject ?: throw invalidParam(key) + +internal fun JSONObject.optionalObject(key: String): JSONObject? = + if (!has(key) || isNull(key)) null else requireObject(key) + +internal fun JSONObject.requireArray(key: String): JSONArray = + opt(key) as? JSONArray ?: throw invalidParam(key) + +internal fun JSONObject.optionalBoolean(key: String): Boolean? = + if (!has(key) || isNull(key)) null else opt(key) as? Boolean ?: throw invalidParam(key) + +internal fun JSONObject.optionalUnsigned(key: String, max: ULong = ULong.MAX_VALUE): ULong? { + if (!has(key) || isNull(key)) return null + val number = opt(key) as? Number ?: throw invalidParam(key) + val value = number.toString().toULongOrNull() ?: throw invalidParam(key) + if (value > max) throw invalidParam(key) + return value +} + +internal fun JSONObject.mapValues(decode: (JSONObject, String) -> T): Map = + keys().asSequence().associateWith { decode(this, it) } + +internal fun JSONObject.stringMap(): Map = mapValues { obj, key -> obj.requireString(key) } + +internal fun JSONArray.strings(): List = (0 until length()).map { + opt(it) as? String ?: throw invalidParam("[$it]") +} + +internal fun JSONArray.objects(decode: (JSONObject) -> T): List = (0 until length()).map { + decode(opt(it) as? JSONObject ?: throw invalidParam("[$it]")) +} + +internal fun JSONObject.versionData() = ManifestVersionData( + integrity = optionalString("integrity"), metadata = optionalObject("metadata")?.stringMap(), +) +internal fun JSONObject.stageData() = ManifestStageData( + version = requireString("version"), data = optionalObject("data")?.versionData(), +) +internal fun JSONObject.removeData() = ManifestRemoveData( + versions = requireArray("versions").strings(), force = optionalBoolean("force"), +) +internal fun JSONObject.bundleUpdate() = BundleUpdate( + name = requireString("name"), version = requireString("version"), + downloadUrl = optionalString("downloadUrl"), integrity = optionalString("integrity"), + metadata = optionalObject("metadata")?.stringMap(), +) + +internal fun SourceKind.bridgeValue(): String = when (this) { + SourceKind.BUILTIN -> "builtin" + SourceKind.REMOTE -> "remote" +} + +internal fun ManifestBundleItemStatus.bridgeValue(): String = when (this) { + ManifestBundleItemStatus.CURRENT -> "current" + ManifestBundleItemStatus.PREVIOUS -> "previous" + ManifestBundleItemStatus.STAGED -> "staged" + ManifestBundleItemStatus.ORPHAN -> "orphan" +} + +internal fun ManifestStageResultKind.bridgeValue(): String = when (this) { + ManifestStageResultKind.STAGED -> "staged" + ManifestStageResultKind.IN_USE -> "in_use" +} -// These mirror the JSON the web side consumes from the Electron bridge -// (`@wvb/node`): the source kind is keyed `type` on the wire, and nullable -// fields are omitted (putOpt) to match the TS optionals. +internal fun ManifestSetCurrentVersionResultKind.bridgeValue(): String = when (this) { + ManifestSetCurrentVersionResultKind.SETTLED -> "settled" + ManifestSetCurrentVersionResultKind.NOT_EXISTS -> "not_exists" + ManifestSetCurrentVersionResultKind.VERSION_NOT_EXISTS -> "version_not_exists" +} + +internal fun ManifestRemoveResultKind.bridgeValue(): String = when (this) { + ManifestRemoveResultKind.REMOVED -> "removed" + ManifestRemoveResultKind.NOT_EXISTS -> "not_exists" + ManifestRemoveResultKind.VERSION_NOT_EXISTS -> "version_not_exists" + ManifestRemoveResultKind.IN_USE -> "in_use" +} + +internal val WebviewBundleException.bridgeCode: String + get() = when (this) { + is WebviewBundleException.CoreIo -> "core.io" + is WebviewBundleException.CoreCompress -> "core.compress" + is WebviewBundleException.CoreDecompress -> "core.decompress" + is WebviewBundleException.CoreEncode -> "core.encode" + is WebviewBundleException.CoreDecode -> "core.decode" + is WebviewBundleException.CoreHttp -> "core.http" + is WebviewBundleException.CoreHttpInvalidUri -> "core.http_invalid_uri" + is WebviewBundleException.CoreCancelled -> "core.cancelled" + is WebviewBundleException.CoreTimeout -> "core.timeout" + is WebviewBundleException.CoreInvalidMagicNum -> "core.invalid_magic_num" + is WebviewBundleException.CoreInvalidVersion -> "core.invalid_version" + is WebviewBundleException.CoreInvalidHeaderChecksum -> "core.invalid_header_checksum" + is WebviewBundleException.CoreInvalidIndexChecksum -> "core.invalid_index_checksum" + is WebviewBundleException.CoreChecksumMismatch -> "core.checksum_mismatch" + is WebviewBundleException.CoreBundleNotFound -> "core.bundle_not_found" + is WebviewBundleException.CoreInvalidFilepath -> "core.invalid_filepath" + is WebviewBundleException.CoreSerdeJson -> "core.serde_json" + is WebviewBundleException.CoreCannotResolveProxyServer -> "core.cannot_resolve_proxy_server" + is WebviewBundleException.CoreHttpClient -> "core.http_client" + is WebviewBundleException.CoreRemoteHttp -> "core.remote_http" + is WebviewBundleException.CoreBadRemoteResponse -> "core.bad_remote_response" + is WebviewBundleException.CoreInvalidRemoteConfig -> "core.invalid_remote_config" + is WebviewBundleException.CoreInvalidUpdaterConfig -> "core.invalid_updater_config" + is WebviewBundleException.CoreInvalidIntegrity -> "core.invalid_integrity" + is WebviewBundleException.CoreIntegrityRequired -> "core.integrity_required" + is WebviewBundleException.CoreIntegrityVerifyFailed -> "core.integrity_verify_failed" + is WebviewBundleException.CoreInvalidSignature -> "core.invalid_signature" + is WebviewBundleException.CoreInvalidSignatureKey -> "core.invalid_signature_key" + is WebviewBundleException.CoreExpectSignatureNotFound -> "core.expect_signature_not_found" + is WebviewBundleException.CoreSignatureVerifyFailed -> "core.signature_verify_failed" + is WebviewBundleException.CoreGeneric -> "core.generic" + is WebviewBundleException.BindingInvalidHeaderName -> "invalid_header_name" + is WebviewBundleException.BindingInvalidHeaderValue -> "invalid_header_value" + is WebviewBundleException.BindingInvalidSignatureOptions -> "invalid_signature_options" + } + +internal fun UpdaterDownloadResultKind.toJson(): JSONObject = when (this) { + UpdaterDownloadResultKind.Downloaded -> JSONObject().put("type", "downloaded") + is UpdaterDownloadResultKind.Error -> JSONObject().put("type", "error") + .put("code", v1.bridgeCode).put("message", v1.message) +} + +internal fun UpdaterInstallResultKind.toJson(): JSONObject = when (this) { + UpdaterInstallResultKind.Installed -> JSONObject().put("type", "installed") + UpdaterInstallResultKind.StagedVersionNotMatched -> JSONObject().put("type", "staged_version_not_matched") + UpdaterInstallResultKind.StagedBundleNotExists -> JSONObject().put("type", "staged_bundle_not_exists") + UpdaterInstallResultKind.VerifyFailed -> JSONObject().put("type", "verify_failed") + is UpdaterInstallResultKind.Error -> JSONObject().put("type", "error") + .put("code", v1.bridgeCode).put("message", v1.message) +} -private fun BundleSourceKind.bridgeValue(): String = when (this) { - BundleSourceKind.BUILTIN -> "builtin" - BundleSourceKind.REMOTE -> "remote" +internal fun UpdaterRollbackResultKind.toJson(): JSONObject = when (this) { + UpdaterRollbackResultKind.RolledBack -> JSONObject().put("type", "rolled_back") + UpdaterRollbackResultKind.PreviousVersionNotMatched -> JSONObject().put("type", "previous_version_not_matched") + UpdaterRollbackResultKind.PreviousBundleNotExists -> JSONObject().put("type", "previous_bundle_not_exists") + UpdaterRollbackResultKind.VerifyFailed -> JSONObject().put("type", "verify_failed") + is UpdaterRollbackResultKind.Error -> JSONObject().put("type", "error") + .put("code", v1.bridgeCode).put("message", v1.message) } -internal fun BundleManifestMetadata.toJson(): JSONObject = - JSONObject() - .putOpt("etag", etag) - .putOpt("integrity", integrity) - .putOpt("signature", signature) - .putOpt("lastModified", lastModified) - -internal fun BundleSourceVersion.toJson(): JSONObject = - JSONObject().put("type", kind.bridgeValue()).put("version", version) - -internal fun ListBundleItem.toJson(): JSONObject = - JSONObject() - .put("type", kind.bridgeValue()) - .put("name", name) - .put("version", version) - .put("current", current) - .put("metadata", metadata.toJson()) - -internal fun ListRemoteBundleInfo.toJson(): JSONObject = - JSONObject().put("name", name).put("version", version) - -internal fun RemoteBundleInfo.toJson(): JSONObject = - JSONObject() - .put("name", name) - .put("version", version) - .putOpt("etag", etag) - .putOpt("integrity", integrity) - .putOpt("signature", signature) - .putOpt("lastModified", lastModified) - -internal fun BundleUpdateInfo.toJson(): JSONObject = - JSONObject() - .put("name", name) - .put("version", version) - .put("isAvailable", isAvailable) - .putOpt("localVersion", localVersion) - .putOpt("etag", etag) - .putOpt("integrity", integrity) - .putOpt("signature", signature) - .putOpt("lastModified", lastModified) +internal fun ManifestVersionData.toJson(): JSONObject = JSONObject() + .putOpt("integrity", integrity) + .putOpt("metadata", metadata?.let { JSONObject(it) }) + +internal fun BundleSourceVersion.toJson(): JSONObject = JSONObject() + .put("source", source.bridgeValue()) + .put("version", version) + +internal fun ManifestBundleItem.toJson(): JSONObject = JSONObject() + .put("name", name) + .put("version", version) + .put("status", status.bridgeValue()) + .put("data", data.toJson()) + +internal fun SourceListItem.toJson(): JSONObject = JSONObject() + .put("source", source.bridgeValue()) + .put("item", item.toJson()) + +internal fun ManifestStageResult.toJson(): JSONObject = JSONObject() + .put("name", name) + .put("version", version) + .put("kind", kind.bridgeValue()) + +internal fun ManifestSetCurrentVersionResult.toJson(): JSONObject = JSONObject() + .put("name", name) + .put("version", version) + .put("kind", kind.bridgeValue()) + +internal fun ManifestRemoveResult.toJson(): JSONObject = JSONObject() + .put("name", name) + .put("version", version) + .put("kind", kind.bridgeValue()) + +internal fun ManifestPruneResult.toJson(): JSONObject = JSONObject() + .put("name", name) + .put("prunedVersions", JSONArray(prunedVersions)) + +internal fun BundleUpdate.toJson(): JSONObject = JSONObject() + .put("name", name) + .put("version", version) + .putOpt("downloadUrl", downloadUrl) + .putOpt("integrity", integrity) + .putOpt("metadata", metadata?.let { JSONObject(it) }) + +internal fun Update.toJson(): JSONObject = JSONObject() + .put("id", id) + .put("createdAt", createdAt) + .put("runtimeVersion", runtimeVersion.toInt()) + .put("bundles", JSONArray(bundles.map { it.toJson() })) + .put("metadata", JSONObject(metadata)) + +internal fun UpdateSignature.toJson(): JSONObject = JSONObject() + .put("keyId", keyId) + .put("sig", sig) + .put("alg", alg) + +internal fun RemoteUpdateResponse.toJson(): JSONObject = JSONObject() + .put("update", update.toJson()) + .putOpt("etag", etag) + .putOpt("signature", signature?.toJson()) + +internal fun UpdaterDownloadResult.toJson(): JSONObject = JSONObject() + .put("name", name) + .put("version", version) + .putOpt("integrity", integrity) + .putOpt("metadata", metadata?.let { JSONObject(it) }) + .put("result", result.toJson()) + +internal fun UpdaterInstallResult.toJson(): JSONObject = JSONObject() + .put("name", name) + .putOpt("targetVersion", targetVersion) + .putOpt("installVersion", installVersion) + .put("result", result.toJson()) + +internal fun UpdaterRollbackResult.toJson(): JSONObject = JSONObject() + .put("name", name) + .putOpt("targetVersion", targetVersion) + .putOpt("rollbackVersion", rollbackVersion) + .put("result", result.toJson()) diff --git a/lib/src/main/kotlin/dev/wvb/Source.kt b/lib/src/main/kotlin/dev/wvb/Source.kt index 5a15fc8..981e181 100644 --- a/lib/src/main/kotlin/dev/wvb/Source.kt +++ b/lib/src/main/kotlin/dev/wvb/Source.kt @@ -7,7 +7,7 @@ import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicLong /** - * Options for building a [BundleSource] with sensible Android defaults. Every field + * Options for building a [Source] with sensible Android defaults. Every field * is optional and falls back to an app-private directory when omitted. * * @property builtinAssetsDir when non-null, the APK `assets/` @@ -20,11 +20,11 @@ public data class SourceOptions( val builtinManifestFilepath: String? = null, val remoteManifestFilepath: String? = null, val builtinAssetsDir: String? = "bundles", - val integrity: BundleSourceIntegrityOptions? = null, - val signature: BundleSourceSignatureOptions? = null, + val integrity: SourceIntegrityOptions? = null, val dataReadOptions: DataReadOptions? = null, val headerReadOptions: HeaderReadOptions? = null, val indexReadOptions: IndexReadOptions? = null, + val removeBundleChunkSize: UInt? = null, ) /** `/.wvb/builtin/bundles` — the read-only directory for bundles shipped with the app. */ @@ -36,16 +36,16 @@ public fun defaultRemoteDir(context: Context): String = File(context.filesDir, ".wvb/remote/bundles").absolutePath /** - * Builds a [BundleSource] from [config], filling in default directories, copying + * Builds a [Source] from [config], filling in default directories, copying * bundled assets into the builtin dir, and creating the writable remote dir. * * Performs blocking file I/O; prefer building off the main thread when shipping * large builtin bundles. */ -public fun makeBundleSource( +public fun makeSource( context: Context, config: SourceOptions, -): BundleSource { +): Source { val builtinDir = config.builtinDir ?: defaultBuiltinDir(context) val remoteDir = config.remoteDir ?: defaultRemoteDir(context) @@ -56,18 +56,18 @@ public fun makeBundleSource( installBuiltinBundlesFromAssets(context, assetDir, builtinDir) } - val bundleConfig = BundleSourceConfig( + val bundleConfig = SourceConfig( builtinDir = builtinDir, remoteDir = remoteDir, builtinManifestFilepath = config.builtinManifestFilepath, remoteManifestFilepath = config.remoteManifestFilepath, integrity = config.integrity, - signature = config.signature, dataReadOptions = config.dataReadOptions, headerReadOptions = config.headerReadOptions, indexReadOptions = config.indexReadOptions, + removeBundleChunkSize = config.removeBundleChunkSize, ) - return BundleSource(bundleConfig) + return Source(bundleConfig) } /** Per-destination locks serializing concurrent same-process extraction. */ @@ -78,7 +78,7 @@ private val tmpCounter = AtomicLong(0) /** * Copies bundles shipped in the APK `assets/[assetDir]` (default `bundles/`) into - * [builtinDir] so the native [BundleSource] can read them as real files, returning + * [builtinDir] so the native [Source] can read them as real files, returning * the destination. Copies recursively as an additive overlay — files removed from * the assets in a later app version are **not** deleted from [builtinDir]. No-op * when the asset directory does not exist. diff --git a/lib/src/main/kotlin/dev/wvb/WebViewBundle.kt b/lib/src/main/kotlin/dev/wvb/WebViewBundle.kt index a35ac81..624292a 100644 --- a/lib/src/main/kotlin/dev/wvb/WebViewBundle.kt +++ b/lib/src/main/kotlin/dev/wvb/WebViewBundle.kt @@ -11,7 +11,8 @@ import kotlinx.coroutines.runBlocking /** Remote endpoint configuration for [WebViewBundleUpdaterConfig]. */ data class WebViewBundleRemoteConfig( /** The base URL of the remote server, e.g. `"https://bundles.example.com"`. */ - val endpoint: String, + val baseUrl: String, + val options: RemoteOptions? = null, ) /** Updater configuration for [WebViewBundleConfig]. */ @@ -19,8 +20,10 @@ data class WebViewBundleUpdaterConfig( val remote: WebViewBundleRemoteConfig, /** Release channel (e.g. `"stable"`, `"beta"`). */ val channel: String? = null, - val integrityPolicy: IntegrityPolicy? = null, - val signatureVerifier: SignatureVerifierOptions? = null, + val integrity: UpdaterIntegrityOptions? = null, + val signature: UpdaterSignatureOptions? = null, + /** Persisted update document; defaults to `/update.json`. */ + val updateFilepath: String? = null, ) /** @@ -65,7 +68,7 @@ private class RegisteredProtocol( */ class WebViewBundle private constructor( /** The bundle source requests are served from. */ - val source: BundleSource, + val source: Source, /** The remote endpoint, when an [WebViewBundleUpdaterConfig] was provided. */ val remote: Remote?, /** The updater, when an [WebViewBundleUpdaterConfig] was provided. */ @@ -174,27 +177,27 @@ class WebViewBundle private constructor( getInstance(context, config) private fun create(context: Context, config: WebViewBundleConfig): WebViewBundle { - val source = makeBundleSource(context, config.source) + WvbAndroid.initialize(context) + val source = makeSource(context, config.source) var remote: Remote? = null var updater: Updater? = null val registered = mutableListOf() try { config.updater?.let { updaterConfig -> - val createdRemote = Remote(updaterConfig.remote.endpoint) + val createdRemote = Remote(updaterConfig.remote.baseUrl, updaterConfig.remote.options) remote = createdRemote // assign before Updater(), so a throw there still closes it updater = Updater( source, createdRemote, + updaterConfig.updateFilepath ?: java.io.File( + config.source.remoteDir ?: defaultRemoteDir(context), "update.json" + ).absolutePath, UpdaterOptions( channel = updaterConfig.channel, - integrity = updaterConfig.integrityPolicy?.let { - UpdaterIntegrityOptions(policy = it) - }, - signature = updaterConfig.signatureVerifier?.let { - UpdaterSignatureOptions(verify = SignatureVerification.Key(it)) - }, + integrity = updaterConfig.integrity, + signature = updaterConfig.signature, ), ) } diff --git a/lib/src/main/kotlin/dev/wvb/WebViewBundleBridge.kt b/lib/src/main/kotlin/dev/wvb/WebViewBundleBridge.kt index b9f81b9..78d2241 100644 --- a/lib/src/main/kotlin/dev/wvb/WebViewBundleBridge.kt +++ b/lib/src/main/kotlin/dev/wvb/WebViewBundleBridge.kt @@ -2,9 +2,7 @@ package dev.wvb import org.json.JSONArray -/** - * Bridges to communicate with the WebView. - */ +/** Commands and payloads match @wvb/bridge's source, remote, and updater APIs. */ internal class WebViewBundleBridge(private val wvb: WebViewBundle) : BridgeHandlers { override fun register(bridge: Bridge) { registerSource(bridge) @@ -14,111 +12,137 @@ internal class WebViewBundleBridge(private val wvb: WebViewBundle) : BridgeHandl private fun registerSource(bridge: Bridge) { val source = wvb.source - bridge.handler("sourceListBundles") { JSONArray(source.listBundles().map { it.toJson() }) } - bridge.handler("sourceLoadVersion") { params -> - source.loadVersion(BridgeCodec.params(params).requireString("bundleName"))?.toJson() + bridge.handler("sourceListBundles") { + JSONArray(source.listBundles().map { it.toJson() }) } - bridge.handler("sourceUpdateVersion") { params -> + bridge.handler("sourceListBuiltinBundles") { + JSONArray(source.listBuiltinBundles().map { it.toJson() }) + } + bridge.handler("sourceListRemoteBundles") { + JSONArray(source.listRemoteBundles().map { it.toJson() }) + } + bridge.handler("sourceGetVersion") { params -> val p = BridgeCodec.params(params) - source.updateVersion(p.requireString("bundleName"), p.requireString("version")) - null + source.getVersion(p.requireString("bundleName"))?.toJson() + } + bridge.handler("sourceGetRemoteStagedVersion") { params -> + val p = BridgeCodec.params(params) + source.getRemoteStagedVersion(p.requireString("bundleName")) + } + bridge.handler("sourceGetRemotePreviousVersion") { params -> + val p = BridgeCodec.params(params) + source.getRemotePreviousVersion(p.requireString("bundleName")) } bridge.handler("sourceResolveFilepath") { params -> - source.resolveFilepath(BridgeCodec.params(params).requireString("bundleName")) + val p = BridgeCodec.params(params) + source.resolveFilepath(p.requireString("bundleName")) + } + bridge.handler("sourceUnload") { params -> + val p = BridgeCodec.params(params) + source.unload(p.requireString("bundleName")) } bridge.handler("sourceGetBuiltinBundleFilepath") { params -> val p = BridgeCodec.params(params) - source.getBuiltinBundleFilepath( - p.requireString("bundleName"), p.requireString("version") - ) + source.getBuiltinBundleFilepath(p.requireString("bundleName"), p.requireString("version")) } bridge.handler("sourceGetRemoteBundleFilepath") { params -> val p = BridgeCodec.params(params) - source.getRemoteBundleFilepath( - p.requireString("bundleName"), p.requireString("version") - ) + source.getRemoteBundleFilepath(p.requireString("bundleName"), p.requireString("version")) + } + bridge.handler("sourceGetBuiltinVersionData") { params -> + val p = BridgeCodec.params(params) + source.getBuiltinVersionData(p.requireString("bundleName"), p.requireString("version"))?.toJson() + } + bridge.handler("sourceGetRemoteVersionData") { params -> + val p = BridgeCodec.params(params) + source.getRemoteVersionData(p.requireString("bundleName"), p.requireString("version"))?.toJson() + } + bridge.handler("sourceUpdateRemoteVersion") { params -> + val p = BridgeCodec.params(params) + source.updateRemoteVersion(p.requireString("bundleName"), p.requireString("version")).toJson() + } + bridge.handler("sourceUpdateRemoteVersions") { params -> + val p = BridgeCodec.params(params) + JSONArray(source.updateRemoteVersions(p.requireObject("items").stringMap()).map { it.toJson() }) } - bridge.handler("sourceLoadBuiltinMetadata") { params -> + bridge.handler("sourceStageRemoteBundle") { params -> val p = BridgeCodec.params(params) - source.loadBuiltinMetadata(p.requireString("bundleName"), p.requireString("version")) - ?.toJson() + source.stageRemoteBundle(p.requireString("bundleName"), p.requireObject("data").stageData()).toJson() } - bridge.handler("sourceLoadRemoteMetadata") { params -> + bridge.handler("sourceStageRemoteBundles") { params -> val p = BridgeCodec.params(params) - source.loadRemoteMetadata(p.requireString("bundleName"), p.requireString("version")) - ?.toJson() + JSONArray(source.stageRemoteBundles(p.requireObject("items").mapValues { obj, key -> obj.requireObject(key).stageData() }).map { it.toJson() }) } - bridge.handler("sourceUnloadDescriptor") { params -> - source.unloadDescriptor(BridgeCodec.params(params).requireString("bundleName")) + bridge.handler("sourceRemoveRemoteBundles") { params -> + val p = BridgeCodec.params(params) + JSONArray(source.removeRemoteBundles(p.requireObject("items").mapValues { obj, key -> obj.requireObject(key).removeData() }).map { it.toJson() }) } bridge.handler("sourceRemoveRemoteBundle") { params -> val p = BridgeCodec.params(params) - source.removeRemoteBundle(p.requireString("bundleName"), p.requireString("version")) + source.removeRemoteBundle(p.requireString("bundleName"), p.requireString("version"), p.optionalBoolean("force")).toJson() } - bridge.handler("sourceRemoteRetainedVersions") { params -> - source.remoteRetainedVersions(BridgeCodec.params(params).requireString("bundleName")) + bridge.handler("sourcePruneRemoteBundle") { params -> + val p = BridgeCodec.params(params) + source.pruneRemoteBundle(p.requireString("bundleName")).toJson() } bridge.handler("sourcePruneRemoteBundles") { params -> - source.pruneRemoteBundles(BridgeCodec.params(params).requireString("bundleName")) + val p = BridgeCodec.params(params) + JSONArray(source.pruneRemoteBundles(p.requireArray("bundleNames").strings()).map { it.toJson() }) } } private fun registerRemote(bridge: Bridge) { - bridge.handler("remoteListBundles") { params -> - val channel = BridgeCodec.params(params).optionalString("channel") - JSONArray( - requireRemote().listBundles(RemoteFetchOptions(channel = channel)) - .map { it.toJson() }, - ) - } - bridge.handler("remoteGetInfo") { params -> - val p = BridgeCodec.params(params) - requireRemote().getInfo( - p.requireString("bundleName"), - RemoteFetchOptions(channel = p.optionalString("channel")), - ).toJson() + bridge.handler("remoteGetUpdate") { params -> + val options = BridgeCodec.params(params).optionalObject("options") + requireRemote().getUpdate(options?.let { + RemoteGetUpdateOptions(etag = it.optionalString("etag"), channel = it.optionalString("channel")) + })?.toJson() } bridge.handler("remoteDownload") { params -> val p = BridgeCodec.params(params) - requireRemote().download( - p.requireString("bundleName"), p.optionalString("channel") - ).info.toJson() - } - bridge.handler("remoteDownloadVersion") { params -> - val p = BridgeCodec.params(params) - requireRemote().downloadVersion( - p.requireString("bundleName"), p.requireString("version") - ).info.toJson() + requireRemote().download(p.requireString("url"), p.requireString("filepath")) + null } } private fun registerUpdater(bridge: Bridge) { - bridge.handler("updaterListRemotes") { - JSONArray( - requireUpdater().listRemotes().map { it.toJson() }) - } bridge.handler("updaterGetUpdate") { params -> - requireUpdater().getUpdate(BridgeCodec.params(params).requireString("bundleName")) - .toJson() + val options = BridgeCodec.params(params).optionalObject("options") + requireUpdater().getUpdate(options?.let { + UpdaterGetUpdateOptions(expectSignatureKeyId = it.optionalString("expectSignatureKeyId")) + })?.toJson() } bridge.handler("updaterDownload") { params -> val p = BridgeCodec.params(params) - requireUpdater().downloadUpdate( - p.requireString("bundleName"), p.optionalString("version") - ).toJson() + val options = p.optionalObject("options")?.let { + UpdaterDownloadOptions( + concurrency = it.optionalUnsigned("concurrency", UInt.MAX_VALUE.toULong())?.toUInt(), + timeout = it.optionalUnsigned("timeout"), + ) + } + JSONArray(requireUpdater().download( + p.requireArray("bundleUpdates").objects { it.bundleUpdate() }, options, + ).map { it.toJson() }) } bridge.handler("updaterInstall") { params -> - val p = BridgeCodec.params(params) - requireUpdater().install(p.requireString("bundleName"), p.requireString("version")) - null + val targets = BridgeCodec.params(params).requireArray("targets").objects { + UpdaterInstallTarget(name = it.requireString("name"), version = it.optionalString("version")) + } + JSONArray(requireUpdater().install(targets).map { it.toJson() }) + } + bridge.handler("updaterRollback") { params -> + val targets = BridgeCodec.params(params).requireArray("targets").objects { + UpdaterRollbackTarget(name = it.requireString("name"), version = it.optionalString("version")) + } + JSONArray(requireUpdater().rollback(targets).map { it.toJson() }) } } private fun requireRemote(): Remote = wvb.remote ?: throw BridgeError( - code = "remote_not_initialized", message = "remote is not initialized." + code = "remote_not_initialized", message = "remote is not initialized.", ) private fun requireUpdater(): Updater = wvb.updater ?: throw BridgeError( - code = "updater_not_initialized", message = "updater is not initialized." + code = "updater_not_initialized", message = "updater is not initialized.", ) } diff --git a/lib/src/main/kotlin/dev/wvb/wvb_ffi.kt b/lib/src/main/kotlin/dev/wvb/WvbFFI.kt similarity index 78% rename from lib/src/main/kotlin/dev/wvb/wvb_ffi.kt rename to lib/src/main/kotlin/dev/wvb/WvbFFI.kt index d7105ec..2866547 100644 --- a/lib/src/main/kotlin/dev/wvb/wvb_ffi.kt +++ b/lib/src/main/kotlin/dev/wvb/WvbFFI.kt @@ -30,6 +30,8 @@ import java.nio.CharBuffer import java.nio.charset.CodingErrorAction import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.ConcurrentHashMap +import android.os.Build +import androidx.annotation.RequiresApi import java.util.concurrent.atomic.AtomicBoolean import kotlin.coroutines.resume import kotlinx.coroutines.CancellableContinuation @@ -620,37 +622,15 @@ internal open class UniffiForeignFutureResultVoid( internal interface UniffiForeignFutureCompleteVoid : com.sun.jna.Callback { fun callback(`callbackData`: Long,`result`: UniffiForeignFutureResultVoid.UniffiByValue,) } -internal interface UniffiCallbackInterfaceIntegrityCheckMethod0 : com.sun.jna.Callback { - fun callback(`uniffiHandle`: Long,`data`: RustBuffer.ByValue,`integrity`: RustBuffer.ByValue,`uniffiFutureCallback`: UniffiForeignFutureCompleteI8,`uniffiCallbackData`: Long,`uniffiOutDroppedCallback`: UniffiForeignFutureDroppedCallbackStruct,) -} internal interface UniffiCallbackInterfaceProxyResolverMethod0 : com.sun.jna.Callback { fun callback(`uniffiHandle`: Long,`uri`: RustBuffer.ByValue,`uniffiFutureCallback`: UniffiForeignFutureCompleteRustBuffer,`uniffiCallbackData`: Long,`uniffiOutDroppedCallback`: UniffiForeignFutureDroppedCallbackStruct,) } internal interface UniffiCallbackInterfaceRemoteOnDownloadMethod0 : com.sun.jna.Callback { fun callback(`uniffiHandle`: Long,`data`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) } -internal interface UniffiCallbackInterfaceSignatureVerifyMethod0 : com.sun.jna.Callback { +internal interface UniffiCallbackInterfaceSignatureCustomVerifyMethod0 : com.sun.jna.Callback { fun callback(`uniffiHandle`: Long,`message`: RustBuffer.ByValue,`signature`: RustBuffer.ByValue,`uniffiFutureCallback`: UniffiForeignFutureCompleteI8,`uniffiCallbackData`: Long,`uniffiOutDroppedCallback`: UniffiForeignFutureDroppedCallbackStruct,) } -@Structure.FieldOrder("uniffiFree", "uniffiClone", "check") -internal open class UniffiVTableCallbackInterfaceIntegrityCheck( - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, - @JvmField internal var `uniffiClone`: UniffiCallbackInterfaceClone? = null, - @JvmField internal var `check`: UniffiCallbackInterfaceIntegrityCheckMethod0? = null, -) : Structure() { - class UniffiByValue( - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - `uniffiClone`: UniffiCallbackInterfaceClone? = null, - `check`: UniffiCallbackInterfaceIntegrityCheckMethod0? = null, - ): UniffiVTableCallbackInterfaceIntegrityCheck(`uniffiFree`,`uniffiClone`,`check`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceIntegrityCheck) { - `uniffiFree` = other.`uniffiFree` - `uniffiClone` = other.`uniffiClone` - `check` = other.`check` - } - -} @Structure.FieldOrder("uniffiFree", "uniffiClone", "resolve") internal open class UniffiVTableCallbackInterfaceProxyResolver( @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, @@ -690,18 +670,18 @@ internal open class UniffiVTableCallbackInterfaceRemoteOnDownload( } @Structure.FieldOrder("uniffiFree", "uniffiClone", "verify") -internal open class UniffiVTableCallbackInterfaceSignatureVerify( +internal open class UniffiVTableCallbackInterfaceSignatureCustomVerify( @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, @JvmField internal var `uniffiClone`: UniffiCallbackInterfaceClone? = null, - @JvmField internal var `verify`: UniffiCallbackInterfaceSignatureVerifyMethod0? = null, + @JvmField internal var `verify`: UniffiCallbackInterfaceSignatureCustomVerifyMethod0? = null, ) : Structure() { class UniffiByValue( `uniffiFree`: UniffiCallbackInterfaceFree? = null, `uniffiClone`: UniffiCallbackInterfaceClone? = null, - `verify`: UniffiCallbackInterfaceSignatureVerifyMethod0? = null, - ): UniffiVTableCallbackInterfaceSignatureVerify(`uniffiFree`,`uniffiClone`,`verify`,), Structure.ByValue + `verify`: UniffiCallbackInterfaceSignatureCustomVerifyMethod0? = null, + ): UniffiVTableCallbackInterfaceSignatureCustomVerify(`uniffiFree`,`uniffiClone`,`verify`,), Structure.ByValue - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceSignatureVerify) { + internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceSignatureCustomVerify) { `uniffiFree` = other.`uniffiFree` `uniffiClone` = other.`uniffiClone` `verify` = other.`verify` @@ -739,6 +719,14 @@ internal object IntegrityCheckingUniffiLib { ): Short external fun uniffi_wvb_ffi_checksum_func_write_bundle_to_bytes( ): Short + external fun uniffi_wvb_ffi_checksum_func_extension( + ): Short + external fun uniffi_wvb_ffi_checksum_func_mime_type( + ): Short + external fun uniffi_wvb_ffi_checksum_func_runtime_version( + ): Short + external fun uniffi_wvb_ffi_checksum_func_update_protocol_version( + ): Short external fun uniffi_wvb_ffi_checksum_method_bundle_descriptor( ): Short external fun uniffi_wvb_ffi_checksum_method_bundle_get_data( @@ -787,14 +775,16 @@ internal object IntegrityCheckingUniffiLib { ): Short external fun uniffi_wvb_ffi_checksum_method_index_get_entry( ): Short + external fun uniffi_wvb_ffi_checksum_method_cancellation_cancel( + ): Short + external fun uniffi_wvb_ffi_checksum_method_cancellation_is_cancelled( + ): Short external fun uniffi_wvb_ffi_checksum_method_integrity_serialize( ): Short external fun uniffi_wvb_ffi_checksum_method_integrity_validate( ): Short external fun uniffi_wvb_ffi_checksum_method_integrity_value( ): Short - external fun uniffi_wvb_ffi_checksum_method_integritycheck_check( - ): Short external fun uniffi_wvb_ffi_checksum_method_bundleprotocolhandler_handle( ): Short external fun uniffi_wvb_ffi_checksum_method_proxyprotocolhandler_handle( @@ -803,68 +793,80 @@ internal object IntegrityCheckingUniffiLib { ): Short external fun uniffi_wvb_ffi_checksum_method_remote_download( ): Short - external fun uniffi_wvb_ffi_checksum_method_remote_download_version( + external fun uniffi_wvb_ffi_checksum_method_remote_get_update( ): Short - external fun uniffi_wvb_ffi_checksum_method_remote_get_info( + external fun uniffi_wvb_ffi_checksum_method_remoteondownload_on_download( ): Short - external fun uniffi_wvb_ffi_checksum_method_remote_list_bundles( + external fun uniffi_wvb_ffi_checksum_method_signaturecustomverify_verify( ): Short - external fun uniffi_wvb_ffi_checksum_method_remoteondownload_on_download( + external fun uniffi_wvb_ffi_checksum_method_loadeddescriptor_descriptor( + ): Short + external fun uniffi_wvb_ffi_checksum_method_loadeddescriptor_get_data( ): Short - external fun uniffi_wvb_ffi_checksum_method_signatureverify_verify( + external fun uniffi_wvb_ffi_checksum_method_loadeddescriptor_get_data_checksum( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_fetch_builtin_bundle( + external fun uniffi_wvb_ffi_checksum_method_source_fetch_builtin_bundle( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_fetch_bundle( + external fun uniffi_wvb_ffi_checksum_method_source_fetch_bundle( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_fetch_descriptor( + external fun uniffi_wvb_ffi_checksum_method_source_fetch_descriptor( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_fetch_remote_bundle( + external fun uniffi_wvb_ffi_checksum_method_source_fetch_remote_bundle( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_get_builtin_bundle_filepath( + external fun uniffi_wvb_ffi_checksum_method_source_get_builtin_bundle_filepath( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_get_remote_bundle_filepath( + external fun uniffi_wvb_ffi_checksum_method_source_get_builtin_version_data( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_list_bundles( + external fun uniffi_wvb_ffi_checksum_method_source_get_remote_bundle_filepath( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_load_builtin_metadata( + external fun uniffi_wvb_ffi_checksum_method_source_get_remote_previous_version( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_load_descriptor( + external fun uniffi_wvb_ffi_checksum_method_source_get_remote_staged_version( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_load_remote_metadata( + external fun uniffi_wvb_ffi_checksum_method_source_get_remote_version_data( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_load_version( + external fun uniffi_wvb_ffi_checksum_method_source_get_version( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_prune_remote_bundles( + external fun uniffi_wvb_ffi_checksum_method_source_list_builtin_bundles( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_remote_retained_versions( + external fun uniffi_wvb_ffi_checksum_method_source_list_bundles( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_remove_remote_bundle( + external fun uniffi_wvb_ffi_checksum_method_source_list_remote_bundles( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_resolve_filepath( + external fun uniffi_wvb_ffi_checksum_method_source_load( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_unload_descriptor( + external fun uniffi_wvb_ffi_checksum_method_source_prune_remote_bundle( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_update_version( + external fun uniffi_wvb_ffi_checksum_method_source_prune_remote_bundles( ): Short - external fun uniffi_wvb_ffi_checksum_method_bundlesource_write_remote_bundle( + external fun uniffi_wvb_ffi_checksum_method_source_remove_remote_bundle( ): Short - external fun uniffi_wvb_ffi_checksum_method_loadeddescriptor_descriptor( + external fun uniffi_wvb_ffi_checksum_method_source_remove_remote_bundles( ): Short - external fun uniffi_wvb_ffi_checksum_method_loadeddescriptor_get_data( + external fun uniffi_wvb_ffi_checksum_method_source_resolve_filepath( ): Short - external fun uniffi_wvb_ffi_checksum_method_loadeddescriptor_get_data_checksum( + external fun uniffi_wvb_ffi_checksum_method_source_stage_remote_bundle( + ): Short + external fun uniffi_wvb_ffi_checksum_method_source_stage_remote_bundles( + ): Short + external fun uniffi_wvb_ffi_checksum_method_source_unload( + ): Short + external fun uniffi_wvb_ffi_checksum_method_source_update_remote_version( ): Short - external fun uniffi_wvb_ffi_checksum_method_updater_download_update( + external fun uniffi_wvb_ffi_checksum_method_source_update_remote_versions( + ): Short + external fun uniffi_wvb_ffi_checksum_method_updater_download( ): Short external fun uniffi_wvb_ffi_checksum_method_updater_get_update( ): Short external fun uniffi_wvb_ffi_checksum_method_updater_install( ): Short - external fun uniffi_wvb_ffi_checksum_method_updater_list_remotes( + external fun uniffi_wvb_ffi_checksum_method_updater_rollback( ): Short external fun uniffi_wvb_ffi_checksum_constructor_bundlebuilder_new( ): Short + external fun uniffi_wvb_ffi_checksum_constructor_cancellation_new( + ): Short external fun uniffi_wvb_ffi_checksum_constructor_integrity_compute( ): Short external fun uniffi_wvb_ffi_checksum_constructor_integrity_parse( @@ -877,7 +879,7 @@ internal object IntegrityCheckingUniffiLib { ): Short external fun uniffi_wvb_ffi_checksum_constructor_remote_new( ): Short - external fun uniffi_wvb_ffi_checksum_constructor_bundlesource_new( + external fun uniffi_wvb_ffi_checksum_constructor_source_new( ): Short external fun uniffi_wvb_ffi_checksum_constructor_updater_new( ): Short @@ -897,10 +899,9 @@ internal object UniffiLib { init { Native.register(UniffiLib::class.java, findLibraryName(componentName = "wvb_ffi")) - uniffiCallbackInterfaceIntegrityCheck.register(this) uniffiCallbackInterfaceProxyResolver.register(this) uniffiCallbackInterfaceRemoteOnDownload.register(this) - uniffiCallbackInterfaceSignatureVerify.register(this) + uniffiCallbackInterfaceSignatureCustomVerify.register(this) } external fun uniffi_wvb_ffi_fn_clone_bundle(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, @@ -973,6 +974,16 @@ external fun uniffi_wvb_ffi_fn_method_index_entries(`ptr`: Long,uniffi_out_err: ): RustBuffer.ByValue external fun uniffi_wvb_ffi_fn_method_index_get_entry(`ptr`: Long,`path`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): RustBuffer.ByValue +external fun uniffi_wvb_ffi_fn_clone_cancellation(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Long +external fun uniffi_wvb_ffi_fn_free_cancellation(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Unit +external fun uniffi_wvb_ffi_fn_constructor_cancellation_new(uniffi_out_err: UniffiRustCallStatus, +): Long +external fun uniffi_wvb_ffi_fn_method_cancellation_cancel(`ptr`: Long,uniffi_out_err: UniffiRustCallStatus, +): Unit +external fun uniffi_wvb_ffi_fn_method_cancellation_is_cancelled(`ptr`: Long,uniffi_out_err: UniffiRustCallStatus, +): Byte external fun uniffi_wvb_ffi_fn_clone_integrity(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, ): Long external fun uniffi_wvb_ffi_fn_free_integrity(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, @@ -987,14 +998,6 @@ external fun uniffi_wvb_ffi_fn_method_integrity_validate(`ptr`: Long,`data`: Rus ): Byte external fun uniffi_wvb_ffi_fn_method_integrity_value(`ptr`: Long,uniffi_out_err: UniffiRustCallStatus, ): RustBuffer.ByValue -external fun uniffi_wvb_ffi_fn_clone_integritycheck(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Long -external fun uniffi_wvb_ffi_fn_free_integritycheck(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Unit -external fun uniffi_wvb_ffi_fn_init_callback_vtable_integritycheck(`vtable`: UniffiVTableCallbackInterfaceIntegrityCheck, -): Unit -external fun uniffi_wvb_ffi_fn_method_integritycheck_check(`ptr`: Long,`data`: RustBuffer.ByValue,`integrity`: RustBuffer.ByValue, -): Long external fun uniffi_wvb_ffi_fn_clone_bundleprotocolhandler(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, ): Long external fun uniffi_wvb_ffi_fn_free_bundleprotocolhandler(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, @@ -1025,15 +1028,11 @@ external fun uniffi_wvb_ffi_fn_clone_remote(`handle`: Long,uniffi_out_err: Uniff ): Long external fun uniffi_wvb_ffi_fn_free_remote(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, ): Unit -external fun uniffi_wvb_ffi_fn_constructor_remote_new(`endpoint`: RustBuffer.ByValue,`options`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Long -external fun uniffi_wvb_ffi_fn_method_remote_download(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`channel`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_constructor_remote_new(`baseUrl`: RustBuffer.ByValue,`options`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): Long -external fun uniffi_wvb_ffi_fn_method_remote_download_version(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_remote_download(`ptr`: Long,`url`: RustBuffer.ByValue,`filepath`: RustBuffer.ByValue,`cancellation`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_remote_get_info(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`options`: RustBuffer.ByValue, -): Long -external fun uniffi_wvb_ffi_fn_method_remote_list_bundles(`ptr`: Long,`options`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_remote_get_update(`ptr`: Long,`options`: RustBuffer.ByValue, ): Long external fun uniffi_wvb_ffi_fn_clone_remoteondownload(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, ): Long @@ -1043,79 +1042,93 @@ external fun uniffi_wvb_ffi_fn_init_callback_vtable_remoteondownload(`vtable`: U ): Unit external fun uniffi_wvb_ffi_fn_method_remoteondownload_on_download(`ptr`: Long,`data`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): Unit -external fun uniffi_wvb_ffi_fn_clone_signatureverify(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +external fun uniffi_wvb_ffi_fn_clone_signaturecustomverify(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, ): Long -external fun uniffi_wvb_ffi_fn_free_signatureverify(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +external fun uniffi_wvb_ffi_fn_free_signaturecustomverify(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Unit +external fun uniffi_wvb_ffi_fn_init_callback_vtable_signaturecustomverify(`vtable`: UniffiVTableCallbackInterfaceSignatureCustomVerify, ): Unit -external fun uniffi_wvb_ffi_fn_init_callback_vtable_signatureverify(`vtable`: UniffiVTableCallbackInterfaceSignatureVerify, +external fun uniffi_wvb_ffi_fn_method_signaturecustomverify_verify(`ptr`: Long,`message`: RustBuffer.ByValue,`signature`: RustBuffer.ByValue, +): Long +external fun uniffi_wvb_ffi_fn_clone_loadeddescriptor(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Long +external fun uniffi_wvb_ffi_fn_free_loadeddescriptor(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, ): Unit -external fun uniffi_wvb_ffi_fn_method_signatureverify_verify(`ptr`: Long,`message`: RustBuffer.ByValue,`signature`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_loadeddescriptor_descriptor(`ptr`: Long,uniffi_out_err: UniffiRustCallStatus, +): Long +external fun uniffi_wvb_ffi_fn_method_loadeddescriptor_get_data(`ptr`: Long,`path`: RustBuffer.ByValue, +): Long +external fun uniffi_wvb_ffi_fn_method_loadeddescriptor_get_data_checksum(`ptr`: Long,`path`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_clone_bundlesource(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +external fun uniffi_wvb_ffi_fn_clone_source(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, ): Long -external fun uniffi_wvb_ffi_fn_free_bundlesource(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +external fun uniffi_wvb_ffi_fn_free_source(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, ): Unit -external fun uniffi_wvb_ffi_fn_constructor_bundlesource_new(`config`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +external fun uniffi_wvb_ffi_fn_constructor_source_new(`config`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_fetch_builtin_bundle(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_fetch_builtin_bundle(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_fetch_bundle(`ptr`: Long,`bundleName`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_fetch_bundle(`ptr`: Long,`bundleName`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_fetch_descriptor(`ptr`: Long,`bundleName`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_fetch_descriptor(`ptr`: Long,`bundleName`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_fetch_remote_bundle(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_fetch_remote_bundle(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_get_builtin_bundle_filepath(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +external fun uniffi_wvb_ffi_fn_method_source_get_builtin_bundle_filepath(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): RustBuffer.ByValue -external fun uniffi_wvb_ffi_fn_method_bundlesource_get_remote_bundle_filepath(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +external fun uniffi_wvb_ffi_fn_method_source_get_builtin_version_data(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, +): Long +external fun uniffi_wvb_ffi_fn_method_source_get_remote_bundle_filepath(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): RustBuffer.ByValue -external fun uniffi_wvb_ffi_fn_method_bundlesource_list_bundles(`ptr`: Long, +external fun uniffi_wvb_ffi_fn_method_source_get_remote_previous_version(`ptr`: Long,`bundleName`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_load_builtin_metadata(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_get_remote_staged_version(`ptr`: Long,`bundleName`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_load_descriptor(`ptr`: Long,`bundleName`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_get_remote_version_data(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_load_remote_metadata(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_get_version(`ptr`: Long,`bundleName`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_load_version(`ptr`: Long,`bundleName`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_list_builtin_bundles(`ptr`: Long, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_prune_remote_bundles(`ptr`: Long,`bundleName`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_list_bundles(`ptr`: Long, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_remote_retained_versions(`ptr`: Long,`bundleName`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_list_remote_bundles(`ptr`: Long, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_remove_remote_bundle(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_load(`ptr`: Long,`bundleName`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_resolve_filepath(`ptr`: Long,`bundleName`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_prune_remote_bundle(`ptr`: Long,`bundleName`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_unload_descriptor(`ptr`: Long,`bundleName`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Byte -external fun uniffi_wvb_ffi_fn_method_bundlesource_update_version(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_prune_remote_bundles(`ptr`: Long,`bundleNames`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_bundlesource_write_remote_bundle(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue,`bundle`: Long,`metadata`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_remove_remote_bundle(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue,`force`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_clone_loadeddescriptor(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +external fun uniffi_wvb_ffi_fn_method_source_remove_remote_bundles(`ptr`: Long,`items`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_free_loadeddescriptor(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Unit -external fun uniffi_wvb_ffi_fn_method_loadeddescriptor_descriptor(`ptr`: Long,uniffi_out_err: UniffiRustCallStatus, +external fun uniffi_wvb_ffi_fn_method_source_resolve_filepath(`ptr`: Long,`bundleName`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_loadeddescriptor_get_data(`ptr`: Long,`path`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_stage_remote_bundle(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`data`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_loadeddescriptor_get_data_checksum(`ptr`: Long,`path`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_source_stage_remote_bundles(`ptr`: Long,`items`: RustBuffer.ByValue, +): Long +external fun uniffi_wvb_ffi_fn_method_source_unload(`ptr`: Long,`bundleName`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Byte +external fun uniffi_wvb_ffi_fn_method_source_update_remote_version(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, +): Long +external fun uniffi_wvb_ffi_fn_method_source_update_remote_versions(`ptr`: Long,`items`: RustBuffer.ByValue, ): Long external fun uniffi_wvb_ffi_fn_clone_updater(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, ): Long external fun uniffi_wvb_ffi_fn_free_updater(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, ): Unit -external fun uniffi_wvb_ffi_fn_constructor_updater_new(`source`: Long,`remote`: Long,`options`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +external fun uniffi_wvb_ffi_fn_constructor_updater_new(`source`: Long,`remote`: Long,`updateFilepath`: RustBuffer.ByValue,`options`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): Long -external fun uniffi_wvb_ffi_fn_method_updater_download_update(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_updater_download(`ptr`: Long,`bundleUpdates`: RustBuffer.ByValue,`options`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_updater_get_update(`ptr`: Long,`bundleName`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_updater_get_update(`ptr`: Long,`options`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_updater_install(`ptr`: Long,`bundleName`: RustBuffer.ByValue,`version`: RustBuffer.ByValue, +external fun uniffi_wvb_ffi_fn_method_updater_install(`ptr`: Long,`targets`: RustBuffer.ByValue, ): Long -external fun uniffi_wvb_ffi_fn_method_updater_list_remotes(`ptr`: Long, +external fun uniffi_wvb_ffi_fn_method_updater_rollback(`ptr`: Long,`targets`: RustBuffer.ByValue, ): Long external fun uniffi_wvb_ffi_fn_func_read_bundle(`filepath`: RustBuffer.ByValue, ): Long @@ -1125,6 +1138,14 @@ external fun uniffi_wvb_ffi_fn_func_write_bundle(`bundle`: Long,`filepath`: Rust ): Long external fun uniffi_wvb_ffi_fn_func_write_bundle_to_bytes(`bundle`: Long,uniffi_out_err: UniffiRustCallStatus, ): RustBuffer.ByValue +external fun uniffi_wvb_ffi_fn_func_extension(uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +external fun uniffi_wvb_ffi_fn_func_mime_type(uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +external fun uniffi_wvb_ffi_fn_func_runtime_version(uniffi_out_err: UniffiRustCallStatus, +): Byte +external fun uniffi_wvb_ffi_fn_func_update_protocol_version(uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue external fun ffi_wvb_ffi_rustbuffer_alloc(`size`: Long,uniffi_out_err: UniffiRustCallStatus, ): RustBuffer.ByValue external fun ffi_wvb_ffi_rustbuffer_from_bytes(`bytes`: ForeignBytes.ByValue,uniffi_out_err: UniffiRustCallStatus, @@ -1256,6 +1277,18 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { if (lib.uniffi_wvb_ffi_checksum_func_write_bundle_to_bytes() != 60094.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } + if (lib.uniffi_wvb_ffi_checksum_func_extension() != 30907.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_wvb_ffi_checksum_func_mime_type() != 22305.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_wvb_ffi_checksum_func_runtime_version() != 51343.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_wvb_ffi_checksum_func_update_protocol_version() != 24855.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } if (lib.uniffi_wvb_ffi_checksum_method_bundle_descriptor() != 24504.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } @@ -1328,6 +1361,12 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { if (lib.uniffi_wvb_ffi_checksum_method_index_get_entry() != 50953.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } + if (lib.uniffi_wvb_ffi_checksum_method_cancellation_cancel() != 13086.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_wvb_ffi_checksum_method_cancellation_is_cancelled() != 6967.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } if (lib.uniffi_wvb_ffi_checksum_method_integrity_serialize() != 62469.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } @@ -1337,9 +1376,6 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { if (lib.uniffi_wvb_ffi_checksum_method_integrity_value() != 613.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_integritycheck_check() != 15325.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } if (lib.uniffi_wvb_ffi_checksum_method_bundleprotocolhandler_handle() != 38322.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } @@ -1349,109 +1385,127 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { if (lib.uniffi_wvb_ffi_checksum_method_proxyresolver_resolve() != 3972.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_remote_download() != 21588.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_remote_download() != 26520.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_remote_download_version() != 8652.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_remote_get_update() != 30874.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_remote_get_info() != 26038.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_remoteondownload_on_download() != 24193.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_remote_list_bundles() != 46632.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_signaturecustomverify_verify() != 33505.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_remoteondownload_on_download() != 62542.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_loadeddescriptor_descriptor() != 52553.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_signatureverify_verify() != 61012.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_loadeddescriptor_get_data() != 42308.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_fetch_builtin_bundle() != 46850.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_loadeddescriptor_get_data_checksum() != 3423.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_fetch_bundle() != 3805.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_fetch_builtin_bundle() != 17435.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_fetch_descriptor() != 51584.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_fetch_bundle() != 44562.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_fetch_remote_bundle() != 65226.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_fetch_descriptor() != 51197.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_get_builtin_bundle_filepath() != 24323.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_fetch_remote_bundle() != 43833.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_get_remote_bundle_filepath() != 7311.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_get_builtin_bundle_filepath() != 33416.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_list_bundles() != 57264.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_get_builtin_version_data() != 36382.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_load_builtin_metadata() != 45714.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_get_remote_bundle_filepath() != 26863.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_load_descriptor() != 38145.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_get_remote_previous_version() != 56770.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_load_remote_metadata() != 53479.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_get_remote_staged_version() != 11049.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_load_version() != 12691.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_get_remote_version_data() != 64245.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_prune_remote_bundles() != 10982.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_get_version() != 13477.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_remote_retained_versions() != 40540.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_list_builtin_bundles() != 23719.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_remove_remote_bundle() != 28722.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_list_bundles() != 52967.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_resolve_filepath() != 59141.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_list_remote_bundles() != 47012.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_unload_descriptor() != 36116.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_load() != 63609.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_update_version() != 26121.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_prune_remote_bundle() != 21250.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_bundlesource_write_remote_bundle() != 31819.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_prune_remote_bundles() != 11241.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_loadeddescriptor_descriptor() != 52553.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_remove_remote_bundle() != 9597.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_loadeddescriptor_get_data() != 42308.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_remove_remote_bundles() != 53613.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_loadeddescriptor_get_data_checksum() != 3423.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_resolve_filepath() != 34868.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_wvb_ffi_checksum_method_source_stage_remote_bundle() != 34711.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_wvb_ffi_checksum_method_source_stage_remote_bundles() != 6972.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_wvb_ffi_checksum_method_source_unload() != 39693.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_updater_download_update() != 48197.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_update_remote_version() != 8467.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_updater_get_update() != 7181.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_source_update_remote_versions() != 46886.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_updater_install() != 17169.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_updater_download() != 27702.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_method_updater_list_remotes() != 43567.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_method_updater_get_update() != 56888.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_wvb_ffi_checksum_method_updater_install() != 12072.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_wvb_ffi_checksum_method_updater_rollback() != 19709.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_wvb_ffi_checksum_constructor_bundlebuilder_new() != 7081.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_constructor_integrity_compute() != 53699.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_constructor_cancellation_new() != 18611.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_constructor_integrity_parse() != 58850.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_constructor_integrity_compute() != 55661.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_constructor_bundleprotocolhandler_new() != 65404.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_constructor_integrity_parse() != 59952.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_wvb_ffi_checksum_constructor_bundleprotocolhandler_new() != 49457.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_wvb_ffi_checksum_constructor_proxyprotocolhandler_custom() != 21782.toShort()) { @@ -1460,13 +1514,13 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { if (lib.uniffi_wvb_ffi_checksum_constructor_proxyprotocolhandler_new() != 17067.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_constructor_remote_new() != 63643.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_constructor_remote_new() != 1084.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_constructor_bundlesource_new() != 38494.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_constructor_source_new() != 4331.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_wvb_ffi_checksum_constructor_updater_new() != 63636.toShort()) { + if (lib.uniffi_wvb_ffi_checksum_constructor_updater_new() != 4993.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } } @@ -1760,32 +1814,55 @@ private class UniffiJnaCleanable( // using Android or not. // There are further runtime checks to chose the correct implementation // of the cleaner. + + private fun UniffiCleaner.Companion.create(): UniffiCleaner = - try { - // For safety's sake: if the library hasn't been run in android_cleaner = true - // mode, but is being run on Android, then we still need to think about - // Android API versions. - // So we check if java.lang.ref.Cleaner is there, and use that… - java.lang.Class.forName("java.lang.ref.Cleaner") - JavaLangRefCleaner() - } catch (e: ClassNotFoundException) { - // … otherwise, fallback to the JNA cleaner. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + AndroidSystemCleaner() + } else { UniffiJnaCleaner() } -private class JavaLangRefCleaner : UniffiCleaner { - val cleaner = java.lang.ref.Cleaner.create() +// The SystemCleaner, available from API Level 33. +// Some API Level 33 OSes do not support using it, so we require API Level 34. +@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) +private class AndroidSystemCleaner : UniffiCleaner { + val cleaner = android.system.SystemCleaner.cleaner() override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable = - JavaLangRefCleanable(cleaner.register(value, cleanUpTask)) + AndroidSystemCleanable(cleaner.register(value, cleanUpTask)) } -private class JavaLangRefCleanable( - val cleanable: java.lang.ref.Cleaner.Cleanable +@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) +private class AndroidSystemCleanable( + private val cleanable: java.lang.ref.Cleaner.Cleanable, ) : UniffiCleaner.Cleanable { override fun clean() = cleanable.clean() } +/** + * @suppress + */ +public object FfiConverterUByte: FfiConverter { + override fun lift(value: Byte): UByte { + return value.toUByte() + } + + override fun read(buf: ByteBuffer): UByte { + return lift(buf.get()) + } + + override fun lower(value: UByte): Byte { + return value.toByte() + } + + override fun allocationSize(value: UByte) = 1UL + + override fun write(value: UByte, buf: ByteBuffer) { + buf.put(value.toByte()) + } +} + /** * @suppress */ @@ -3175,7 +3252,7 @@ public object FfiConverterTypeBundleDescriptor: FfiConverter UniffiLib.uniffi_wvb_ffi_fn_constructor_bundleprotocolhandler_new( - FfiConverterTypeBundleSource.lower(`source`),FfiConverterOptionalTypeBundleProtocolOptions.lower(`options`),_status) + FfiConverterTypeSource.lower(`source`),FfiConverterOptionalTypeBundleProtocolOptions.lower(`options`),_status) } ) @@ -3464,114 +3541,16 @@ public object FfiConverterTypeBundleProtocolHandler: FfiConverter - - /** - * Loads the manifest metadata for the builtin bundle `bundle_name` at `version`, - * or `None` if no such entry exists. - */ - suspend fun `loadBuiltinMetadata`(`bundleName`: kotlin.String, `version`: kotlin.String): BundleManifestMetadata? - - /** - * Loads (and caches) the descriptor for the current version of `bundle_name`. - * Concurrent calls for the same bundle share a single load (single-flight) and - * return the cached descriptor until the active version changes or - * [`unload_descriptor`](BundleSource::unload_descriptor) is called. - */ - suspend fun `loadDescriptor`(`bundleName`: kotlin.String): LoadedDescriptor - - /** - * Loads the manifest metadata for the remote bundle `bundle_name` at `version`, - * or `None` if no such entry exists. - */ - suspend fun `loadRemoteMetadata`(`bundleName`: kotlin.String, `version`: kotlin.String): BundleManifestMetadata? - - suspend fun `loadVersion`(`bundleName`: kotlin.String): BundleSourceVersion? - - /** - * Removes every staged remote version except the retained set (current and - * previous). Returns the versions that were removed. - */ - suspend fun `pruneRemoteBundles`(`bundleName`: kotlin.String): List - - /** - * Returns the remote versions that pruning retains (the current and previous). - */ - suspend fun `remoteRetainedVersions`(`bundleName`: kotlin.String): List - - /** - * Removes a single staged remote bundle version: drops its manifest entry and - * deletes its file from disk. Returns `true` if the entry existed. - */ - suspend fun `removeRemoteBundle`(`bundleName`: kotlin.String, `version`: kotlin.String): kotlin.Boolean - - suspend fun `resolveFilepath`(`bundleName`: kotlin.String): kotlin.String - - /** - * Drops the cached descriptor for `bundle_name`, if present. Already-returned - * [`LoadedDescriptor`] handles keep working; the next [`load_descriptor`] - * reloads from disk. Returns `true` if a cached descriptor was removed. - */ - fun `unloadDescriptor`(`bundleName`: kotlin.String): kotlin.Boolean +public interface CancellationInterface { - suspend fun `updateVersion`(`bundleName`: kotlin.String, `version`: kotlin.String) + fun `cancel`() - suspend fun `writeRemoteBundle`(`bundleName`: kotlin.String, `version`: kotlin.String, `bundle`: Bundle, `metadata`: BundleManifestMetadata) + fun `isCancelled`(): kotlin.Boolean companion object } -/** - * Unified access point for bundles from both the builtin and remote sources. - * - * The remote source takes precedence over the builtin source when both contain - * a bundle with the same name. - */ -open class BundleSource: Disposable, AutoCloseable, BundleSourceInterface +open class Cancellation: Disposable, AutoCloseable, CancellationInterface { @Suppress("UNUSED_PARAMETER") @@ -3595,12 +3574,12 @@ open class BundleSource: Disposable, AutoCloseable, BundleSourceInterface this.handle = 0 this.cleanable = null } - constructor(`config`: BundleSourceConfig) : + constructor() : this(UniffiWithHandle, - uniffiRustCallWithError(WebviewBundleException) { _status -> - UniffiLib.uniffi_wvb_ffi_fn_constructor_bundlesource_new( + uniffiRustCall() { _status -> + UniffiLib.uniffi_wvb_ffi_fn_constructor_cancellation_new( - FfiConverterTypeBundleSourceConfig.lower(`config`),_status) + _status) } ) @@ -3658,7 +3637,7 @@ open class BundleSource: Disposable, AutoCloseable, BundleSourceInterface return; } uniffiRustCall { status -> - UniffiLib.uniffi_wvb_ffi_fn_free_bundlesource(handle, status) + UniffiLib.uniffi_wvb_ffi_fn_free_cancellation(handle, status) } } } @@ -3671,372 +3650,317 @@ open class BundleSource: Disposable, AutoCloseable, BundleSourceInterface throw InternalException("uniffiCloneHandle() called on NoHandle object"); } return uniffiRustCall() { status -> - UniffiLib.uniffi_wvb_ffi_fn_clone_bundlesource(handle, status) + UniffiLib.uniffi_wvb_ffi_fn_clone_cancellation(handle, status) } } + override fun `cancel`() + = + callWithHandle { + uniffiRustCall() { _status -> + UniffiLib.uniffi_wvb_ffi_fn_method_cancellation_cancel( + it, + _status) +} + } - /** - * Loads the full builtin bundle for `bundle_name` at `version`, bypassing the - * remote source and version resolution. - */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `fetchBuiltinBundle`(`bundleName`: kotlin.String, `version`: kotlin.String) : Bundle { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_fetch_builtin_bundle( - uniffiHandle, - FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_u64(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_u64(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_u64(future) }, - // lift function - { FfiConverterTypeBundle.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, + + + override fun `isCancelled`(): kotlin.Boolean { + return FfiConverterBoolean.lift( + callWithHandle { + uniffiRustCall() { _status -> + UniffiLib.uniffi_wvb_ffi_fn_method_cancellation_is_cancelled( + it, + _status) +} + } ) } + - /** - * Loads the full bundle (header + index + data) for `bundle_name`. - */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `fetchBundle`(`bundleName`: kotlin.String) : Bundle { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_fetch_bundle( - uniffiHandle, - FfiConverterString.lower(`bundleName`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_u64(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_u64(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_u64(future) }, - // lift function - { FfiConverterTypeBundle.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) - } + + + + /** - * Loads only the header and index for `bundle_name`, skipping the data section. - * The returned descriptor does not support [`BundleDescriptor::index`]; use - * [`fetch`](BundleSource::fetch_bundle) when entry data is needed. + * @suppress */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `fetchDescriptor`(`bundleName`: kotlin.String) : BundleDescriptor { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_fetch_descriptor( - uniffiHandle, - FfiConverterString.lower(`bundleName`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_u64(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_u64(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_u64(future) }, - // lift function - { FfiConverterTypeBundleDescriptor.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) + companion object + +} + + +/** + * @suppress + */ +public object FfiConverterTypeCancellation: FfiConverter { + override fun lower(value: Cancellation): Long { + return value.uniffiCloneHandle() } - - /** - * Loads the full remote bundle for `bundle_name` at `version`, bypassing the - * builtin source and version resolution. - */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `fetchRemoteBundle`(`bundleName`: kotlin.String, `version`: kotlin.String) : Bundle { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_fetch_remote_bundle( - uniffiHandle, - FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_u64(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_u64(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_u64(future) }, - // lift function - { FfiConverterTypeBundle.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) + override fun lift(value: Long): Cancellation { + return Cancellation(UniffiWithHandle, value) + } + + override fun read(buf: ByteBuffer): Cancellation { + return lift(buf.getLong()) + } + + override fun allocationSize(value: Cancellation) = 8UL + + override fun write(value: Cancellation, buf: ByteBuffer) { + buf.putLong(lower(value)) } +} + + +// This template implements a class for working with a Rust struct via a handle +// to the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque handle to the underlying Rust struct. +// Method calls need to read this handle from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its handle should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the handle, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the handle, but is interrupted +// before it can pass the handle over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read handle value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + +/** + * Fixed-size bundle file header containing format metadata. + */ +public interface HeaderInterface { /** - * Resolves the on-disk path of the builtin bundle `bundle_name` at `version`, - * without checking whether the file exists. + * Byte offset at which the index section ends (and data section begins). */ - @Throws(WebviewBundleException::class)override fun `getBuiltinBundleFilepath`(`bundleName`: kotlin.String, `version`: kotlin.String): kotlin.String { - return FfiConverterString.lift( - callWithHandle { - uniffiRustCallWithError(WebviewBundleException) { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_get_builtin_bundle_filepath( - it, - FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`),_status) -} - } - ) - } + fun `indexEndOffset`(): kotlin.ULong - + /** + * Byte length of the serialized index section. + */ + fun `indexSize`(): kotlin.UInt /** - * Resolves the on-disk path of the remote bundle `bundle_name` at `version`, - * without checking whether the file exists. + * Bundle format version encoded in the header. */ - @Throws(WebviewBundleException::class)override fun `getRemoteBundleFilepath`(`bundleName`: kotlin.String, `version`: kotlin.String): kotlin.String { - return FfiConverterString.lift( - callWithHandle { - uniffiRustCallWithError(WebviewBundleException) { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_get_remote_bundle_filepath( - it, - FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`),_status) -} - } - ) - } + fun `version`(): Version + companion object +} - - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `listBundles`() : List { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_list_bundles( - uniffiHandle, - - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterSequenceTypeListBundleItem.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) - } +/** + * Fixed-size bundle file header containing format metadata. + */ +open class Header: Disposable, AutoCloseable, HeaderInterface +{ - + @Suppress("UNUSED_PARAMETER") /** - * Loads the manifest metadata for the builtin bundle `bundle_name` at `version`, - * or `None` if no such entry exists. + * @suppress */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `loadBuiltinMetadata`(`bundleName`: kotlin.String, `version`: kotlin.String) : BundleManifestMetadata? { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_load_builtin_metadata( - uniffiHandle, - FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterOptionalTypeBundleManifestMetadata.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) + constructor(withHandle: UniffiWithHandle, handle: Long) { + this.handle = handle + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(handle)) } - /** - * Loads (and caches) the descriptor for the current version of `bundle_name`. - * Concurrent calls for the same bundle share a single load (single-flight) and - * return the cached descriptor until the active version changes or - * [`unload_descriptor`](BundleSource::unload_descriptor) is called. + * @suppress + * + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `loadDescriptor`(`bundleName`: kotlin.String) : LoadedDescriptor { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_load_descriptor( - uniffiHandle, - FfiConverterString.lower(`bundleName`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_u64(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_u64(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_u64(future) }, - // lift function - { FfiConverterTypeLoadedDescriptor.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) + @Suppress("UNUSED_PARAMETER") + constructor(noHandle: NoHandle) { + this.handle = 0 + this.cleanable = null } - - /** - * Loads the manifest metadata for the remote bundle `bundle_name` at `version`, - * or `None` if no such entry exists. - */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `loadRemoteMetadata`(`bundleName`: kotlin.String, `version`: kotlin.String) : BundleManifestMetadata? { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_load_remote_metadata( - uniffiHandle, - FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterOptionalTypeBundleManifestMetadata.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) + protected val handle: Long + protected val cleanable: UniffiCleaner.Cleanable? + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable?.clean() + } + } } - - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `loadVersion`(`bundleName`: kotlin.String) : BundleSourceVersion? { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_load_version( - uniffiHandle, - FfiConverterString.lower(`bundleName`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterOptionalTypeBundleSourceVersion.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) + @Synchronized + override fun close() { + this.destroy() } - - /** - * Removes every staged remote version except the retained set (current and - * previous). Returns the versions that were removed. - */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `pruneRemoteBundles`(`bundleName`: kotlin.String) : List { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_prune_remote_bundles( - uniffiHandle, - FfiConverterString.lower(`bundleName`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterSequenceString.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) + internal inline fun callWithHandle(block: (handle: Long) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the handle being freed concurrently. + try { + return block(this.uniffiCloneHandle()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable?.clean() + } + } } - - /** - * Returns the remote versions that pruning retains (the current and previous). - */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `remoteRetainedVersions`(`bundleName`: kotlin.String) : List { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_remote_retained_versions( - uniffiHandle, - FfiConverterString.lower(`bundleName`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterSequenceString.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val handle: Long) : Runnable { + override fun run() { + if (handle == 0.toLong()) { + // Fake object created with `NoHandle`, don't try to free. + return; + } + uniffiRustCall { status -> + UniffiLib.uniffi_wvb_ffi_fn_free_header(handle, status) + } + } } - /** - * Removes a single staged remote bundle version: drops its manifest entry and - * deletes its file from disk. Returns `true` if the entry existed. + * @suppress */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `removeRemoteBundle`(`bundleName`: kotlin.String, `version`: kotlin.String) : kotlin.Boolean { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_remove_remote_bundle( - uniffiHandle, - FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_i8(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_i8(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_i8(future) }, - // lift function - { FfiConverterBoolean.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) + fun uniffiCloneHandle(): Long { + if (handle == 0.toLong()) { + throw InternalException("uniffiCloneHandle() called on NoHandle object"); + } + return uniffiRustCall() { status -> + UniffiLib.uniffi_wvb_ffi_fn_clone_header(handle, status) + } } - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `resolveFilepath`(`bundleName`: kotlin.String) : kotlin.String { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_resolve_filepath( - uniffiHandle, - FfiConverterString.lower(`bundleName`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterString.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, + /** + * Byte offset at which the index section ends (and data section begins). + */override fun `indexEndOffset`(): kotlin.ULong { + return FfiConverterULong.lift( + callWithHandle { + uniffiRustCall() { _status -> + UniffiLib.uniffi_wvb_ffi_fn_method_header_index_end_offset( + it, + _status) +} + } ) } + /** - * Drops the cached descriptor for `bundle_name`, if present. Already-returned - * [`LoadedDescriptor`] handles keep working; the next [`load_descriptor`] - * reloads from disk. Returns `true` if a cached descriptor was removed. - */override fun `unloadDescriptor`(`bundleName`: kotlin.String): kotlin.Boolean { - return FfiConverterBoolean.lift( + * Byte length of the serialized index section. + */override fun `indexSize`(): kotlin.UInt { + return FfiConverterUInt.lift( callWithHandle { uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_unload_descriptor( + UniffiLib.uniffi_wvb_ffi_fn_method_header_index_size( it, - FfiConverterString.lower(`bundleName`),_status) + _status) } } ) @@ -4044,48 +3968,20 @@ open class BundleSource: Disposable, AutoCloseable, BundleSourceInterface - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `updateVersion`(`bundleName`: kotlin.String, `version`: kotlin.String) { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_update_version( - uniffiHandle, - FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) + /** + * Bundle format version encoded in the header. + */override fun `version`(): Version { + return FfiConverterTypeVersion.lift( + callWithHandle { + uniffiRustCall() { _status -> + UniffiLib.uniffi_wvb_ffi_fn_method_header_version( + it, + _status) +} } - - - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `writeRemoteBundle`(`bundleName`: kotlin.String, `version`: kotlin.String, `bundle`: Bundle, `metadata`: BundleManifestMetadata) { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_bundlesource_write_remote_bundle( - uniffiHandle, - FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`),FfiConverterTypeBundle.lower(`bundle`),FfiConverterTypeBundleManifestMetadata.lower(`metadata`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - - // Error FFI converter - WebviewBundleException.ErrorHandler, ) } + @@ -4105,22 +4001,22 @@ open class BundleSource: Disposable, AutoCloseable, BundleSourceInterface /** * @suppress */ -public object FfiConverterTypeBundleSource: FfiConverter { - override fun lower(value: BundleSource): Long { +public object FfiConverterTypeHeader: FfiConverter { + override fun lower(value: Header): Long { return value.uniffiCloneHandle() } - override fun lift(value: Long): BundleSource { - return BundleSource(UniffiWithHandle, value) + override fun lift(value: Long): Header { + return Header(UniffiWithHandle, value) } - override fun read(buf: ByteBuffer): BundleSource { + override fun read(buf: ByteBuffer): Header { return lift(buf.getLong()) } - override fun allocationSize(value: BundleSource) = 8UL + override fun allocationSize(value: Header) = 8UL - override fun write(value: BundleSource, buf: ByteBuffer) { + override fun write(value: Header, buf: ByteBuffer) { buf.putLong(lower(value)) } } @@ -4222,32 +4118,32 @@ public object FfiConverterTypeBundleSource: FfiConverter { /** - * Fixed-size bundle file header containing format metadata. + * View into the index section of a bundle. + * + * Holds the index metadata itself, so it is available on a metadata-only descriptor + * (from `BundleSource::fetch_descriptor`) as well as on a fully loaded [`Bundle`]. */ -public interface HeaderInterface { +public interface IndexInterface { - /** - * Byte offset at which the index section ends (and data section begins). - */ - fun `indexEndOffset`(): kotlin.ULong + fun `containsPath`(`path`: kotlin.String): kotlin.Boolean /** - * Byte length of the serialized index section. + * Returns all index entries keyed by their path (e.g. `"/index.html"`). */ - fun `indexSize`(): kotlin.UInt + fun `entries`(): Map - /** - * Bundle format version encoded in the header. - */ - fun `version`(): Version + fun `getEntry`(`path`: kotlin.String): IndexEntry? companion object } /** - * Fixed-size bundle file header containing format metadata. + * View into the index section of a bundle. + * + * Holds the index metadata itself, so it is available on a metadata-only descriptor + * (from `BundleSource::fetch_descriptor`) as well as on a fully loaded [`Bundle`]. */ -open class Header: Disposable, AutoCloseable, HeaderInterface +open class Index: Disposable, AutoCloseable, IndexInterface { @Suppress("UNUSED_PARAMETER") @@ -4326,7 +4222,7 @@ open class Header: Disposable, AutoCloseable, HeaderInterface return; } uniffiRustCall { status -> - UniffiLib.uniffi_wvb_ffi_fn_free_header(handle, status) + UniffiLib.uniffi_wvb_ffi_fn_free_index(handle, status) } } } @@ -4339,20 +4235,17 @@ open class Header: Disposable, AutoCloseable, HeaderInterface throw InternalException("uniffiCloneHandle() called on NoHandle object"); } return uniffiRustCall() { status -> - UniffiLib.uniffi_wvb_ffi_fn_clone_header(handle, status) + UniffiLib.uniffi_wvb_ffi_fn_clone_index(handle, status) } } - - /** - * Byte offset at which the index section ends (and data section begins). - */override fun `indexEndOffset`(): kotlin.ULong { - return FfiConverterULong.lift( + override fun `containsPath`(`path`: kotlin.String): kotlin.Boolean { + return FfiConverterBoolean.lift( callWithHandle { uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_header_index_end_offset( + UniffiLib.uniffi_wvb_ffi_fn_method_index_contains_path( it, - _status) + FfiConverterString.lower(`path`),_status) } } ) @@ -4361,12 +4254,12 @@ open class Header: Disposable, AutoCloseable, HeaderInterface /** - * Byte length of the serialized index section. - */override fun `indexSize`(): kotlin.UInt { - return FfiConverterUInt.lift( + * Returns all index entries keyed by their path (e.g. `"/index.html"`). + */override fun `entries`(): Map { + return FfiConverterMapStringTypeIndexEntry.lift( callWithHandle { uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_header_index_size( + UniffiLib.uniffi_wvb_ffi_fn_method_index_entries( it, _status) } @@ -4375,16 +4268,13 @@ open class Header: Disposable, AutoCloseable, HeaderInterface } - - /** - * Bundle format version encoded in the header. - */override fun `version`(): Version { - return FfiConverterTypeVersion.lift( + override fun `getEntry`(`path`: kotlin.String): IndexEntry? { + return FfiConverterOptionalTypeIndexEntry.lift( callWithHandle { uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_header_version( + UniffiLib.uniffi_wvb_ffi_fn_method_index_get_entry( it, - _status) + FfiConverterString.lower(`path`),_status) } } ) @@ -4409,22 +4299,22 @@ open class Header: Disposable, AutoCloseable, HeaderInterface /** * @suppress */ -public object FfiConverterTypeHeader: FfiConverter { - override fun lower(value: Header): Long { +public object FfiConverterTypeIndex: FfiConverter { + override fun lower(value: Index): Long { return value.uniffiCloneHandle() } - override fun lift(value: Long): Header { - return Header(UniffiWithHandle, value) + override fun lift(value: Long): Index { + return Index(UniffiWithHandle, value) } - override fun read(buf: ByteBuffer): Header { + override fun read(buf: ByteBuffer): Index { return lift(buf.getLong()) } - override fun allocationSize(value: Header) = 8UL + override fun allocationSize(value: Index) = 8UL - override fun write(value: Header, buf: ByteBuffer) { + override fun write(value: Index, buf: ByteBuffer) { buf.putLong(lower(value)) } } @@ -4526,32 +4416,32 @@ public object FfiConverterTypeHeader: FfiConverter { /** - * View into the index section of a bundle. - * - * Holds the index metadata itself, so it is available on a metadata-only descriptor - * (from `BundleSource::fetch_descriptor`) as well as on a fully loaded [`Bundle`]. + * A digest over some bytes, serialized as `:` (e.g. `"sha256:n4bQ..."`). */ -public interface IndexInterface { +public interface IntegrityInterface { - fun `containsPath`(`path`: kotlin.String): kotlin.Boolean + /** + * Serializes to `:`. + */ + fun `serialize`(): kotlin.String /** - * Returns all index entries keyed by their path (e.g. `"/index.html"`). + * Whether `data` digests to this integrity. */ - fun `entries`(): Map + fun `validate`(`data`: kotlin.ByteArray): kotlin.Boolean - fun `getEntry`(`path`: kotlin.String): IndexEntry? + /** + * The raw digest bytes. + */ + fun `value`(): kotlin.ByteArray companion object } /** - * View into the index section of a bundle. - * - * Holds the index metadata itself, so it is available on a metadata-only descriptor - * (from `BundleSource::fetch_descriptor`) as well as on a fully loaded [`Bundle`]. + * A digest over some bytes, serialized as `:` (e.g. `"sha256:n4bQ..."`). */ -open class Index: Disposable, AutoCloseable, IndexInterface +open class Integrity: Disposable, AutoCloseable, IntegrityInterface { @Suppress("UNUSED_PARAMETER") @@ -4630,7 +4520,7 @@ open class Index: Disposable, AutoCloseable, IndexInterface return; } uniffiRustCall { status -> - UniffiLib.uniffi_wvb_ffi_fn_free_index(handle, status) + UniffiLib.uniffi_wvb_ffi_fn_free_integrity(handle, status) } } } @@ -4643,17 +4533,20 @@ open class Index: Disposable, AutoCloseable, IndexInterface throw InternalException("uniffiCloneHandle() called on NoHandle object"); } return uniffiRustCall() { status -> - UniffiLib.uniffi_wvb_ffi_fn_clone_index(handle, status) + UniffiLib.uniffi_wvb_ffi_fn_clone_integrity(handle, status) } } - override fun `containsPath`(`path`: kotlin.String): kotlin.Boolean { - return FfiConverterBoolean.lift( + + /** + * Serializes to `:`. + */override fun `serialize`(): kotlin.String { + return FfiConverterString.lift( callWithHandle { uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_index_contains_path( + UniffiLib.uniffi_wvb_ffi_fn_method_integrity_serialize( it, - FfiConverterString.lower(`path`),_status) + _status) } } ) @@ -4662,27 +4555,30 @@ open class Index: Disposable, AutoCloseable, IndexInterface /** - * Returns all index entries keyed by their path (e.g. `"/index.html"`). - */override fun `entries`(): Map { - return FfiConverterMapStringTypeIndexEntry.lift( + * Whether `data` digests to this integrity. + */override fun `validate`(`data`: kotlin.ByteArray): kotlin.Boolean { + return FfiConverterBoolean.lift( callWithHandle { uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_index_entries( + UniffiLib.uniffi_wvb_ffi_fn_method_integrity_validate( it, - _status) + FfiConverterByteArray.lower(`data`),_status) } } ) } - override fun `getEntry`(`path`: kotlin.String): IndexEntry? { - return FfiConverterOptionalTypeIndexEntry.lift( + + /** + * The raw digest bytes. + */override fun `value`(): kotlin.ByteArray { + return FfiConverterByteArray.lift( callWithHandle { uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_index_get_entry( + UniffiLib.uniffi_wvb_ffi_fn_method_integrity_value( it, - FfiConverterString.lower(`path`),_status) + _status) } } ) @@ -4695,11 +4591,32 @@ open class Index: Disposable, AutoCloseable, IndexInterface + companion object { + fun `compute`(`algorithm`: IntegrityAlgorithm, `data`: kotlin.ByteArray): Integrity { + return FfiConverterTypeIntegrity.lift( + uniffiRustCall() { _status -> + UniffiLib.uniffi_wvb_ffi_fn_constructor_integrity_compute( - /** - * @suppress - */ - companion object + FfiConverterTypeIntegrityAlgorithm.lower(`algorithm`),FfiConverterByteArray.lower(`data`),_status) +} + ) + } + + + + @Throws(WebviewBundleException::class) fun `parse`(`integrity`: kotlin.String): Integrity { + return FfiConverterTypeIntegrity.lift( + uniffiRustCallWithError(WebviewBundleException) { _status -> + UniffiLib.uniffi_wvb_ffi_fn_constructor_integrity_parse( + + FfiConverterString.lower(`integrity`),_status) +} + ) + } + + + + } } @@ -4707,22 +4624,22 @@ open class Index: Disposable, AutoCloseable, IndexInterface /** * @suppress */ -public object FfiConverterTypeIndex: FfiConverter { - override fun lower(value: Index): Long { +public object FfiConverterTypeIntegrity: FfiConverter { + override fun lower(value: Integrity): Long { return value.uniffiCloneHandle() } - override fun lift(value: Long): Index { - return Index(UniffiWithHandle, value) + override fun lift(value: Long): Integrity { + return Integrity(UniffiWithHandle, value) } - override fun read(buf: ByteBuffer): Index { + override fun read(buf: ByteBuffer): Integrity { return lift(buf.getLong()) } - override fun allocationSize(value: Index) = 8UL + override fun allocationSize(value: Integrity) = 8UL - override fun write(value: Index, buf: ByteBuffer) { + override fun write(value: Integrity, buf: ByteBuffer) { buf.putLong(lower(value)) } } @@ -4824,32 +4741,54 @@ public object FfiConverterTypeIndex: FfiConverter { /** - * A digest over some bytes, serialized as `:` (e.g. `"sha256:n4bQ..."`). + * A descriptor loaded (and cached) by a [`Source`]. + * + * Holds the parsed header/index together with the filepath it was loaded from, so + * reading entry data always targets the exact bundle version that produced this + * descriptor — even if the source's active version is swapped concurrently. Entry + * data is read lazily from disk via [`LoadedDescriptor::get_data`], avoiding loading + * the whole bundle into memory. */ -public interface IntegrityInterface { +public interface LoadedDescriptorInterface { /** - * Serializes to `:`. + * Returns the bundle descriptor (header + index metadata). + * + * The returned descriptor carries no reference back to the source, so it can + * outlive this `LoadedDescriptor`. It holds only metadata, so its `index()` is + * unsupported; use [`get_data`](LoadedDescriptor::get_data) for entry data. */ - fun `serialize`(): kotlin.String + fun `descriptor`(): BundleDescriptor /** - * Whether `data` digests to this integrity. + * Reads the bytes for `path`, loading them lazily from disk. + * + * The read targets the bundle file this descriptor was loaded from, so the data + * stays consistent with [`descriptor`](LoadedDescriptor::descriptor) even if the + * source's active version changes meanwhile. Returns `None` if `path` does not + * exist in the bundle. */ - fun `validate`(`data`: kotlin.ByteArray): kotlin.Boolean + suspend fun `getData`(`path`: kotlin.String): kotlin.ByteArray? /** - * The raw digest bytes. + * Reads the xxHash-32 checksum for `path`, loading it lazily from disk. + * Returns `None` if `path` does not exist in the bundle. */ - fun `value`(): kotlin.ByteArray + suspend fun `getDataChecksum`(`path`: kotlin.String): kotlin.UInt? companion object } /** - * A digest over some bytes, serialized as `:` (e.g. `"sha256:n4bQ..."`). + * A descriptor loaded (and cached) by a [`Source`]. + * + * Holds the parsed header/index together with the filepath it was loaded from, so + * reading entry data always targets the exact bundle version that produced this + * descriptor — even if the source's active version is swapped concurrently. Entry + * data is read lazily from disk via [`LoadedDescriptor::get_data`], avoiding loading + * the whole bundle into memory. */ -open class Integrity: Disposable, AutoCloseable, IntegrityInterface +open class LoadedDescriptor: Disposable, AutoCloseable, LoadedDescriptorInterface { @Suppress("UNUSED_PARAMETER") @@ -4928,7 +4867,7 @@ open class Integrity: Disposable, AutoCloseable, IntegrityInterface return; } uniffiRustCall { status -> - UniffiLib.uniffi_wvb_ffi_fn_free_integrity(handle, status) + UniffiLib.uniffi_wvb_ffi_fn_free_loadeddescriptor(handle, status) } } } @@ -4941,18 +4880,22 @@ open class Integrity: Disposable, AutoCloseable, IntegrityInterface throw InternalException("uniffiCloneHandle() called on NoHandle object"); } return uniffiRustCall() { status -> - UniffiLib.uniffi_wvb_ffi_fn_clone_integrity(handle, status) + UniffiLib.uniffi_wvb_ffi_fn_clone_loadeddescriptor(handle, status) } } /** - * Serializes to `:`. - */override fun `serialize`(): kotlin.String { - return FfiConverterString.lift( + * Returns the bundle descriptor (header + index metadata). + * + * The returned descriptor carries no reference back to the source, so it can + * outlive this `LoadedDescriptor`. It holds only metadata, so its `index()` is + * unsupported; use [`get_data`](LoadedDescriptor::get_data) for entry data. + */override fun `descriptor`(): BundleDescriptor { + return FfiConverterTypeBundleDescriptor.lift( callWithHandle { uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_integrity_serialize( + UniffiLib.uniffi_wvb_ffi_fn_method_loadeddescriptor_descriptor( it, _status) } @@ -4963,74 +4906,69 @@ open class Integrity: Disposable, AutoCloseable, IntegrityInterface /** - * Whether `data` digests to this integrity. - */override fun `validate`(`data`: kotlin.ByteArray): kotlin.Boolean { - return FfiConverterBoolean.lift( - callWithHandle { - uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_integrity_validate( - it, - FfiConverterByteArray.lower(`data`),_status) -} - } - ) - } - - - - /** - * The raw digest bytes. - */override fun `value`(): kotlin.ByteArray { - return FfiConverterByteArray.lift( - callWithHandle { - uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_integrity_value( - it, - _status) -} - } - ) - } - - - - - - - - - companion object { - - /** - * Computes the integrity of `data` with `algorithm`. - */ fun `compute`(`algorithm`: IntegrityAlgorithm, `data`: kotlin.ByteArray): Integrity { - return FfiConverterTypeIntegrity.lift( - uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_constructor_integrity_compute( - - FfiConverterTypeIntegrityAlgorithm.lower(`algorithm`),FfiConverterByteArray.lower(`data`),_status) -} + * Reads the bytes for `path`, loading them lazily from disk. + * + * The read targets the bundle file this descriptor was loaded from, so the data + * stays consistent with [`descriptor`](LoadedDescriptor::descriptor) even if the + * source's active version changes meanwhile. Returns `None` if `path` does not + * exist in the bundle. + */ + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `getData`(`path`: kotlin.String) : kotlin.ByteArray? { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_loadeddescriptor_get_data( + uniffiHandle, + FfiConverterString.lower(`path`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterOptionalByteArray.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, ) } - - + /** - * Parses a serialized integrity string (e.g. `"sha256:n4bQ..."`). + * Reads the xxHash-32 checksum for `path`, loading it lazily from disk. + * Returns `None` if `path` does not exist in the bundle. */ - @Throws(WebviewBundleException::class) fun `parse`(`integrity`: kotlin.String): Integrity { - return FfiConverterTypeIntegrity.lift( - uniffiRustCallWithError(WebviewBundleException) { _status -> - UniffiLib.uniffi_wvb_ffi_fn_constructor_integrity_parse( - - FfiConverterString.lower(`integrity`),_status) -} + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `getDataChecksum`(`path`: kotlin.String) : kotlin.UInt? { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_loadeddescriptor_get_data_checksum( + uniffiHandle, + FfiConverterString.lower(`path`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterOptionalUInt.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, ) } + - - } + + + + + + /** + * @suppress + */ + companion object } @@ -5038,22 +4976,22 @@ open class Integrity: Disposable, AutoCloseable, IntegrityInterface /** * @suppress */ -public object FfiConverterTypeIntegrity: FfiConverter { - override fun lower(value: Integrity): Long { +public object FfiConverterTypeLoadedDescriptor: FfiConverter { + override fun lower(value: LoadedDescriptor): Long { return value.uniffiCloneHandle() } - override fun lift(value: Long): Integrity { - return Integrity(UniffiWithHandle, value) + override fun lift(value: Long): LoadedDescriptor { + return LoadedDescriptor(UniffiWithHandle, value) } - override fun read(buf: ByteBuffer): Integrity { + override fun read(buf: ByteBuffer): LoadedDescriptor { return lift(buf.getLong()) } - override fun allocationSize(value: Integrity) = 8UL + override fun allocationSize(value: LoadedDescriptor) = 8UL - override fun write(value: Integrity, buf: ByteBuffer) { + override fun write(value: LoadedDescriptor, buf: ByteBuffer) { buf.putLong(lower(value)) } } @@ -5155,19 +5093,30 @@ public object FfiConverterTypeIntegrity: FfiConverter { /** - * A custom checker that validates a bundle's bytes against its integrity string. + * Proxies HTTP-like requests to another HTTP server (typically a local dev server). + * + * The target is resolved per request — either from a static host mapping + * (e.g. `{"myapp": "http://localhost:8080"}`, keyed by uri host) or by a custom + * [`ProxyResolver`]. A request whose target cannot be resolved fails with an error. */ -public interface IntegrityCheck { +public interface ProxyProtocolHandlerInterface { - suspend fun `check`(`data`: kotlin.ByteArray, `integrity`: kotlin.String): kotlin.Boolean + /** + * Forwards the request — including `body`, for POST/PUT/PATCH — to the resolved target. + */ + suspend fun `handle`(`method`: HttpMethod, `uri`: kotlin.String, `headers`: Map? = null, `body`: kotlin.ByteArray? = null): HttpResponse companion object } /** - * A custom checker that validates a bundle's bytes against its integrity string. + * Proxies HTTP-like requests to another HTTP server (typically a local dev server). + * + * The target is resolved per request — either from a static host mapping + * (e.g. `{"myapp": "http://localhost:8080"}`, keyed by uri host) or by a custom + * [`ProxyResolver`]. A request whose target cannot be resolved fails with an error. */ -open class IntegrityCheckImpl: Disposable, AutoCloseable, IntegrityCheck +open class ProxyProtocolHandler: Disposable, AutoCloseable, ProxyProtocolHandlerInterface { @Suppress("UNUSED_PARAMETER") @@ -5191,6 +5140,17 @@ open class IntegrityCheckImpl: Disposable, AutoCloseable, IntegrityCheck this.handle = 0 this.cleanable = null } + /** + * Proxy by a static host mapping, keyed by the uri host. + */ + constructor(`hosts`: Map) : + this(UniffiWithHandle, + uniffiRustCall() { _status -> + UniffiLib.uniffi_wvb_ffi_fn_constructor_proxyprotocolhandler_new( + + FfiConverterMapStringString.lower(`hosts`),_status) +} + ) protected val handle: Long protected val cleanable: UniffiCleaner.Cleanable? @@ -5246,7 +5206,7 @@ open class IntegrityCheckImpl: Disposable, AutoCloseable, IntegrityCheck return; } uniffiRustCall { status -> - UniffiLib.uniffi_wvb_ffi_fn_free_integritycheck(handle, status) + UniffiLib.uniffi_wvb_ffi_fn_free_proxyprotocolhandler(handle, status) } } } @@ -5259,27 +5219,31 @@ open class IntegrityCheckImpl: Disposable, AutoCloseable, IntegrityCheck throw InternalException("uniffiCloneHandle() called on NoHandle object"); } return uniffiRustCall() { status -> - UniffiLib.uniffi_wvb_ffi_fn_clone_integritycheck(handle, status) + UniffiLib.uniffi_wvb_ffi_fn_clone_proxyprotocolhandler(handle, status) } } + /** + * Forwards the request — including `body`, for POST/PUT/PATCH — to the resolved target. + */ + @Throws(WebviewBundleException::class) @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `check`(`data`: kotlin.ByteArray, `integrity`: kotlin.String) : kotlin.Boolean { + override suspend fun `handle`(`method`: HttpMethod, `uri`: kotlin.String, `headers`: Map?, `body`: kotlin.ByteArray?) : HttpResponse { return uniffiRustCallAsync( callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_integritycheck_check( + UniffiLib.uniffi_wvb_ffi_fn_method_proxyprotocolhandler_handle( uniffiHandle, - FfiConverterByteArray.lower(`data`),FfiConverterString.lower(`integrity`), + FfiConverterTypeHttpMethod.lower(`method`),FfiConverterString.lower(`uri`),FfiConverterOptionalMapStringString.lower(`headers`),FfiConverterOptionalByteArray.lower(`body`), ) }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_i8(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_i8(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_i8(future) }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, // lift function - { FfiConverterBoolean.lift(it) }, + { FfiConverterTypeHttpResponse.lift(it) }, // Error FFI converter - UniffiNullRustCallStatusErrorHandler, + WebviewBundleException.ErrorHandler, ) } @@ -5289,112 +5253,46 @@ open class IntegrityCheckImpl: Disposable, AutoCloseable, IntegrityCheck - + companion object { + /** - * @suppress - */ - companion object + * Proxy by a custom resolver. + */ fun `custom`(`resolver`: ProxyResolver): ProxyProtocolHandler { + return FfiConverterTypeProxyProtocolHandler.lift( + uniffiRustCall() { _status -> + UniffiLib.uniffi_wvb_ffi_fn_constructor_proxyprotocolhandler_custom( + FfiConverterTypeProxyResolver.lower(`resolver`),_status) } - - - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceIntegrityCheck { - internal object `check`: UniffiCallbackInterfaceIntegrityCheckMethod0 { - override fun callback(`uniffiHandle`: Long,`data`: RustBuffer.ByValue,`integrity`: RustBuffer.ByValue,`uniffiFutureCallback`: UniffiForeignFutureCompleteI8,`uniffiCallbackData`: Long,`uniffiOutDroppedCallback`: UniffiForeignFutureDroppedCallbackStruct,) { - val uniffiObj = FfiConverterTypeIntegrityCheck.handleMap.get(uniffiHandle) - val makeCall = suspend { -> - uniffiObj.`check`( - FfiConverterByteArray.lift(`data`), - FfiConverterString.lift(`integrity`), - ) - } - val uniffiHandleSuccess = { returnValue: kotlin.Boolean -> - val uniffiResult = UniffiForeignFutureResultI8.UniffiByValue( - FfiConverterBoolean.lower(returnValue), - UniffiRustCallStatus.ByValue() - ) - uniffiResult.write() - uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) - } - val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> - uniffiFutureCallback.callback( - uniffiCallbackData, - UniffiForeignFutureResultI8.UniffiByValue( - 0.toByte(), - callStatus, - ), - ) - } - uniffiTraitInterfaceCallAsync( - makeCall, - uniffiHandleSuccess, - uniffiHandleError, - uniffiOutDroppedCallback - ) - } - } - - internal object uniffiFree: UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeIntegrityCheck.handleMap.remove(handle) - } - } - - internal object uniffiClone: UniffiCallbackInterfaceClone { - override fun callback(handle: Long): Long { - return FfiConverterTypeIntegrityCheck.handleMap.clone(handle) - } - } - - internal var vtable = UniffiVTableCallbackInterfaceIntegrityCheck.UniffiByValue( - uniffiFree, - uniffiClone, - `check`, ) + } + - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_wvb_ffi_fn_init_callback_vtable_integritycheck(vtable) + } + } + /** * @suppress */ -public object FfiConverterTypeIntegrityCheck: FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: IntegrityCheck): Long { - if (value is IntegrityCheckImpl) { - // Rust-implemented object. Clone the handle and return it - return value.uniffiCloneHandle() - } else { - // Kotlin object, generate a new vtable handle and return that. - return handleMap.insert(value) - } +public object FfiConverterTypeProxyProtocolHandler: FfiConverter { + override fun lower(value: ProxyProtocolHandler): Long { + return value.uniffiCloneHandle() } - override fun lift(value: Long): IntegrityCheck { - if ((value and 1.toLong()) == 0.toLong()) { - // Rust-generated handle, construct a new class that uses the handle to implement the - // interface - return IntegrityCheckImpl(UniffiWithHandle, value) - } else { - // Kotlin-generated handle, get the object from the handle map - return handleMap.remove(value) - } + override fun lift(value: Long): ProxyProtocolHandler { + return ProxyProtocolHandler(UniffiWithHandle, value) } - override fun read(buf: ByteBuffer): IntegrityCheck { + override fun read(buf: ByteBuffer): ProxyProtocolHandler { return lift(buf.getLong()) } - override fun allocationSize(value: IntegrityCheck) = 8UL + override fun allocationSize(value: ProxyProtocolHandler) = 8UL - override fun write(value: IntegrityCheck, buf: ByteBuffer) { + override fun write(value: ProxyProtocolHandler, buf: ByteBuffer) { buf.putLong(lower(value)) } } @@ -5496,54 +5394,25 @@ public object FfiConverterTypeIntegrityCheck: FfiConverter /** - * A descriptor loaded (and cached) by a [`BundleSource`]. + * Resolves the proxy target for a request uri, e.g. `http://localhost:3000`. * - * Holds the parsed header/index together with the filepath it was loaded from, so - * reading entry data always targets the exact bundle version that produced this - * descriptor — even if the source's active version is swapped concurrently. Entry - * data is read lazily from disk via [`LoadedDescriptor::get_data`], avoiding loading - * the whole bundle into memory. + * The path and query of the request are appended to the returned target. Return `null` to + * not proxy the request; the handler then fails with `CannotResolveProxyServer`. */ -public interface LoadedDescriptorInterface { - - /** - * Returns the bundle descriptor (header + index metadata). - * - * The returned descriptor carries no reference back to the source, so it can - * outlive this `LoadedDescriptor`. It holds only metadata, so its `index()` is - * unsupported; use [`get_data`](LoadedDescriptor::get_data) for entry data. - */ - fun `descriptor`(): BundleDescriptor +public interface ProxyResolver { - /** - * Reads the bytes for `path`, loading them lazily from disk. - * - * The read targets the bundle file this descriptor was loaded from, so the data - * stays consistent with [`descriptor`](LoadedDescriptor::descriptor) even if the - * source's active version changes meanwhile. Returns `None` if `path` does not - * exist in the bundle. - */ - suspend fun `getData`(`path`: kotlin.String): kotlin.ByteArray? - - /** - * Reads the xxHash-32 checksum for `path`, loading it lazily from disk. - * Returns `None` if `path` does not exist in the bundle. - */ - suspend fun `getDataChecksum`(`path`: kotlin.String): kotlin.UInt? + suspend fun `resolve`(`uri`: kotlin.String): kotlin.String? companion object } /** - * A descriptor loaded (and cached) by a [`BundleSource`]. + * Resolves the proxy target for a request uri, e.g. `http://localhost:3000`. * - * Holds the parsed header/index together with the filepath it was loaded from, so - * reading entry data always targets the exact bundle version that produced this - * descriptor — even if the source's active version is swapped concurrently. Entry - * data is read lazily from disk via [`LoadedDescriptor::get_data`], avoiding loading - * the whole bundle into memory. + * The path and query of the request are appended to the returned target. Return `null` to + * not proxy the request; the handler then fails with `CannotResolveProxyServer`. */ -open class LoadedDescriptor: Disposable, AutoCloseable, LoadedDescriptorInterface +open class ProxyResolverImpl: Disposable, AutoCloseable, ProxyResolver { @Suppress("UNUSED_PARAMETER") @@ -5568,91 +5437,1957 @@ open class LoadedDescriptor: Disposable, AutoCloseable, LoadedDescriptorInterfac this.cleanable = null } - protected val handle: Long - protected val cleanable: UniffiCleaner.Cleanable? - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable?.clean() - } - } + protected val handle: Long + protected val cleanable: UniffiCleaner.Cleanable? + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable?.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithHandle(block: (handle: Long) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the handle being freed concurrently. + try { + return block(this.uniffiCloneHandle()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable?.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val handle: Long) : Runnable { + override fun run() { + if (handle == 0.toLong()) { + // Fake object created with `NoHandle`, don't try to free. + return; + } + uniffiRustCall { status -> + UniffiLib.uniffi_wvb_ffi_fn_free_proxyresolver(handle, status) + } + } + } + + /** + * @suppress + */ + fun uniffiCloneHandle(): Long { + if (handle == 0.toLong()) { + throw InternalException("uniffiCloneHandle() called on NoHandle object"); + } + return uniffiRustCall() { status -> + UniffiLib.uniffi_wvb_ffi_fn_clone_proxyresolver(handle, status) + } + } + + + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `resolve`(`uri`: kotlin.String) : kotlin.String? { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_proxyresolver_resolve( + uniffiHandle, + FfiConverterString.lower(`uri`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterOptionalString.lift(it) }, + // Error FFI converter + UniffiNullRustCallStatusErrorHandler, + ) + } + + + + + + + + + /** + * @suppress + */ + companion object + +} + + + +// Put the implementation in an object so we don't pollute the top-level namespace +internal object uniffiCallbackInterfaceProxyResolver { + internal object `resolve`: UniffiCallbackInterfaceProxyResolverMethod0 { + override fun callback(`uniffiHandle`: Long,`uri`: RustBuffer.ByValue,`uniffiFutureCallback`: UniffiForeignFutureCompleteRustBuffer,`uniffiCallbackData`: Long,`uniffiOutDroppedCallback`: UniffiForeignFutureDroppedCallbackStruct,) { + val uniffiObj = FfiConverterTypeProxyResolver.handleMap.get(uniffiHandle) + val makeCall = suspend { -> + uniffiObj.`resolve`( + FfiConverterString.lift(`uri`), + ) + } + val uniffiHandleSuccess = { returnValue: kotlin.String? -> + val uniffiResult = UniffiForeignFutureResultRustBuffer.UniffiByValue( + FfiConverterOptionalString.lower(returnValue), + UniffiRustCallStatus.ByValue() + ) + uniffiResult.write() + uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) + } + val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> + uniffiFutureCallback.callback( + uniffiCallbackData, + UniffiForeignFutureResultRustBuffer.UniffiByValue( + RustBuffer.ByValue(), + callStatus, + ), + ) + } + uniffiTraitInterfaceCallAsync( + makeCall, + uniffiHandleSuccess, + uniffiHandleError, + uniffiOutDroppedCallback + ) + } + } + + internal object uniffiFree: UniffiCallbackInterfaceFree { + override fun callback(handle: Long) { + FfiConverterTypeProxyResolver.handleMap.remove(handle) + } + } + + internal object uniffiClone: UniffiCallbackInterfaceClone { + override fun callback(handle: Long): Long { + return FfiConverterTypeProxyResolver.handleMap.clone(handle) + } + } + + internal var vtable = UniffiVTableCallbackInterfaceProxyResolver.UniffiByValue( + uniffiFree, + uniffiClone, + `resolve`, + ) + + // Registers the foreign callback with the Rust side. + // This method is generated for each callback interface. + internal fun register(lib: UniffiLib) { + lib.uniffi_wvb_ffi_fn_init_callback_vtable_proxyresolver(vtable) + } +} + +/** + * @suppress + */ +public object FfiConverterTypeProxyResolver: FfiConverter { + internal val handleMap = UniffiHandleMap() + + override fun lower(value: ProxyResolver): Long { + if (value is ProxyResolverImpl) { + // Rust-implemented object. Clone the handle and return it + return value.uniffiCloneHandle() + } else { + // Kotlin object, generate a new vtable handle and return that. + return handleMap.insert(value) + } + } + + override fun lift(value: Long): ProxyResolver { + if ((value and 1.toLong()) == 0.toLong()) { + // Rust-generated handle, construct a new class that uses the handle to implement the + // interface + return ProxyResolverImpl(UniffiWithHandle, value) + } else { + // Kotlin-generated handle, get the object from the handle map + return handleMap.remove(value) + } + } + + override fun read(buf: ByteBuffer): ProxyResolver { + return lift(buf.getLong()) + } + + override fun allocationSize(value: ProxyResolver) = 8UL + + override fun write(value: ProxyResolver, buf: ByteBuffer) { + buf.putLong(lower(value)) + } +} + + +// This template implements a class for working with a Rust struct via a handle +// to the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque handle to the underlying Rust struct. +// Method calls need to read this handle from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its handle should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the handle, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the handle, but is interrupted +// before it can pass the handle over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read handle value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +/** + * HTTP client for a WebViewBundle remote server. + */ +public interface RemoteInterface { + + /** + * Downloads `url` into `filepath`. + * + * The optional cancellation token stops the transfer without deleting a previously completed + * bundle at the destination. + */ + suspend fun `download`(`url`: kotlin.String, `filepath`: kotlin.String, `cancellation`: Cancellation? = null) + + /** + * Gets the current update document, or `None` when its ETag has not changed. + */ + suspend fun `getUpdate`(`options`: RemoteGetUpdateOptions?): RemoteUpdateResponse? + + companion object +} + +/** + * HTTP client for a WebViewBundle remote server. + */ +open class Remote: Disposable, AutoCloseable, RemoteInterface +{ + + @Suppress("UNUSED_PARAMETER") + /** + * @suppress + */ + constructor(withHandle: UniffiWithHandle, handle: Long) { + this.handle = handle + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(handle)) + } + + /** + * @suppress + * + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noHandle: NoHandle) { + this.handle = 0 + this.cleanable = null + } + /** + * Creates a client for the server at `base_url` (e.g. `"https://bundles.example.com"`). + */ + constructor(`baseUrl`: kotlin.String, `options`: RemoteOptions? = null) : + this(UniffiWithHandle, + uniffiRustCallWithError(WebviewBundleException) { _status -> + UniffiLib.uniffi_wvb_ffi_fn_constructor_remote_new( + + FfiConverterString.lower(`baseUrl`),FfiConverterOptionalTypeRemoteOptions.lower(`options`),_status) +} + ) + + protected val handle: Long + protected val cleanable: UniffiCleaner.Cleanable? + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable?.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithHandle(block: (handle: Long) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the handle being freed concurrently. + try { + return block(this.uniffiCloneHandle()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable?.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val handle: Long) : Runnable { + override fun run() { + if (handle == 0.toLong()) { + // Fake object created with `NoHandle`, don't try to free. + return; + } + uniffiRustCall { status -> + UniffiLib.uniffi_wvb_ffi_fn_free_remote(handle, status) + } + } + } + + /** + * @suppress + */ + fun uniffiCloneHandle(): Long { + if (handle == 0.toLong()) { + throw InternalException("uniffiCloneHandle() called on NoHandle object"); + } + return uniffiRustCall() { status -> + UniffiLib.uniffi_wvb_ffi_fn_clone_remote(handle, status) + } + } + + + /** + * Downloads `url` into `filepath`. + * + * The optional cancellation token stops the transfer without deleting a previously completed + * bundle at the destination. + */ + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `download`(`url`: kotlin.String, `filepath`: kotlin.String, `cancellation`: Cancellation?) { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_remote_download( + uniffiHandle, + FfiConverterString.lower(`url`),FfiConverterString.lower(`filepath`),FfiConverterOptionalTypeCancellation.lower(`cancellation`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_void(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_void(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_void(future) }, + // lift function + { Unit }, + + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + /** + * Gets the current update document, or `None` when its ETag has not changed. + */ + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `getUpdate`(`options`: RemoteGetUpdateOptions?) : RemoteUpdateResponse? { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_remote_get_update( + uniffiHandle, + FfiConverterOptionalTypeRemoteGetUpdateOptions.lower(`options`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterOptionalTypeRemoteUpdateResponse.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + + + + + + + /** + * @suppress + */ + companion object + +} + + +/** + * @suppress + */ +public object FfiConverterTypeRemote: FfiConverter { + override fun lower(value: Remote): Long { + return value.uniffiCloneHandle() + } + + override fun lift(value: Long): Remote { + return Remote(UniffiWithHandle, value) + } + + override fun read(buf: ByteBuffer): Remote { + return lift(buf.getLong()) + } + + override fun allocationSize(value: Remote) = 8UL + + override fun write(value: Remote, buf: ByteBuffer) { + buf.putLong(lower(value)) + } +} + + +// This template implements a class for working with a Rust struct via a handle +// to the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque handle to the underlying Rust struct. +// Method calls need to read this handle from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its handle should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the handle, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the handle, but is interrupted +// before it can pass the handle over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read handle value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +/** + * A callback invoked with download progress as a bundle downloads. + */ +public interface RemoteOnDownload { + + /** + * Receives the current progress for a bundle download. + */ + fun `onDownload`(`data`: RemoteOnDownloadData) + + companion object +} + +/** + * A callback invoked with download progress as a bundle downloads. + */ +open class RemoteOnDownloadImpl: Disposable, AutoCloseable, RemoteOnDownload +{ + + @Suppress("UNUSED_PARAMETER") + /** + * @suppress + */ + constructor(withHandle: UniffiWithHandle, handle: Long) { + this.handle = handle + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(handle)) + } + + /** + * @suppress + * + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noHandle: NoHandle) { + this.handle = 0 + this.cleanable = null + } + + protected val handle: Long + protected val cleanable: UniffiCleaner.Cleanable? + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable?.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithHandle(block: (handle: Long) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the handle being freed concurrently. + try { + return block(this.uniffiCloneHandle()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable?.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val handle: Long) : Runnable { + override fun run() { + if (handle == 0.toLong()) { + // Fake object created with `NoHandle`, don't try to free. + return; + } + uniffiRustCall { status -> + UniffiLib.uniffi_wvb_ffi_fn_free_remoteondownload(handle, status) + } + } + } + + /** + * @suppress + */ + fun uniffiCloneHandle(): Long { + if (handle == 0.toLong()) { + throw InternalException("uniffiCloneHandle() called on NoHandle object"); + } + return uniffiRustCall() { status -> + UniffiLib.uniffi_wvb_ffi_fn_clone_remoteondownload(handle, status) + } + } + + + /** + * Receives the current progress for a bundle download. + */override fun `onDownload`(`data`: RemoteOnDownloadData) + = + callWithHandle { + uniffiRustCall() { _status -> + UniffiLib.uniffi_wvb_ffi_fn_method_remoteondownload_on_download( + it, + FfiConverterTypeRemoteOnDownloadData.lower(`data`),_status) +} + } + + + + + + + + + + + /** + * @suppress + */ + companion object + +} + + + +// Put the implementation in an object so we don't pollute the top-level namespace +internal object uniffiCallbackInterfaceRemoteOnDownload { + internal object `onDownload`: UniffiCallbackInterfaceRemoteOnDownloadMethod0 { + override fun callback(`uniffiHandle`: Long,`data`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) { + val uniffiObj = FfiConverterTypeRemoteOnDownload.handleMap.get(uniffiHandle) + val makeCall = { -> + uniffiObj.`onDownload`( + FfiConverterTypeRemoteOnDownloadData.lift(`data`), + ) + } + val writeReturn = { _: Unit -> Unit } + uniffiTraitInterfaceCall(uniffiCallStatus, makeCall, writeReturn) + } + } + + internal object uniffiFree: UniffiCallbackInterfaceFree { + override fun callback(handle: Long) { + FfiConverterTypeRemoteOnDownload.handleMap.remove(handle) + } + } + + internal object uniffiClone: UniffiCallbackInterfaceClone { + override fun callback(handle: Long): Long { + return FfiConverterTypeRemoteOnDownload.handleMap.clone(handle) + } + } + + internal var vtable = UniffiVTableCallbackInterfaceRemoteOnDownload.UniffiByValue( + uniffiFree, + uniffiClone, + `onDownload`, + ) + + // Registers the foreign callback with the Rust side. + // This method is generated for each callback interface. + internal fun register(lib: UniffiLib) { + lib.uniffi_wvb_ffi_fn_init_callback_vtable_remoteondownload(vtable) + } +} + +/** + * @suppress + */ +public object FfiConverterTypeRemoteOnDownload: FfiConverter { + internal val handleMap = UniffiHandleMap() + + override fun lower(value: RemoteOnDownload): Long { + if (value is RemoteOnDownloadImpl) { + // Rust-implemented object. Clone the handle and return it + return value.uniffiCloneHandle() + } else { + // Kotlin object, generate a new vtable handle and return that. + return handleMap.insert(value) + } + } + + override fun lift(value: Long): RemoteOnDownload { + if ((value and 1.toLong()) == 0.toLong()) { + // Rust-generated handle, construct a new class that uses the handle to implement the + // interface + return RemoteOnDownloadImpl(UniffiWithHandle, value) + } else { + // Kotlin-generated handle, get the object from the handle map + return handleMap.remove(value) + } + } + + override fun read(buf: ByteBuffer): RemoteOnDownload { + return lift(buf.getLong()) + } + + override fun allocationSize(value: RemoteOnDownload) = 8UL + + override fun write(value: RemoteOnDownload, buf: ByteBuffer) { + buf.putLong(lower(value)) + } +} + + +// This template implements a class for working with a Rust struct via a handle +// to the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque handle to the underlying Rust struct. +// Method calls need to read this handle from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its handle should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the handle, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the handle, but is interrupted +// before it can pass the handle over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read handle value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +public interface SignatureCustomVerify { + + suspend fun `verify`(`message`: kotlin.ByteArray, `signature`: kotlin.String): kotlin.Boolean + + companion object +} + +open class SignatureCustomVerifyImpl: Disposable, AutoCloseable, SignatureCustomVerify +{ + + @Suppress("UNUSED_PARAMETER") + /** + * @suppress + */ + constructor(withHandle: UniffiWithHandle, handle: Long) { + this.handle = handle + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(handle)) + } + + /** + * @suppress + * + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noHandle: NoHandle) { + this.handle = 0 + this.cleanable = null + } + + protected val handle: Long + protected val cleanable: UniffiCleaner.Cleanable? + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable?.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithHandle(block: (handle: Long) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the handle being freed concurrently. + try { + return block(this.uniffiCloneHandle()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable?.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val handle: Long) : Runnable { + override fun run() { + if (handle == 0.toLong()) { + // Fake object created with `NoHandle`, don't try to free. + return; + } + uniffiRustCall { status -> + UniffiLib.uniffi_wvb_ffi_fn_free_signaturecustomverify(handle, status) + } + } + } + + /** + * @suppress + */ + fun uniffiCloneHandle(): Long { + if (handle == 0.toLong()) { + throw InternalException("uniffiCloneHandle() called on NoHandle object"); + } + return uniffiRustCall() { status -> + UniffiLib.uniffi_wvb_ffi_fn_clone_signaturecustomverify(handle, status) + } + } + + + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `verify`(`message`: kotlin.ByteArray, `signature`: kotlin.String) : kotlin.Boolean { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_signaturecustomverify_verify( + uniffiHandle, + FfiConverterByteArray.lower(`message`),FfiConverterString.lower(`signature`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_i8(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_i8(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_i8(future) }, + // lift function + { FfiConverterBoolean.lift(it) }, + // Error FFI converter + UniffiNullRustCallStatusErrorHandler, + ) + } + + + + + + + + + /** + * @suppress + */ + companion object + +} + + + +// Put the implementation in an object so we don't pollute the top-level namespace +internal object uniffiCallbackInterfaceSignatureCustomVerify { + internal object `verify`: UniffiCallbackInterfaceSignatureCustomVerifyMethod0 { + override fun callback(`uniffiHandle`: Long,`message`: RustBuffer.ByValue,`signature`: RustBuffer.ByValue,`uniffiFutureCallback`: UniffiForeignFutureCompleteI8,`uniffiCallbackData`: Long,`uniffiOutDroppedCallback`: UniffiForeignFutureDroppedCallbackStruct,) { + val uniffiObj = FfiConverterTypeSignatureCustomVerify.handleMap.get(uniffiHandle) + val makeCall = suspend { -> + uniffiObj.`verify`( + FfiConverterByteArray.lift(`message`), + FfiConverterString.lift(`signature`), + ) + } + val uniffiHandleSuccess = { returnValue: kotlin.Boolean -> + val uniffiResult = UniffiForeignFutureResultI8.UniffiByValue( + FfiConverterBoolean.lower(returnValue), + UniffiRustCallStatus.ByValue() + ) + uniffiResult.write() + uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) + } + val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> + uniffiFutureCallback.callback( + uniffiCallbackData, + UniffiForeignFutureResultI8.UniffiByValue( + 0.toByte(), + callStatus, + ), + ) + } + uniffiTraitInterfaceCallAsync( + makeCall, + uniffiHandleSuccess, + uniffiHandleError, + uniffiOutDroppedCallback + ) + } + } + + internal object uniffiFree: UniffiCallbackInterfaceFree { + override fun callback(handle: Long) { + FfiConverterTypeSignatureCustomVerify.handleMap.remove(handle) + } + } + + internal object uniffiClone: UniffiCallbackInterfaceClone { + override fun callback(handle: Long): Long { + return FfiConverterTypeSignatureCustomVerify.handleMap.clone(handle) + } + } + + internal var vtable = UniffiVTableCallbackInterfaceSignatureCustomVerify.UniffiByValue( + uniffiFree, + uniffiClone, + `verify`, + ) + + // Registers the foreign callback with the Rust side. + // This method is generated for each callback interface. + internal fun register(lib: UniffiLib) { + lib.uniffi_wvb_ffi_fn_init_callback_vtable_signaturecustomverify(vtable) + } +} + +/** + * @suppress + */ +public object FfiConverterTypeSignatureCustomVerify: FfiConverter { + internal val handleMap = UniffiHandleMap() + + override fun lower(value: SignatureCustomVerify): Long { + if (value is SignatureCustomVerifyImpl) { + // Rust-implemented object. Clone the handle and return it + return value.uniffiCloneHandle() + } else { + // Kotlin object, generate a new vtable handle and return that. + return handleMap.insert(value) + } + } + + override fun lift(value: Long): SignatureCustomVerify { + if ((value and 1.toLong()) == 0.toLong()) { + // Rust-generated handle, construct a new class that uses the handle to implement the + // interface + return SignatureCustomVerifyImpl(UniffiWithHandle, value) + } else { + // Kotlin-generated handle, get the object from the handle map + return handleMap.remove(value) + } + } + + override fun read(buf: ByteBuffer): SignatureCustomVerify { + return lift(buf.getLong()) + } + + override fun allocationSize(value: SignatureCustomVerify) = 8UL + + override fun write(value: SignatureCustomVerify, buf: ByteBuffer) { + buf.putLong(lower(value)) + } +} + + +// This template implements a class for working with a Rust struct via a handle +// to the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque handle to the underlying Rust struct. +// Method calls need to read this handle from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its handle should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the handle, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the handle, but is interrupted +// before it can pass the handle over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read handle value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +/** + * Unified access point for bundles from both the builtin and remote sources. + * + * The remote source takes precedence over the builtin source when both contain + * a bundle with the same name. + */ +public interface SourceInterface { + + suspend fun `fetchBuiltinBundle`(`bundleName`: kotlin.String, `version`: kotlin.String): Bundle + + /** + * Loads the full bundle (header + index + data) for `bundle_name`. + */ + suspend fun `fetchBundle`(`bundleName`: kotlin.String): Bundle + + /** + * Loads only the header and index for `bundle_name`, skipping the data section. + * The returned descriptor does not support [`BundleDescriptor::index`]; use + * [`fetch`](Source::fetch_bundle) when entry data is needed. + */ + suspend fun `fetchDescriptor`(`bundleName`: kotlin.String): BundleDescriptor + + suspend fun `fetchRemoteBundle`(`bundleName`: kotlin.String, `version`: kotlin.String): Bundle + + /** + * Resolves the on-disk path of the builtin bundle `bundle_name` at `version`, + * without checking whether the file exists. + */ + fun `getBuiltinBundleFilepath`(`bundleName`: kotlin.String, `version`: kotlin.String): kotlin.String + + suspend fun `getBuiltinVersionData`(`bundleName`: kotlin.String, `version`: kotlin.String): ManifestVersionData? + + /** + * Resolves the on-disk path of the remote bundle `bundle_name` at `version`, + * without checking whether the file exists. + */ + fun `getRemoteBundleFilepath`(`bundleName`: kotlin.String, `version`: kotlin.String): kotlin.String + + suspend fun `getRemotePreviousVersion`(`bundleName`: kotlin.String): kotlin.String? + + suspend fun `getRemoteStagedVersion`(`bundleName`: kotlin.String): kotlin.String? + + suspend fun `getRemoteVersionData`(`bundleName`: kotlin.String, `version`: kotlin.String): ManifestVersionData? + + suspend fun `getVersion`(`bundleName`: kotlin.String): BundleSourceVersion? + + suspend fun `listBuiltinBundles`(): List + + suspend fun `listBundles`(): List + + suspend fun `listRemoteBundles`(): List + + /** + * Loads (and caches) the descriptor for the current version of `bundle_name`. + * Concurrent calls for the same bundle share a single load (single-flight) and + * return the cached descriptor until the active version changes or + * [`unload_descriptor`](Source::unload) is called. + */ + suspend fun `load`(`bundleName`: kotlin.String): LoadedDescriptor + + suspend fun `pruneRemoteBundle`(`bundleName`: kotlin.String): ManifestPruneResult + + suspend fun `pruneRemoteBundles`(`bundleNames`: List): List + + suspend fun `removeRemoteBundle`(`bundleName`: kotlin.String, `version`: kotlin.String, `force`: kotlin.Boolean? = null): ManifestRemoveResult + + suspend fun `removeRemoteBundles`(`items`: Map): List + + suspend fun `resolveFilepath`(`bundleName`: kotlin.String): kotlin.String + + suspend fun `stageRemoteBundle`(`bundleName`: kotlin.String, `data`: ManifestStageData): ManifestStageResult + + suspend fun `stageRemoteBundles`(`items`: Map): List + + /** + * Drops the cached descriptor for `bundle_name`, if present. Already-returned + * [`LoadedDescriptor`] handles keep working; the next call to [`Self::fetch_descriptor`] + * reloads from disk. Returns `true` if a cached descriptor was removed. + */ + fun `unload`(`bundleName`: kotlin.String): kotlin.Boolean + + suspend fun `updateRemoteVersion`(`bundleName`: kotlin.String, `version`: kotlin.String): ManifestSetCurrentVersionResult + + suspend fun `updateRemoteVersions`(`items`: Map): List + + companion object +} + +/** + * Unified access point for bundles from both the builtin and remote sources. + * + * The remote source takes precedence over the builtin source when both contain + * a bundle with the same name. + */ +open class Source: Disposable, AutoCloseable, SourceInterface +{ + + @Suppress("UNUSED_PARAMETER") + /** + * @suppress + */ + constructor(withHandle: UniffiWithHandle, handle: Long) { + this.handle = handle + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(handle)) + } + + /** + * @suppress + * + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noHandle: NoHandle) { + this.handle = 0 + this.cleanable = null + } + constructor(`config`: SourceConfig) : + this(UniffiWithHandle, + uniffiRustCallWithError(WebviewBundleException) { _status -> + UniffiLib.uniffi_wvb_ffi_fn_constructor_source_new( + + FfiConverterTypeSourceConfig.lower(`config`),_status) +} + ) + + protected val handle: Long + protected val cleanable: UniffiCleaner.Cleanable? + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable?.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithHandle(block: (handle: Long) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the handle being freed concurrently. + try { + return block(this.uniffiCloneHandle()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable?.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val handle: Long) : Runnable { + override fun run() { + if (handle == 0.toLong()) { + // Fake object created with `NoHandle`, don't try to free. + return; + } + uniffiRustCall { status -> + UniffiLib.uniffi_wvb_ffi_fn_free_source(handle, status) + } + } + } + + /** + * @suppress + */ + fun uniffiCloneHandle(): Long { + if (handle == 0.toLong()) { + throw InternalException("uniffiCloneHandle() called on NoHandle object"); + } + return uniffiRustCall() { status -> + UniffiLib.uniffi_wvb_ffi_fn_clone_source(handle, status) + } + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `fetchBuiltinBundle`(`bundleName`: kotlin.String, `version`: kotlin.String) : Bundle { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_fetch_builtin_bundle( + uniffiHandle, + FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_u64(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_u64(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_u64(future) }, + // lift function + { FfiConverterTypeBundle.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + /** + * Loads the full bundle (header + index + data) for `bundle_name`. + */ + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `fetchBundle`(`bundleName`: kotlin.String) : Bundle { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_fetch_bundle( + uniffiHandle, + FfiConverterString.lower(`bundleName`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_u64(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_u64(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_u64(future) }, + // lift function + { FfiConverterTypeBundle.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + /** + * Loads only the header and index for `bundle_name`, skipping the data section. + * The returned descriptor does not support [`BundleDescriptor::index`]; use + * [`fetch`](Source::fetch_bundle) when entry data is needed. + */ + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `fetchDescriptor`(`bundleName`: kotlin.String) : BundleDescriptor { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_fetch_descriptor( + uniffiHandle, + FfiConverterString.lower(`bundleName`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_u64(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_u64(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_u64(future) }, + // lift function + { FfiConverterTypeBundleDescriptor.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `fetchRemoteBundle`(`bundleName`: kotlin.String, `version`: kotlin.String) : Bundle { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_fetch_remote_bundle( + uniffiHandle, + FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_u64(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_u64(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_u64(future) }, + // lift function + { FfiConverterTypeBundle.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + /** + * Resolves the on-disk path of the builtin bundle `bundle_name` at `version`, + * without checking whether the file exists. + */ + @Throws(WebviewBundleException::class)override fun `getBuiltinBundleFilepath`(`bundleName`: kotlin.String, `version`: kotlin.String): kotlin.String { + return FfiConverterString.lift( + callWithHandle { + uniffiRustCallWithError(WebviewBundleException) { _status -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_get_builtin_bundle_filepath( + it, + FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`),_status) +} + } + ) + } + + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `getBuiltinVersionData`(`bundleName`: kotlin.String, `version`: kotlin.String) : ManifestVersionData? { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_get_builtin_version_data( + uniffiHandle, + FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterOptionalTypeManifestVersionData.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + /** + * Resolves the on-disk path of the remote bundle `bundle_name` at `version`, + * without checking whether the file exists. + */ + @Throws(WebviewBundleException::class)override fun `getRemoteBundleFilepath`(`bundleName`: kotlin.String, `version`: kotlin.String): kotlin.String { + return FfiConverterString.lift( + callWithHandle { + uniffiRustCallWithError(WebviewBundleException) { _status -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_get_remote_bundle_filepath( + it, + FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`),_status) +} + } + ) + } + + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `getRemotePreviousVersion`(`bundleName`: kotlin.String) : kotlin.String? { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_get_remote_previous_version( + uniffiHandle, + FfiConverterString.lower(`bundleName`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterOptionalString.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `getRemoteStagedVersion`(`bundleName`: kotlin.String) : kotlin.String? { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_get_remote_staged_version( + uniffiHandle, + FfiConverterString.lower(`bundleName`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterOptionalString.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `getRemoteVersionData`(`bundleName`: kotlin.String, `version`: kotlin.String) : ManifestVersionData? { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_get_remote_version_data( + uniffiHandle, + FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterOptionalTypeManifestVersionData.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `getVersion`(`bundleName`: kotlin.String) : BundleSourceVersion? { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_get_version( + uniffiHandle, + FfiConverterString.lower(`bundleName`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterOptionalTypeBundleSourceVersion.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `listBuiltinBundles`() : List { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_list_builtin_bundles( + uniffiHandle, + + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterSequenceTypeSourceListItem.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `listBundles`() : List { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_list_bundles( + uniffiHandle, + + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterSequenceTypeSourceListItem.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `listRemoteBundles`() : List { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_list_remote_bundles( + uniffiHandle, + + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterSequenceTypeSourceListItem.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + /** + * Loads (and caches) the descriptor for the current version of `bundle_name`. + * Concurrent calls for the same bundle share a single load (single-flight) and + * return the cached descriptor until the active version changes or + * [`unload_descriptor`](Source::unload) is called. + */ + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `load`(`bundleName`: kotlin.String) : LoadedDescriptor { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_load( + uniffiHandle, + FfiConverterString.lower(`bundleName`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_u64(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_u64(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_u64(future) }, + // lift function + { FfiConverterTypeLoadedDescriptor.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `pruneRemoteBundle`(`bundleName`: kotlin.String) : ManifestPruneResult { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_prune_remote_bundle( + uniffiHandle, + FfiConverterString.lower(`bundleName`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterTypeManifestPruneResult.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `pruneRemoteBundles`(`bundleNames`: List) : List { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_prune_remote_bundles( + uniffiHandle, + FfiConverterSequenceString.lower(`bundleNames`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterSequenceTypeManifestPruneResult.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `removeRemoteBundle`(`bundleName`: kotlin.String, `version`: kotlin.String, `force`: kotlin.Boolean?) : ManifestRemoveResult { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_remove_remote_bundle( + uniffiHandle, + FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`),FfiConverterOptionalBoolean.lower(`force`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterTypeManifestRemoveResult.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) } - @Synchronized - override fun close() { - this.destroy() + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `removeRemoteBundles`(`items`: Map) : List { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_remove_remote_bundles( + uniffiHandle, + FfiConverterMapStringTypeManifestRemoveData.lower(`items`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterSequenceTypeManifestRemoveResult.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) } - internal inline fun callWithHandle(block: (handle: Long) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the handle being freed concurrently. - try { - return block(this.uniffiCloneHandle()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable?.clean() - } - } + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `resolveFilepath`(`bundleName`: kotlin.String) : kotlin.String { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_resolve_filepath( + uniffiHandle, + FfiConverterString.lower(`bundleName`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterString.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) } - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val handle: Long) : Runnable { - override fun run() { - if (handle == 0.toLong()) { - // Fake object created with `NoHandle`, don't try to free. - return; - } - uniffiRustCall { status -> - UniffiLib.uniffi_wvb_ffi_fn_free_loadeddescriptor(handle, status) - } - } + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `stageRemoteBundle`(`bundleName`: kotlin.String, `data`: ManifestStageData) : ManifestStageResult { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_stage_remote_bundle( + uniffiHandle, + FfiConverterString.lower(`bundleName`),FfiConverterTypeManifestStageData.lower(`data`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterTypeManifestStageResult.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) } - /** - * @suppress - */ - fun uniffiCloneHandle(): Long { - if (handle == 0.toLong()) { - throw InternalException("uniffiCloneHandle() called on NoHandle object"); - } - return uniffiRustCall() { status -> - UniffiLib.uniffi_wvb_ffi_fn_clone_loadeddescriptor(handle, status) - } + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `stageRemoteBundles`(`items`: Map) : List { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_source_stage_remote_bundles( + uniffiHandle, + FfiConverterMapStringTypeManifestStageData.lower(`items`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterSequenceTypeManifestStageResult.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) } /** - * Returns the bundle descriptor (header + index metadata). - * - * The returned descriptor carries no reference back to the source, so it can - * outlive this `LoadedDescriptor`. It holds only metadata, so its `index()` is - * unsupported; use [`get_data`](LoadedDescriptor::get_data) for entry data. - */override fun `descriptor`(): BundleDescriptor { - return FfiConverterTypeBundleDescriptor.lift( + * Drops the cached descriptor for `bundle_name`, if present. Already-returned + * [`LoadedDescriptor`] handles keep working; the next call to [`Self::fetch_descriptor`] + * reloads from disk. Returns `true` if a cached descriptor was removed. + */override fun `unload`(`bundleName`: kotlin.String): kotlin.Boolean { + return FfiConverterBoolean.lift( callWithHandle { uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_loadeddescriptor_descriptor( + UniffiLib.uniffi_wvb_ffi_fn_method_source_unload( it, - _status) + FfiConverterString.lower(`bundleName`),_status) } } ) @@ -5660,54 +7395,42 @@ open class LoadedDescriptor: Disposable, AutoCloseable, LoadedDescriptorInterfac - /** - * Reads the bytes for `path`, loading them lazily from disk. - * - * The read targets the bundle file this descriptor was loaded from, so the data - * stays consistent with [`descriptor`](LoadedDescriptor::descriptor) even if the - * source's active version changes meanwhile. Returns `None` if `path` does not - * exist in the bundle. - */ @Throws(WebviewBundleException::class) @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `getData`(`path`: kotlin.String) : kotlin.ByteArray? { + override suspend fun `updateRemoteVersion`(`bundleName`: kotlin.String, `version`: kotlin.String) : ManifestSetCurrentVersionResult { return uniffiRustCallAsync( callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_loadeddescriptor_get_data( + UniffiLib.uniffi_wvb_ffi_fn_method_source_update_remote_version( uniffiHandle, - FfiConverterString.lower(`path`), + FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`), ) }, { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, // lift function - { FfiConverterOptionalByteArray.lift(it) }, + { FfiConverterTypeManifestSetCurrentVersionResult.lift(it) }, // Error FFI converter WebviewBundleException.ErrorHandler, ) } - /** - * Reads the xxHash-32 checksum for `path`, loading it lazily from disk. - * Returns `None` if `path` does not exist in the bundle. - */ @Throws(WebviewBundleException::class) @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `getDataChecksum`(`path`: kotlin.String) : kotlin.UInt? { + override suspend fun `updateRemoteVersions`(`items`: Map) : List { return uniffiRustCallAsync( callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_loadeddescriptor_get_data_checksum( + UniffiLib.uniffi_wvb_ffi_fn_method_source_update_remote_versions( uniffiHandle, - FfiConverterString.lower(`path`), + FfiConverterMapStringString.lower(`items`), ) }, { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, // lift function - { FfiConverterOptionalUInt.lift(it) }, + { FfiConverterSequenceTypeManifestSetCurrentVersionResult.lift(it) }, // Error FFI converter WebviewBundleException.ErrorHandler, ) @@ -5731,22 +7454,22 @@ open class LoadedDescriptor: Disposable, AutoCloseable, LoadedDescriptorInterfac /** * @suppress */ -public object FfiConverterTypeLoadedDescriptor: FfiConverter { - override fun lower(value: LoadedDescriptor): Long { +public object FfiConverterTypeSource: FfiConverter { + override fun lower(value: Source): Long { return value.uniffiCloneHandle() } - override fun lift(value: Long): LoadedDescriptor { - return LoadedDescriptor(UniffiWithHandle, value) + override fun lift(value: Long): Source { + return Source(UniffiWithHandle, value) } - override fun read(buf: ByteBuffer): LoadedDescriptor { + override fun read(buf: ByteBuffer): Source { return lift(buf.getLong()) } - override fun allocationSize(value: LoadedDescriptor) = 8UL + override fun allocationSize(value: Source) = 8UL - override fun write(value: LoadedDescriptor, buf: ByteBuffer) { + override fun write(value: Source, buf: ByteBuffer) { buf.putLong(lower(value)) } } @@ -5848,30 +7571,27 @@ public object FfiConverterTypeLoadedDescriptor: FfiConverter? = null, `body`: kotlin.ByteArray? = null): HttpResponse + suspend fun `download`(`bundleUpdates`: List, `options`: UpdaterDownloadOptions? = null): List + + suspend fun `getUpdate`(`options`: UpdaterGetUpdateOptions? = null): Update? + + suspend fun `install`(`targets`: List): List + + suspend fun `rollback`(`targets`: List): List companion object } /** - * Proxies HTTP-like requests to another HTTP server (typically a local dev server). - * - * The target is resolved per request — either from a static host mapping - * (e.g. `{"myapp": "http://localhost:8080"}`, keyed by uri host) or by a custom - * [`ProxyResolver`]. A request whose target cannot be resolved fails with an error. + * Orchestrates the full update cycle: checks for a new version on the remote, + * downloads it, verifies integrity/signature, and writes it to the local source. */ -open class ProxyProtocolHandler: Disposable, AutoCloseable, ProxyProtocolHandlerInterface +open class Updater: Disposable, AutoCloseable, UpdaterInterface { @Suppress("UNUSED_PARAMETER") @@ -5895,15 +7615,12 @@ open class ProxyProtocolHandler: Disposable, AutoCloseable, ProxyProtocolHandler this.handle = 0 this.cleanable = null } - /** - * Proxy by a static host mapping, keyed by the uri host. - */ - constructor(`hosts`: Map) : + constructor(`source`: Source, `remote`: Remote, `updateFilepath`: kotlin.String, `options`: UpdaterOptions?) : this(UniffiWithHandle, - uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_constructor_proxyprotocolhandler_new( + uniffiRustCallWithError(WebviewBundleException) { _status -> + UniffiLib.uniffi_wvb_ffi_fn_constructor_updater_new( - FfiConverterMapStringString.lower(`hosts`),_status) + FfiConverterTypeSource.lower(`source`),FfiConverterTypeRemote.lower(`remote`),FfiConverterString.lower(`updateFilepath`),FfiConverterOptionalTypeUpdaterOptions.lower(`options`),_status) } ) @@ -5961,7 +7678,7 @@ open class ProxyProtocolHandler: Disposable, AutoCloseable, ProxyProtocolHandler return; } uniffiRustCall { status -> - UniffiLib.uniffi_wvb_ffi_fn_free_proxyprotocolhandler(handle, status) + UniffiLib.uniffi_wvb_ffi_fn_free_updater(handle, status) } } } @@ -5974,29 +7691,89 @@ open class ProxyProtocolHandler: Disposable, AutoCloseable, ProxyProtocolHandler throw InternalException("uniffiCloneHandle() called on NoHandle object"); } return uniffiRustCall() { status -> - UniffiLib.uniffi_wvb_ffi_fn_clone_proxyprotocolhandler(handle, status) + UniffiLib.uniffi_wvb_ffi_fn_clone_updater(handle, status) } } - /** - * Forwards the request — including `body`, for POST/PUT/PATCH — to the resolved target. - */ @Throws(WebviewBundleException::class) @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `handle`(`method`: HttpMethod, `uri`: kotlin.String, `headers`: Map?, `body`: kotlin.ByteArray?) : HttpResponse { + override suspend fun `download`(`bundleUpdates`: List, `options`: UpdaterDownloadOptions?) : List { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_updater_download( + uniffiHandle, + FfiConverterSequenceTypeBundleUpdate.lower(`bundleUpdates`),FfiConverterOptionalTypeUpdaterDownloadOptions.lower(`options`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterSequenceTypeUpdaterDownloadResult.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `getUpdate`(`options`: UpdaterGetUpdateOptions?) : Update? { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_updater_get_update( + uniffiHandle, + FfiConverterOptionalTypeUpdaterGetUpdateOptions.lower(`options`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterOptionalTypeUpdate.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `install`(`targets`: List) : List { return uniffiRustCallAsync( callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_proxyprotocolhandler_handle( + UniffiLib.uniffi_wvb_ffi_fn_method_updater_install( uniffiHandle, - FfiConverterTypeHttpMethod.lower(`method`),FfiConverterString.lower(`uri`),FfiConverterOptionalMapStringString.lower(`headers`),FfiConverterOptionalByteArray.lower(`body`), + FfiConverterSequenceTypeUpdaterInstallTarget.lower(`targets`), ) }, { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, // lift function - { FfiConverterTypeHttpResponse.lift(it) }, + { FfiConverterSequenceTypeUpdaterInstallResult.lift(it) }, + // Error FFI converter + WebviewBundleException.ErrorHandler, + ) + } + + + @Throws(WebviewBundleException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `rollback`(`targets`: List) : List { + return uniffiRustCallAsync( + callWithHandle { uniffiHandle -> + UniffiLib.uniffi_wvb_ffi_fn_method_updater_rollback( + uniffiHandle, + FfiConverterSequenceTypeUpdaterRollbackTarget.lower(`targets`), + ) + }, + { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, + { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, + { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterSequenceTypeUpdaterRollbackResult.lift(it) }, // Error FFI converter WebviewBundleException.ErrorHandler, ) @@ -6008,1783 +7785,1537 @@ open class ProxyProtocolHandler: Disposable, AutoCloseable, ProxyProtocolHandler - companion object { - + /** - * Proxy by a custom resolver. - */ fun `custom`(`resolver`: ProxyResolver): ProxyProtocolHandler { - return FfiConverterTypeProxyProtocolHandler.lift( - uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_constructor_proxyprotocolhandler_custom( + * @suppress + */ + companion object - FfiConverterTypeProxyResolver.lower(`resolver`),_status) } - ) + + +/** + * @suppress + */ +public object FfiConverterTypeUpdater: FfiConverter { + override fun lower(value: Updater): Long { + return value.uniffiCloneHandle() } - - + override fun lift(value: Long): Updater { + return Updater(UniffiWithHandle, value) + } + + override fun read(buf: ByteBuffer): Updater { + return lift(buf.getLong()) + } + + override fun allocationSize(value: Updater) = 8UL + + override fun write(value: Updater, buf: ByteBuffer) { + buf.putLong(lower(value)) } - } + +/** + * Options for the bundle header section. + */ +data class BuildHeaderOptions ( + var `checksum`: ChecksumWriteOptions? = null + +){ + + + + + + companion object +} + /** * @suppress */ -public object FfiConverterTypeProxyProtocolHandler: FfiConverter { - override fun lower(value: ProxyProtocolHandler): Long { - return value.uniffiCloneHandle() +public object FfiConverterTypeBuildHeaderOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): BuildHeaderOptions { + return BuildHeaderOptions( + FfiConverterOptionalTypeChecksumWriteOptions.read(buf), + ) } - override fun lift(value: Long): ProxyProtocolHandler { - return ProxyProtocolHandler(UniffiWithHandle, value) + override fun allocationSize(value: BuildHeaderOptions) = ( + FfiConverterOptionalTypeChecksumWriteOptions.allocationSize(value.`checksum`) + ) + + override fun write(value: BuildHeaderOptions, buf: ByteBuffer) { + FfiConverterOptionalTypeChecksumWriteOptions.write(value.`checksum`, buf) } +} - override fun read(buf: ByteBuffer): ProxyProtocolHandler { - return lift(buf.getLong()) + + +/** + * Pptions for the bundle index section. + */ +data class BuildIndexOptions ( + var `checksum`: ChecksumWriteOptions? = null + +){ + + + + + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeBuildIndexOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): BuildIndexOptions { + return BuildIndexOptions( + FfiConverterOptionalTypeChecksumWriteOptions.read(buf), + ) } - override fun allocationSize(value: ProxyProtocolHandler) = 8UL + override fun allocationSize(value: BuildIndexOptions) = ( + FfiConverterOptionalTypeChecksumWriteOptions.allocationSize(value.`checksum`) + ) - override fun write(value: ProxyProtocolHandler, buf: ByteBuffer) { - buf.putLong(lower(value)) + override fun write(value: BuildIndexOptions, buf: ByteBuffer) { + FfiConverterOptionalTypeChecksumWriteOptions.write(value.`checksum`, buf) } } -// This template implements a class for working with a Rust struct via a handle -// to the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque handle to the underlying Rust struct. -// Method calls need to read this handle from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its handle should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the handle, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the handle, but is interrupted -// before it can pass the handle over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read handle value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// + +/** + * Top-level options passed to [`BundleBuilder::build`]. + * All fields are optional; omitting them applies the library defaults. + */ +data class BuildOptions ( + var `header`: BuildHeaderOptions? + , + var `index`: BuildIndexOptions? + , + var `dataChecksum`: ChecksumWriteOptions? + +){ + + + + + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeBuildOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): BuildOptions { + return BuildOptions( + FfiConverterOptionalTypeBuildHeaderOptions.read(buf), + FfiConverterOptionalTypeBuildIndexOptions.read(buf), + FfiConverterOptionalTypeChecksumWriteOptions.read(buf), + ) + } + + override fun allocationSize(value: BuildOptions) = ( + FfiConverterOptionalTypeBuildHeaderOptions.allocationSize(value.`header`) + + FfiConverterOptionalTypeBuildIndexOptions.allocationSize(value.`index`) + + FfiConverterOptionalTypeChecksumWriteOptions.allocationSize(value.`dataChecksum`) + ) + + override fun write(value: BuildOptions, buf: ByteBuffer) { + FfiConverterOptionalTypeBuildHeaderOptions.write(value.`header`, buf) + FfiConverterOptionalTypeBuildIndexOptions.write(value.`index`, buf) + FfiConverterOptionalTypeChecksumWriteOptions.write(value.`dataChecksum`, buf) + } +} + /** - * Resolves the proxy target for a request uri, e.g. `http://localhost:3000`. + * How a [`BundleProtocolHandler`] resolves the request uri. * - * The path and query of the request are appended to the returned target. Return `null` to - * not proxy the request; the handler then fails with `CannotResolveProxyServer`. + * Defaults to the first hostname segment as the bundle name, and a directory-index path + * (`/about` -> `/about/index.html`). */ -public interface ProxyResolver { +data class BundleProtocolOptions ( + var `bundleResolver`: BundleResolver? = null + , + var `pathResolver`: PathResolver? = null - suspend fun `resolve`(`uri`: kotlin.String): kotlin.String? +){ + + + + companion object } /** - * Resolves the proxy target for a request uri, e.g. `http://localhost:3000`. - * - * The path and query of the request are appended to the returned target. Return `null` to - * not proxy the request; the handler then fails with `CannotResolveProxyServer`. + * @suppress */ -open class ProxyResolverImpl: Disposable, AutoCloseable, ProxyResolver -{ - - @Suppress("UNUSED_PARAMETER") - /** - * @suppress - */ - constructor(withHandle: UniffiWithHandle, handle: Long) { - this.handle = handle - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(handle)) +public object FfiConverterTypeBundleProtocolOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): BundleProtocolOptions { + return BundleProtocolOptions( + FfiConverterOptionalTypeBundleResolver.read(buf), + FfiConverterOptionalTypePathResolver.read(buf), + ) } - /** - * @suppress - * - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noHandle: NoHandle) { - this.handle = 0 - this.cleanable = null + override fun allocationSize(value: BundleProtocolOptions) = ( + FfiConverterOptionalTypeBundleResolver.allocationSize(value.`bundleResolver`) + + FfiConverterOptionalTypePathResolver.allocationSize(value.`pathResolver`) + ) + + override fun write(value: BundleProtocolOptions, buf: ByteBuffer) { + FfiConverterOptionalTypeBundleResolver.write(value.`bundleResolver`, buf) + FfiConverterOptionalTypePathResolver.write(value.`pathResolver`, buf) } +} - protected val handle: Long - protected val cleanable: UniffiCleaner.Cleanable? - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable?.clean() - } - } - } +/** + * The currently active version of a bundle and where it was loaded from. + */ +data class BundleSourceVersion ( + var `source`: SourceKind + , + var `version`: kotlin.String + +){ + - @Synchronized - override fun close() { - this.destroy() - } + - internal inline fun callWithHandle(block: (handle: Long) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the handle being freed concurrently. - try { - return block(this.uniffiCloneHandle()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable?.clean() - } - } + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeBundleSourceVersion: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): BundleSourceVersion { + return BundleSourceVersion( + FfiConverterTypeSourceKind.read(buf), + FfiConverterString.read(buf), + ) } - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val handle: Long) : Runnable { - override fun run() { - if (handle == 0.toLong()) { - // Fake object created with `NoHandle`, don't try to free. - return; - } - uniffiRustCall { status -> - UniffiLib.uniffi_wvb_ffi_fn_free_proxyresolver(handle, status) - } - } + override fun allocationSize(value: BundleSourceVersion) = ( + FfiConverterTypeSourceKind.allocationSize(value.`source`) + + FfiConverterString.allocationSize(value.`version`) + ) + + override fun write(value: BundleSourceVersion, buf: ByteBuffer) { + FfiConverterTypeSourceKind.write(value.`source`, buf) + FfiConverterString.write(value.`version`, buf) } +} + + +/** + * A bundle advertised by an update document. + */ +data class BundleUpdate ( /** - * @suppress + * Bundle name. */ - fun uniffiCloneHandle(): Long { - if (handle == 0.toLong()) { - throw InternalException("uniffiCloneHandle() called on NoHandle object"); - } - return uniffiRustCall() { status -> - UniffiLib.uniffi_wvb_ffi_fn_clone_proxyresolver(handle, status) - } - } - + var `name`: kotlin.String + , + /** + * Bundle version. + */ + var `version`: kotlin.String + , + /** + * Absolute download URL when the server overrides the default endpoint. + */ + var `downloadUrl`: kotlin.String? + , + /** + * Serialized integrity value for the downloaded bundle. + */ + var `integrity`: kotlin.String? + , + /** + * Provider-defined, string-valued bundle metadata. + */ + var `metadata`: Map? - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `resolve`(`uri`: kotlin.String) : kotlin.String? { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_proxyresolver_resolve( - uniffiHandle, - FfiConverterString.lower(`uri`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterOptionalString.lift(it) }, - // Error FFI converter - UniffiNullRustCallStatusErrorHandler, - ) - } - +){ - - - /** - * @suppress - */ companion object - } - - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceProxyResolver { - internal object `resolve`: UniffiCallbackInterfaceProxyResolverMethod0 { - override fun callback(`uniffiHandle`: Long,`uri`: RustBuffer.ByValue,`uniffiFutureCallback`: UniffiForeignFutureCompleteRustBuffer,`uniffiCallbackData`: Long,`uniffiOutDroppedCallback`: UniffiForeignFutureDroppedCallbackStruct,) { - val uniffiObj = FfiConverterTypeProxyResolver.handleMap.get(uniffiHandle) - val makeCall = suspend { -> - uniffiObj.`resolve`( - FfiConverterString.lift(`uri`), - ) - } - val uniffiHandleSuccess = { returnValue: kotlin.String? -> - val uniffiResult = UniffiForeignFutureResultRustBuffer.UniffiByValue( - FfiConverterOptionalString.lower(returnValue), - UniffiRustCallStatus.ByValue() - ) - uniffiResult.write() - uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) - } - val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> - uniffiFutureCallback.callback( - uniffiCallbackData, - UniffiForeignFutureResultRustBuffer.UniffiByValue( - RustBuffer.ByValue(), - callStatus, - ), - ) - } - uniffiTraitInterfaceCallAsync( - makeCall, - uniffiHandleSuccess, - uniffiHandleError, - uniffiOutDroppedCallback - ) - } +/** + * @suppress + */ +public object FfiConverterTypeBundleUpdate: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): BundleUpdate { + return BundleUpdate( + FfiConverterString.read(buf), + FfiConverterString.read(buf), + FfiConverterOptionalString.read(buf), + FfiConverterOptionalString.read(buf), + FfiConverterOptionalMapStringString.read(buf), + ) } - internal object uniffiFree: UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeProxyResolver.handleMap.remove(handle) - } - } + override fun allocationSize(value: BundleUpdate) = ( + FfiConverterString.allocationSize(value.`name`) + + FfiConverterString.allocationSize(value.`version`) + + FfiConverterOptionalString.allocationSize(value.`downloadUrl`) + + FfiConverterOptionalString.allocationSize(value.`integrity`) + + FfiConverterOptionalMapStringString.allocationSize(value.`metadata`) + ) - internal object uniffiClone: UniffiCallbackInterfaceClone { - override fun callback(handle: Long): Long { - return FfiConverterTypeProxyResolver.handleMap.clone(handle) - } + override fun write(value: BundleUpdate, buf: ByteBuffer) { + FfiConverterString.write(value.`name`, buf) + FfiConverterString.write(value.`version`, buf) + FfiConverterOptionalString.write(value.`downloadUrl`, buf) + FfiConverterOptionalString.write(value.`integrity`, buf) + FfiConverterOptionalMapStringString.write(value.`metadata`, buf) } +} - internal var vtable = UniffiVTableCallbackInterfaceProxyResolver.UniffiByValue( - uniffiFree, - uniffiClone, - `resolve`, - ) - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_wvb_ffi_fn_init_callback_vtable_proxyresolver(vtable) - } + +/** + * How each entry's xxHash-32 data checksum is verified when its data is read. + */ +data class ChecksumReadOptions ( + /** + * Verify each entry's data checksum when its data is read (default: `true`). + */ + var `verify`: kotlin.Boolean? = null + , + /** + * The seed the data checksums were built with (default: `0`). + */ + var `seed`: kotlin.UInt? = null + +){ + + + + + + companion object } /** * @suppress */ -public object FfiConverterTypeProxyResolver: FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: ProxyResolver): Long { - if (value is ProxyResolverImpl) { - // Rust-implemented object. Clone the handle and return it - return value.uniffiCloneHandle() - } else { - // Kotlin object, generate a new vtable handle and return that. - return handleMap.insert(value) - } +public object FfiConverterTypeChecksumReadOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ChecksumReadOptions { + return ChecksumReadOptions( + FfiConverterOptionalBoolean.read(buf), + FfiConverterOptionalUInt.read(buf), + ) } - override fun lift(value: Long): ProxyResolver { - if ((value and 1.toLong()) == 0.toLong()) { - // Rust-generated handle, construct a new class that uses the handle to implement the - // interface - return ProxyResolverImpl(UniffiWithHandle, value) - } else { - // Kotlin-generated handle, get the object from the handle map - return handleMap.remove(value) - } + override fun allocationSize(value: ChecksumReadOptions) = ( + FfiConverterOptionalBoolean.allocationSize(value.`verify`) + + FfiConverterOptionalUInt.allocationSize(value.`seed`) + ) + + override fun write(value: ChecksumReadOptions, buf: ByteBuffer) { + FfiConverterOptionalBoolean.write(value.`verify`, buf) + FfiConverterOptionalUInt.write(value.`seed`, buf) } +} - override fun read(buf: ByteBuffer): ProxyResolver { - return lift(buf.getLong()) + + +data class ChecksumWriteOptions ( + /** + * The seed the data checksums were built with (default: `0`). + */ + var `seed`: kotlin.UInt? = null + +){ + + + + + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeChecksumWriteOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ChecksumWriteOptions { + return ChecksumWriteOptions( + FfiConverterOptionalUInt.read(buf), + ) } - override fun allocationSize(value: ProxyResolver) = 8UL + override fun allocationSize(value: ChecksumWriteOptions) = ( + FfiConverterOptionalUInt.allocationSize(value.`seed`) + ) - override fun write(value: ProxyResolver, buf: ByteBuffer) { - buf.putLong(lower(value)) + override fun write(value: ChecksumWriteOptions, buf: ByteBuffer) { + FfiConverterOptionalUInt.write(value.`seed`, buf) } } -// This template implements a class for working with a Rust struct via a handle -// to the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque handle to the underlying Rust struct. -// Method calls need to read this handle from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its handle should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the handle, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the handle, but is interrupted -// before it can pass the handle over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read handle value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - /** - * HTTP client for a WebViewBundle remote server. + * How each entry's data checksum is verified when its data is read. */ -public interface RemoteInterface { - - suspend fun `download`(`bundleName`: kotlin.String, `channel`: kotlin.String?): DownloadResult - - suspend fun `downloadVersion`(`bundleName`: kotlin.String, `version`: kotlin.String): DownloadResult - +data class DataReadOptions ( /** - * Fetches metadata for the latest version of `bundle_name` without downloading the bundle. + * How each entry's data checksum is verified. */ - suspend fun `getInfo`(`bundleName`: kotlin.String, `options`: RemoteFetchOptions?): RemoteBundleInfo + var `checksum`: ChecksumReadOptions? = null + +){ + + - suspend fun `listBundles`(`options`: RemoteFetchOptions?): List + companion object } /** - * HTTP client for a WebViewBundle remote server. + * @suppress */ -open class Remote: Disposable, AutoCloseable, RemoteInterface -{ - - @Suppress("UNUSED_PARAMETER") - /** - * @suppress - */ - constructor(withHandle: UniffiWithHandle, handle: Long) { - this.handle = handle - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(handle)) +public object FfiConverterTypeDataReadOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): DataReadOptions { + return DataReadOptions( + FfiConverterOptionalTypeChecksumReadOptions.read(buf), + ) } - /** - * @suppress - * - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noHandle: NoHandle) { - this.handle = 0 - this.cleanable = null + override fun allocationSize(value: DataReadOptions) = ( + FfiConverterOptionalTypeChecksumReadOptions.allocationSize(value.`checksum`) + ) + + override fun write(value: DataReadOptions, buf: ByteBuffer) { + FfiConverterOptionalTypeChecksumReadOptions.write(value.`checksum`, buf) } +} + + + +/** + * How a bundle's header checksum is verified when its header is read. + */ +data class HeaderReadOptions ( /** - * Creates a client for the server at `endpoint` (e.g. `"https://bundles.example.com"`). + * How the header checksum is verified. */ - constructor(`endpoint`: kotlin.String, `options`: RemoteOptions? = null) : - this(UniffiWithHandle, - uniffiRustCallWithError(WebviewBundleException) { _status -> - UniffiLib.uniffi_wvb_ffi_fn_constructor_remote_new( + var `checksum`: ChecksumReadOptions? = null + +){ - FfiConverterString.lower(`endpoint`),FfiConverterOptionalTypeRemoteOptions.lower(`options`),_status) -} - ) - protected val handle: Long - protected val cleanable: UniffiCleaner.Cleanable? + - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) + + companion object +} - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable?.clean() - } - } +/** + * @suppress + */ +public object FfiConverterTypeHeaderReadOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): HeaderReadOptions { + return HeaderReadOptions( + FfiConverterOptionalTypeChecksumReadOptions.read(buf), + ) } - @Synchronized - override fun close() { - this.destroy() - } + override fun allocationSize(value: HeaderReadOptions) = ( + FfiConverterOptionalTypeChecksumReadOptions.allocationSize(value.`checksum`) + ) - internal inline fun callWithHandle(block: (handle: Long) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the handle being freed concurrently. - try { - return block(this.uniffiCloneHandle()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable?.clean() - } - } + override fun write(value: HeaderReadOptions, buf: ByteBuffer) { + FfiConverterOptionalTypeChecksumReadOptions.write(value.`checksum`, buf) } +} - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val handle: Long) : Runnable { - override fun run() { - if (handle == 0.toLong()) { - // Fake object created with `NoHandle`, don't try to free. - return; - } - uniffiRustCall { status -> - UniffiLib.uniffi_wvb_ffi_fn_free_remote(handle, status) - } - } - } + +/** + * HTTP client options + */ +data class HttpOptions ( /** - * @suppress + * Headers sent with every request. */ - fun uniffiCloneHandle(): Long { - if (handle == 0.toLong()) { - throw InternalException("uniffiCloneHandle() called on NoHandle object"); - } - return uniffiRustCall() { status -> - UniffiLib.uniffi_wvb_ffi_fn_clone_remote(handle, status) - } - } - + var `defaultHeaders`: Map? = null + , + /** + * User-Agent sent with requests, when supplied. + */ + var `userAgent`: kotlin.String? = null + , + /** + * Total request timeout in milliseconds (default: `120000`). + * + * Bounds an otherwise-unbounded download: without it a stalled transfer would hang + * forever and keep holding the updater's per-bundle lock. + */ + var `timeout`: kotlin.ULong? = null + , + /** + * Timeout in milliseconds for reading the response body. + */ + var `readTimeout`: kotlin.ULong? = null + , + /** + * Timeout in milliseconds for establishing the connection. + */ + var `connectTimeout`: kotlin.ULong? = null + , + /** + * Idle lifetime in milliseconds for pooled connections. + */ + var `poolIdleTimeout`: kotlin.ULong? = null + , + /** + * Maximum idle connections retained for a host. + */ + var `poolMaxIdlePerHost`: kotlin.UInt? = null + , + /** + * Whether to generate a Referer header on redirects. + */ + var `referer`: kotlin.Boolean? = null + , + /** + * Whether to disable Nagle's algorithm on TCP sockets. + */ + var `tcpNodelay`: kotlin.Boolean? = null + +){ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `download`(`bundleName`: kotlin.String, `channel`: kotlin.String?) : DownloadResult { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_remote_download( - uniffiHandle, - FfiConverterString.lower(`bundleName`),FfiConverterOptionalString.lower(`channel`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterTypeDownloadResult.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) - } - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `downloadVersion`(`bundleName`: kotlin.String, `version`: kotlin.String) : DownloadResult { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_remote_download_version( - uniffiHandle, - FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterTypeDownloadResult.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) - } - /** - * Fetches metadata for the latest version of `bundle_name` without downloading the bundle. - */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `getInfo`(`bundleName`: kotlin.String, `options`: RemoteFetchOptions?) : RemoteBundleInfo { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_remote_get_info( - uniffiHandle, - FfiConverterString.lower(`bundleName`),FfiConverterOptionalTypeRemoteFetchOptions.lower(`options`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterTypeRemoteBundleInfo.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeHttpOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): HttpOptions { + return HttpOptions( + FfiConverterOptionalMapStringString.read(buf), + FfiConverterOptionalString.read(buf), + FfiConverterOptionalULong.read(buf), + FfiConverterOptionalULong.read(buf), + FfiConverterOptionalULong.read(buf), + FfiConverterOptionalULong.read(buf), + FfiConverterOptionalUInt.read(buf), + FfiConverterOptionalBoolean.read(buf), + FfiConverterOptionalBoolean.read(buf), + ) } - - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `listBundles`(`options`: RemoteFetchOptions?) : List { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_remote_list_bundles( - uniffiHandle, - FfiConverterOptionalTypeRemoteFetchOptions.lower(`options`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterSequenceTypeListRemoteBundleInfo.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, + override fun allocationSize(value: HttpOptions) = ( + FfiConverterOptionalMapStringString.allocationSize(value.`defaultHeaders`) + + FfiConverterOptionalString.allocationSize(value.`userAgent`) + + FfiConverterOptionalULong.allocationSize(value.`timeout`) + + FfiConverterOptionalULong.allocationSize(value.`readTimeout`) + + FfiConverterOptionalULong.allocationSize(value.`connectTimeout`) + + FfiConverterOptionalULong.allocationSize(value.`poolIdleTimeout`) + + FfiConverterOptionalUInt.allocationSize(value.`poolMaxIdlePerHost`) + + FfiConverterOptionalBoolean.allocationSize(value.`referer`) + + FfiConverterOptionalBoolean.allocationSize(value.`tcpNodelay`) ) + + override fun write(value: HttpOptions, buf: ByteBuffer) { + FfiConverterOptionalMapStringString.write(value.`defaultHeaders`, buf) + FfiConverterOptionalString.write(value.`userAgent`, buf) + FfiConverterOptionalULong.write(value.`timeout`, buf) + FfiConverterOptionalULong.write(value.`readTimeout`, buf) + FfiConverterOptionalULong.write(value.`connectTimeout`, buf) + FfiConverterOptionalULong.write(value.`poolIdleTimeout`, buf) + FfiConverterOptionalUInt.write(value.`poolMaxIdlePerHost`, buf) + FfiConverterOptionalBoolean.write(value.`referer`, buf) + FfiConverterOptionalBoolean.write(value.`tcpNodelay`, buf) } +} + + +/** + * HTTP response returned by protocol handlers. + */ +data class HttpResponse ( + var `status`: kotlin.UShort + , + var `headers`: Map + , + var `body`: kotlin.ByteArray - +){ - + - /** - * @suppress - */ companion object - } - /** * @suppress */ -public object FfiConverterTypeRemote: FfiConverter { - override fun lower(value: Remote): Long { - return value.uniffiCloneHandle() - } - - override fun lift(value: Long): Remote { - return Remote(UniffiWithHandle, value) - } - - override fun read(buf: ByteBuffer): Remote { - return lift(buf.getLong()) +public object FfiConverterTypeHttpResponse: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): HttpResponse { + return HttpResponse( + FfiConverterUShort.read(buf), + FfiConverterMapStringString.read(buf), + FfiConverterByteArray.read(buf), + ) } - override fun allocationSize(value: Remote) = 8UL + override fun allocationSize(value: HttpResponse) = ( + FfiConverterUShort.allocationSize(value.`status`) + + FfiConverterMapStringString.allocationSize(value.`headers`) + + FfiConverterByteArray.allocationSize(value.`body`) + ) - override fun write(value: Remote, buf: ByteBuffer) { - buf.putLong(lower(value)) + override fun write(value: HttpResponse, buf: ByteBuffer) { + FfiConverterUShort.write(value.`status`, buf) + FfiConverterMapStringString.write(value.`headers`, buf) + FfiConverterByteArray.write(value.`body`, buf) } } -// This template implements a class for working with a Rust struct via a handle -// to the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque handle to the underlying Rust struct. -// Method calls need to read this handle from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its handle should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the handle, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the handle, but is interrupted -// before it can pass the handle over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read handle value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - /** - * A callback invoked with download progress as a bundle downloads. + * Metadata for a single entry stored in the bundle index. + * + * `offset` and `len` refer to the entry's position in the data section. + * `content_length` is the logical (uncompressed) size exposed to HTTP clients, + * which may differ from `len` if the data is stored compressed. */ -public interface RemoteOnDownload { +data class IndexEntry ( + var `offset`: kotlin.ULong + , + var `len`: kotlin.ULong + , + var `isEmpty`: kotlin.Boolean + , + var `contentType`: kotlin.String + , + var `contentLength`: kotlin.ULong + , + var `headers`: Map - fun `onDownload`(`data`: RemoteOnDownloadData) +){ + + + + companion object } /** - * A callback invoked with download progress as a bundle downloads. + * @suppress */ -open class RemoteOnDownloadImpl: Disposable, AutoCloseable, RemoteOnDownload -{ - - @Suppress("UNUSED_PARAMETER") - /** - * @suppress - */ - constructor(withHandle: UniffiWithHandle, handle: Long) { - this.handle = handle - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(handle)) - } - - /** - * @suppress - * - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noHandle: NoHandle) { - this.handle = 0 - this.cleanable = null - } - - protected val handle: Long - protected val cleanable: UniffiCleaner.Cleanable? - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable?.clean() - } - } +public object FfiConverterTypeIndexEntry: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): IndexEntry { + return IndexEntry( + FfiConverterULong.read(buf), + FfiConverterULong.read(buf), + FfiConverterBoolean.read(buf), + FfiConverterString.read(buf), + FfiConverterULong.read(buf), + FfiConverterMapStringString.read(buf), + ) } - @Synchronized - override fun close() { - this.destroy() - } + override fun allocationSize(value: IndexEntry) = ( + FfiConverterULong.allocationSize(value.`offset`) + + FfiConverterULong.allocationSize(value.`len`) + + FfiConverterBoolean.allocationSize(value.`isEmpty`) + + FfiConverterString.allocationSize(value.`contentType`) + + FfiConverterULong.allocationSize(value.`contentLength`) + + FfiConverterMapStringString.allocationSize(value.`headers`) + ) - internal inline fun callWithHandle(block: (handle: Long) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the handle being freed concurrently. - try { - return block(this.uniffiCloneHandle()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable?.clean() - } - } + override fun write(value: IndexEntry, buf: ByteBuffer) { + FfiConverterULong.write(value.`offset`, buf) + FfiConverterULong.write(value.`len`, buf) + FfiConverterBoolean.write(value.`isEmpty`, buf) + FfiConverterString.write(value.`contentType`, buf) + FfiConverterULong.write(value.`contentLength`, buf) + FfiConverterMapStringString.write(value.`headers`, buf) } +} + - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val handle: Long) : Runnable { - override fun run() { - if (handle == 0.toLong()) { - // Fake object created with `NoHandle`, don't try to free. - return; - } - uniffiRustCall { status -> - UniffiLib.uniffi_wvb_ffi_fn_free_remoteondownload(handle, status) - } - } - } +/** + * How a bundle's index checksum is verified when its index is read. + */ +data class IndexReadOptions ( /** - * @suppress + * How the index checksum is verified. */ - fun uniffiCloneHandle(): Long { - if (handle == 0.toLong()) { - throw InternalException("uniffiCloneHandle() called on NoHandle object"); - } - return uniffiRustCall() { status -> - UniffiLib.uniffi_wvb_ffi_fn_clone_remoteondownload(handle, status) - } - } - - override fun `onDownload`(`data`: RemoteOnDownloadData) - = - callWithHandle { - uniffiRustCall() { _status -> - UniffiLib.uniffi_wvb_ffi_fn_method_remoteondownload_on_download( - it, - FfiConverterTypeRemoteOnDownloadData.lower(`data`),_status) -} - } - + var `checksum`: ChecksumReadOptions? = null - +){ - - - /** - * @suppress - */ companion object - } +/** + * @suppress + */ +public object FfiConverterTypeIndexReadOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): IndexReadOptions { + return IndexReadOptions( + FfiConverterOptionalTypeChecksumReadOptions.read(buf), + ) + } + override fun allocationSize(value: IndexReadOptions) = ( + FfiConverterOptionalTypeChecksumReadOptions.allocationSize(value.`checksum`) + ) -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceRemoteOnDownload { - internal object `onDownload`: UniffiCallbackInterfaceRemoteOnDownloadMethod0 { - override fun callback(`uniffiHandle`: Long,`data`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) { - val uniffiObj = FfiConverterTypeRemoteOnDownload.handleMap.get(uniffiHandle) - val makeCall = { -> - uniffiObj.`onDownload`( - FfiConverterTypeRemoteOnDownloadData.lift(`data`), - ) - } - val writeReturn = { _: Unit -> Unit } - uniffiTraitInterfaceCall(uniffiCallStatus, makeCall, writeReturn) - } + override fun write(value: IndexReadOptions, buf: ByteBuffer) { + FfiConverterOptionalTypeChecksumReadOptions.write(value.`checksum`, buf) } +} - internal object uniffiFree: UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeRemoteOnDownload.handleMap.remove(handle) - } - } - internal object uniffiClone: UniffiCallbackInterfaceClone { - override fun callback(handle: Long): Long { - return FfiConverterTypeRemoteOnDownload.handleMap.clone(handle) - } - } - internal var vtable = UniffiVTableCallbackInterfaceRemoteOnDownload.UniffiByValue( - uniffiFree, - uniffiClone, - `onDownload`, - ) +data class ManifestBundleItem ( + var `name`: kotlin.String + , + var `version`: kotlin.String + , + var `status`: ManifestBundleItemStatus + , + var `data`: ManifestVersionData + +){ + - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_wvb_ffi_fn_init_callback_vtable_remoteondownload(vtable) - } + + + + companion object } /** * @suppress */ -public object FfiConverterTypeRemoteOnDownload: FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: RemoteOnDownload): Long { - if (value is RemoteOnDownloadImpl) { - // Rust-implemented object. Clone the handle and return it - return value.uniffiCloneHandle() - } else { - // Kotlin object, generate a new vtable handle and return that. - return handleMap.insert(value) - } +public object FfiConverterTypeManifestBundleItem: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ManifestBundleItem { + return ManifestBundleItem( + FfiConverterString.read(buf), + FfiConverterString.read(buf), + FfiConverterTypeManifestBundleItemStatus.read(buf), + FfiConverterTypeManifestVersionData.read(buf), + ) } - override fun lift(value: Long): RemoteOnDownload { - if ((value and 1.toLong()) == 0.toLong()) { - // Rust-generated handle, construct a new class that uses the handle to implement the - // interface - return RemoteOnDownloadImpl(UniffiWithHandle, value) - } else { - // Kotlin-generated handle, get the object from the handle map - return handleMap.remove(value) - } - } + override fun allocationSize(value: ManifestBundleItem) = ( + FfiConverterString.allocationSize(value.`name`) + + FfiConverterString.allocationSize(value.`version`) + + FfiConverterTypeManifestBundleItemStatus.allocationSize(value.`status`) + + FfiConverterTypeManifestVersionData.allocationSize(value.`data`) + ) - override fun read(buf: ByteBuffer): RemoteOnDownload { - return lift(buf.getLong()) + override fun write(value: ManifestBundleItem, buf: ByteBuffer) { + FfiConverterString.write(value.`name`, buf) + FfiConverterString.write(value.`version`, buf) + FfiConverterTypeManifestBundleItemStatus.write(value.`status`, buf) + FfiConverterTypeManifestVersionData.write(value.`data`, buf) } +} - override fun allocationSize(value: RemoteOnDownload) = 8UL - override fun write(value: RemoteOnDownload, buf: ByteBuffer) { - buf.putLong(lower(value)) - } + +data class ManifestPruneResult ( + var `name`: kotlin.String + , + var `prunedVersions`: List + +){ + + + + + + companion object } +/** + * @suppress + */ +public object FfiConverterTypeManifestPruneResult: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ManifestPruneResult { + return ManifestPruneResult( + FfiConverterString.read(buf), + FfiConverterSequenceString.read(buf), + ) + } -// This template implements a class for working with a Rust struct via a handle -// to the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque handle to the underlying Rust struct. -// Method calls need to read this handle from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its handle should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the handle, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the handle, but is interrupted -// before it can pass the handle over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read handle value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// + override fun allocationSize(value: ManifestPruneResult) = ( + FfiConverterString.allocationSize(value.`name`) + + FfiConverterSequenceString.allocationSize(value.`prunedVersions`) + ) + + override fun write(value: ManifestPruneResult, buf: ByteBuffer) { + FfiConverterString.write(value.`name`, buf) + FfiConverterSequenceString.write(value.`prunedVersions`, buf) + } +} + + + +data class ManifestRemoveData ( + var `versions`: List + , + var `force`: kotlin.Boolean? = null + +){ + + + + + companion object +} /** - * A custom function that verifies a bundle's signature. + * @suppress */ -public interface SignatureVerify { +public object FfiConverterTypeManifestRemoveData: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ManifestRemoveData { + return ManifestRemoveData( + FfiConverterSequenceString.read(buf), + FfiConverterOptionalBoolean.read(buf), + ) + } + + override fun allocationSize(value: ManifestRemoveData) = ( + FfiConverterSequenceString.allocationSize(value.`versions`) + + FfiConverterOptionalBoolean.allocationSize(value.`force`) + ) + + override fun write(value: ManifestRemoveData, buf: ByteBuffer) { + FfiConverterSequenceString.write(value.`versions`, buf) + FfiConverterOptionalBoolean.write(value.`force`, buf) + } +} + + + +data class ManifestRemoveResult ( + var `name`: kotlin.String + , + var `version`: kotlin.String + , + var `kind`: ManifestRemoveResultKind - suspend fun `verify`(`message`: kotlin.ByteArray, `signature`: kotlin.String): kotlin.Boolean +){ + + + + companion object } /** - * A custom function that verifies a bundle's signature. + * @suppress */ -open class SignatureVerifyImpl: Disposable, AutoCloseable, SignatureVerify -{ - - @Suppress("UNUSED_PARAMETER") - /** - * @suppress - */ - constructor(withHandle: UniffiWithHandle, handle: Long) { - this.handle = handle - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(handle)) +public object FfiConverterTypeManifestRemoveResult: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ManifestRemoveResult { + return ManifestRemoveResult( + FfiConverterString.read(buf), + FfiConverterString.read(buf), + FfiConverterTypeManifestRemoveResultKind.read(buf), + ) } - /** - * @suppress - * - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noHandle: NoHandle) { - this.handle = 0 - this.cleanable = null + override fun allocationSize(value: ManifestRemoveResult) = ( + FfiConverterString.allocationSize(value.`name`) + + FfiConverterString.allocationSize(value.`version`) + + FfiConverterTypeManifestRemoveResultKind.allocationSize(value.`kind`) + ) + + override fun write(value: ManifestRemoveResult, buf: ByteBuffer) { + FfiConverterString.write(value.`name`, buf) + FfiConverterString.write(value.`version`, buf) + FfiConverterTypeManifestRemoveResultKind.write(value.`kind`, buf) } +} - protected val handle: Long - protected val cleanable: UniffiCleaner.Cleanable? - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable?.clean() - } - } +data class ManifestSetCurrentVersionResult ( + var `name`: kotlin.String + , + var `version`: kotlin.String + , + var `kind`: ManifestSetCurrentVersionResultKind + +){ + + + + + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeManifestSetCurrentVersionResult: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ManifestSetCurrentVersionResult { + return ManifestSetCurrentVersionResult( + FfiConverterString.read(buf), + FfiConverterString.read(buf), + FfiConverterTypeManifestSetCurrentVersionResultKind.read(buf), + ) } - @Synchronized - override fun close() { - this.destroy() + override fun allocationSize(value: ManifestSetCurrentVersionResult) = ( + FfiConverterString.allocationSize(value.`name`) + + FfiConverterString.allocationSize(value.`version`) + + FfiConverterTypeManifestSetCurrentVersionResultKind.allocationSize(value.`kind`) + ) + + override fun write(value: ManifestSetCurrentVersionResult, buf: ByteBuffer) { + FfiConverterString.write(value.`name`, buf) + FfiConverterString.write(value.`version`, buf) + FfiConverterTypeManifestSetCurrentVersionResultKind.write(value.`kind`, buf) } +} - internal inline fun callWithHandle(block: (handle: Long) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the handle being freed concurrently. - try { - return block(this.uniffiCloneHandle()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable?.clean() - } - } + + +data class ManifestStageData ( + var `version`: kotlin.String + , + var `data`: ManifestVersionData? + +){ + + + + + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeManifestStageData: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ManifestStageData { + return ManifestStageData( + FfiConverterString.read(buf), + FfiConverterOptionalTypeManifestVersionData.read(buf), + ) } - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val handle: Long) : Runnable { - override fun run() { - if (handle == 0.toLong()) { - // Fake object created with `NoHandle`, don't try to free. - return; - } - uniffiRustCall { status -> - UniffiLib.uniffi_wvb_ffi_fn_free_signatureverify(handle, status) - } - } + override fun allocationSize(value: ManifestStageData) = ( + FfiConverterString.allocationSize(value.`version`) + + FfiConverterOptionalTypeManifestVersionData.allocationSize(value.`data`) + ) + + override fun write(value: ManifestStageData, buf: ByteBuffer) { + FfiConverterString.write(value.`version`, buf) + FfiConverterOptionalTypeManifestVersionData.write(value.`data`, buf) } +} - /** - * @suppress - */ - fun uniffiCloneHandle(): Long { - if (handle == 0.toLong()) { - throw InternalException("uniffiCloneHandle() called on NoHandle object"); - } - return uniffiRustCall() { status -> - UniffiLib.uniffi_wvb_ffi_fn_clone_signatureverify(handle, status) - } + + +data class ManifestStageResult ( + var `name`: kotlin.String + , + var `version`: kotlin.String + , + var `kind`: ManifestStageResultKind + +){ + + + + + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeManifestStageResult: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ManifestStageResult { + return ManifestStageResult( + FfiConverterString.read(buf), + FfiConverterString.read(buf), + FfiConverterTypeManifestStageResultKind.read(buf), + ) + } + + override fun allocationSize(value: ManifestStageResult) = ( + FfiConverterString.allocationSize(value.`name`) + + FfiConverterString.allocationSize(value.`version`) + + FfiConverterTypeManifestStageResultKind.allocationSize(value.`kind`) + ) + + override fun write(value: ManifestStageResult, buf: ByteBuffer) { + FfiConverterString.write(value.`name`, buf) + FfiConverterString.write(value.`version`, buf) + FfiConverterTypeManifestStageResultKind.write(value.`kind`, buf) } +} + + +data class ManifestVersionData ( + var `integrity`: kotlin.String? + , + var `metadata`: Map? - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `verify`(`message`: kotlin.ByteArray, `signature`: kotlin.String) : kotlin.Boolean { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_signatureverify_verify( - uniffiHandle, - FfiConverterByteArray.lower(`message`),FfiConverterString.lower(`signature`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_i8(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_i8(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_i8(future) }, - // lift function - { FfiConverterBoolean.lift(it) }, - // Error FFI converter - UniffiNullRustCallStatusErrorHandler, +){ + + + + + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeManifestVersionData: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ManifestVersionData { + return ManifestVersionData( + FfiConverterOptionalString.read(buf), + FfiConverterOptionalMapStringString.read(buf), + ) + } + + override fun allocationSize(value: ManifestVersionData) = ( + FfiConverterOptionalString.allocationSize(value.`integrity`) + + FfiConverterOptionalMapStringString.allocationSize(value.`metadata`) ) + + override fun write(value: ManifestVersionData, buf: ByteBuffer) { + FfiConverterOptionalString.write(value.`integrity`, buf) + FfiConverterOptionalMapStringString.write(value.`metadata`, buf) } +} + + +/** + * Options used to request the current update document. + */ +data class RemoteGetUpdateOptions ( + /** + * Entity tag of the previously received update document. + */ + var `etag`: kotlin.String? = null + , + /** + * Release channel to request. + */ + var `channel`: kotlin.String? = null + , + /** + * Public key that must sign the response. + */ + var `expectSignature`: SignatureVerifyKey? = null - +): Disposable{ + + @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here + override fun destroy() { + + Disposable.destroy( + this.`etag`, + this.`channel`, + this.`expectSignature` + ) + } - /** - * @suppress - */ companion object - } +/** + * @suppress + */ +public object FfiConverterTypeRemoteGetUpdateOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): RemoteGetUpdateOptions { + return RemoteGetUpdateOptions( + FfiConverterOptionalString.read(buf), + FfiConverterOptionalString.read(buf), + FfiConverterOptionalTypeSignatureVerifyKey.read(buf), + ) + } + override fun allocationSize(value: RemoteGetUpdateOptions) = ( + FfiConverterOptionalString.allocationSize(value.`etag`) + + FfiConverterOptionalString.allocationSize(value.`channel`) + + FfiConverterOptionalTypeSignatureVerifyKey.allocationSize(value.`expectSignature`) + ) -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceSignatureVerify { - internal object `verify`: UniffiCallbackInterfaceSignatureVerifyMethod0 { - override fun callback(`uniffiHandle`: Long,`message`: RustBuffer.ByValue,`signature`: RustBuffer.ByValue,`uniffiFutureCallback`: UniffiForeignFutureCompleteI8,`uniffiCallbackData`: Long,`uniffiOutDroppedCallback`: UniffiForeignFutureDroppedCallbackStruct,) { - val uniffiObj = FfiConverterTypeSignatureVerify.handleMap.get(uniffiHandle) - val makeCall = suspend { -> - uniffiObj.`verify`( - FfiConverterByteArray.lift(`message`), - FfiConverterString.lift(`signature`), - ) - } - val uniffiHandleSuccess = { returnValue: kotlin.Boolean -> - val uniffiResult = UniffiForeignFutureResultI8.UniffiByValue( - FfiConverterBoolean.lower(returnValue), - UniffiRustCallStatus.ByValue() - ) - uniffiResult.write() - uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) - } - val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> - uniffiFutureCallback.callback( - uniffiCallbackData, - UniffiForeignFutureResultI8.UniffiByValue( - 0.toByte(), - callStatus, - ), - ) - } - uniffiTraitInterfaceCallAsync( - makeCall, - uniffiHandleSuccess, - uniffiHandleError, - uniffiOutDroppedCallback - ) - } + override fun write(value: RemoteGetUpdateOptions, buf: ByteBuffer) { + FfiConverterOptionalString.write(value.`etag`, buf) + FfiConverterOptionalString.write(value.`channel`, buf) + FfiConverterOptionalTypeSignatureVerifyKey.write(value.`expectSignature`, buf) } +} - internal object uniffiFree: UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeSignatureVerify.handleMap.remove(handle) - } - } - internal object uniffiClone: UniffiCallbackInterfaceClone { - override fun callback(handle: Long): Long { - return FfiConverterTypeSignatureVerify.handleMap.clone(handle) - } - } - internal var vtable = UniffiVTableCallbackInterfaceSignatureVerify.UniffiByValue( - uniffiFree, - uniffiClone, - `verify`, - ) +/** + * Progress reported while a bundle is downloading. + */ +data class RemoteOnDownloadData ( + /** + * Bytes downloaded so far. + */ + var `downloadedBytes`: kotlin.ULong + , + /** + * Total bytes to download, when the server advertised a content length. + */ + var `totalBytes`: kotlin.ULong? + , + /** + * The endpoint the bundle is being downloaded from. + */ + var `url`: kotlin.String + +){ + - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_wvb_ffi_fn_init_callback_vtable_signatureverify(vtable) - } + + + + companion object } /** * @suppress */ -public object FfiConverterTypeSignatureVerify: FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: SignatureVerify): Long { - if (value is SignatureVerifyImpl) { - // Rust-implemented object. Clone the handle and return it - return value.uniffiCloneHandle() - } else { - // Kotlin object, generate a new vtable handle and return that. - return handleMap.insert(value) - } +public object FfiConverterTypeRemoteOnDownloadData: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): RemoteOnDownloadData { + return RemoteOnDownloadData( + FfiConverterULong.read(buf), + FfiConverterOptionalULong.read(buf), + FfiConverterString.read(buf), + ) } - override fun lift(value: Long): SignatureVerify { - if ((value and 1.toLong()) == 0.toLong()) { - // Rust-generated handle, construct a new class that uses the handle to implement the - // interface - return SignatureVerifyImpl(UniffiWithHandle, value) - } else { - // Kotlin-generated handle, get the object from the handle map - return handleMap.remove(value) - } - } + override fun allocationSize(value: RemoteOnDownloadData) = ( + FfiConverterULong.allocationSize(value.`downloadedBytes`) + + FfiConverterOptionalULong.allocationSize(value.`totalBytes`) + + FfiConverterString.allocationSize(value.`url`) + ) - override fun read(buf: ByteBuffer): SignatureVerify { - return lift(buf.getLong()) + override fun write(value: RemoteOnDownloadData, buf: ByteBuffer) { + FfiConverterULong.write(value.`downloadedBytes`, buf) + FfiConverterOptionalULong.write(value.`totalBytes`, buf) + FfiConverterString.write(value.`url`, buf) } +} - override fun allocationSize(value: SignatureVerify) = 8UL - override fun write(value: SignatureVerify, buf: ByteBuffer) { - buf.putLong(lower(value)) - } -} +/** + * Options for creating a [`Remote`] client. + */ +data class RemoteOptions ( + /** + * HTTP client options. + */ + var `http`: HttpOptions? = null + , + /** + * Download progress callback. + */ + var `onDownload`: RemoteOnDownload? = null + +): Disposable{ + + + + + + @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here + override fun destroy() { + + Disposable.destroy( + this.`http`, + this.`onDownload` + ) + } + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeRemoteOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): RemoteOptions { + return RemoteOptions( + FfiConverterOptionalTypeHttpOptions.read(buf), + FfiConverterOptionalTypeRemoteOnDownload.read(buf), + ) + } + + override fun allocationSize(value: RemoteOptions) = ( + FfiConverterOptionalTypeHttpOptions.allocationSize(value.`http`) + + FfiConverterOptionalTypeRemoteOnDownload.allocationSize(value.`onDownload`) + ) + + override fun write(value: RemoteOptions, buf: ByteBuffer) { + FfiConverterOptionalTypeHttpOptions.write(value.`http`, buf) + FfiConverterOptionalTypeRemoteOnDownload.write(value.`onDownload`, buf) + } +} -// This template implements a class for working with a Rust struct via a handle -// to the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque handle to the underlying Rust struct. -// Method calls need to read this handle from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its handle should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the handle, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the handle, but is interrupted -// before it can pass the handle over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read handle value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// /** - * Orchestrates the full update cycle: checks for a new version on the remote, - * downloads it, verifies integrity/signature, and writes it to the local source. + * A successful response from [`Remote::get_update`]. */ -public interface UpdaterInterface { - +data class RemoteUpdateResponse ( /** - * Downloads and persists an update for `bundle_name`. - * Uses the latest remote version when `version` is `None`. + * Parsed update document. */ - suspend fun `downloadUpdate`(`bundleName`: kotlin.String, `version`: kotlin.String?): RemoteBundleInfo - + var `update`: Update + , /** - * Checks whether a newer version of `bundle_name` is available on the remote. - * Does not download the bundle. + * HTTP entity tag supplied by the remote server. */ - suspend fun `getUpdate`(`bundleName`: kotlin.String): BundleUpdateInfo - + var `etag`: kotlin.String? + , /** - * Activates a previously downloaded bundle version. - * - * The version must already be staged in the remote source (via - * [`download_update`](Updater::download_update)). When integrity/signature - * verification is configured, the staged bundle is verified before activation. - * On success the current version is updated, the cached descriptor is dropped, - * and stale staged versions are pruned. + * Signature metadata supplied by the remote server. */ - suspend fun `install`(`bundleName`: kotlin.String, `version`: kotlin.String) + var `signature`: UpdateSignature? + +){ + + - suspend fun `listRemotes`(): List + companion object } /** - * Orchestrates the full update cycle: checks for a new version on the remote, - * downloads it, verifies integrity/signature, and writes it to the local source. + * @suppress */ -open class Updater: Disposable, AutoCloseable, UpdaterInterface -{ - - @Suppress("UNUSED_PARAMETER") - /** - * @suppress - */ - constructor(withHandle: UniffiWithHandle, handle: Long) { - this.handle = handle - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(handle)) +public object FfiConverterTypeRemoteUpdateResponse: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): RemoteUpdateResponse { + return RemoteUpdateResponse( + FfiConverterTypeUpdate.read(buf), + FfiConverterOptionalString.read(buf), + FfiConverterOptionalTypeUpdateSignature.read(buf), + ) } - /** - * @suppress - * - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noHandle: NoHandle) { - this.handle = 0 - this.cleanable = null + override fun allocationSize(value: RemoteUpdateResponse) = ( + FfiConverterTypeUpdate.allocationSize(value.`update`) + + FfiConverterOptionalString.allocationSize(value.`etag`) + + FfiConverterOptionalTypeUpdateSignature.allocationSize(value.`signature`) + ) + + override fun write(value: RemoteUpdateResponse, buf: ByteBuffer) { + FfiConverterTypeUpdate.write(value.`update`, buf) + FfiConverterOptionalString.write(value.`etag`, buf) + FfiConverterOptionalTypeUpdateSignature.write(value.`signature`, buf) } - constructor(`source`: BundleSource, `remote`: Remote, `options`: UpdaterOptions?) : - this(UniffiWithHandle, - uniffiRustCallWithError(WebviewBundleException) { _status -> - UniffiLib.uniffi_wvb_ffi_fn_constructor_updater_new( - - FfiConverterTypeBundleSource.lower(`source`),FfiConverterTypeRemote.lower(`remote`),FfiConverterOptionalTypeUpdaterOptions.lower(`options`),_status) } - ) - protected val handle: Long - protected val cleanable: UniffiCleaner.Cleanable? - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) +data class SignatureVerifyKey ( + var `id`: kotlin.String? + , + var `verify`: SignatureVerify + +): Disposable{ + + + + + + @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable?.clean() - } - } + + Disposable.destroy( + this.`id`, + this.`verify` + ) } + + companion object +} - @Synchronized - override fun close() { - this.destroy() +/** + * @suppress + */ +public object FfiConverterTypeSignatureVerifyKey: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): SignatureVerifyKey { + return SignatureVerifyKey( + FfiConverterOptionalString.read(buf), + FfiConverterTypeSignatureVerify.read(buf), + ) } - internal inline fun callWithHandle(block: (handle: Long) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the handle being freed concurrently. - try { - return block(this.uniffiCloneHandle()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable?.clean() - } - } - } + override fun allocationSize(value: SignatureVerifyKey) = ( + FfiConverterOptionalString.allocationSize(value.`id`) + + FfiConverterTypeSignatureVerify.allocationSize(value.`verify`) + ) - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val handle: Long) : Runnable { - override fun run() { - if (handle == 0.toLong()) { - // Fake object created with `NoHandle`, don't try to free. - return; - } - uniffiRustCall { status -> - UniffiLib.uniffi_wvb_ffi_fn_free_updater(handle, status) - } - } + override fun write(value: SignatureVerifyKey, buf: ByteBuffer) { + FfiConverterOptionalString.write(value.`id`, buf) + FfiConverterTypeSignatureVerify.write(value.`verify`, buf) } +} + + +data class SourceConfig ( + var `builtinDir`: kotlin.String + , + var `remoteDir`: kotlin.String + , + var `builtinManifestFilepath`: kotlin.String? = null + , + var `remoteManifestFilepath`: kotlin.String? = null + , /** - * @suppress + * How bundles are checked against their manifest integrity metadata on load. */ - fun uniffiCloneHandle(): Long { - if (handle == 0.toLong()) { - throw InternalException("uniffiCloneHandle() called on NoHandle object"); - } - return uniffiRustCall() { status -> - UniffiLib.uniffi_wvb_ffi_fn_clone_updater(handle, status) - } - } - - + var `integrity`: SourceIntegrityOptions? = null + , /** - * Downloads and persists an update for `bundle_name`. - * Uses the latest remote version when `version` is `None`. + * How each entry's checksum is verified when its data is read. */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `downloadUpdate`(`bundleName`: kotlin.String, `version`: kotlin.String?) : RemoteBundleInfo { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_updater_download_update( - uniffiHandle, - FfiConverterString.lower(`bundleName`),FfiConverterOptionalString.lower(`version`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterTypeRemoteBundleInfo.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) - } - - + var `dataReadOptions`: DataReadOptions? = null + , /** - * Checks whether a newer version of `bundle_name` is available on the remote. - * Does not download the bundle. + * How a bundle's header checksum is verified when its descriptor is read on load. */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `getUpdate`(`bundleName`: kotlin.String) : BundleUpdateInfo { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_updater_get_update( - uniffiHandle, - FfiConverterString.lower(`bundleName`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterTypeBundleUpdateInfo.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, + var `headerReadOptions`: HeaderReadOptions? = null + , + /** + * How a bundle's index checksum is verified when its descriptor is read on load. + */ + var `indexReadOptions`: IndexReadOptions? = null + , + var `removeBundleChunkSize`: kotlin.UInt? = null + +){ + + + + + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeSourceConfig: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): SourceConfig { + return SourceConfig( + FfiConverterString.read(buf), + FfiConverterString.read(buf), + FfiConverterOptionalString.read(buf), + FfiConverterOptionalString.read(buf), + FfiConverterOptionalTypeSourceIntegrityOptions.read(buf), + FfiConverterOptionalTypeDataReadOptions.read(buf), + FfiConverterOptionalTypeHeaderReadOptions.read(buf), + FfiConverterOptionalTypeIndexReadOptions.read(buf), + FfiConverterOptionalUInt.read(buf), + ) + } + + override fun allocationSize(value: SourceConfig) = ( + FfiConverterString.allocationSize(value.`builtinDir`) + + FfiConverterString.allocationSize(value.`remoteDir`) + + FfiConverterOptionalString.allocationSize(value.`builtinManifestFilepath`) + + FfiConverterOptionalString.allocationSize(value.`remoteManifestFilepath`) + + FfiConverterOptionalTypeSourceIntegrityOptions.allocationSize(value.`integrity`) + + FfiConverterOptionalTypeDataReadOptions.allocationSize(value.`dataReadOptions`) + + FfiConverterOptionalTypeHeaderReadOptions.allocationSize(value.`headerReadOptions`) + + FfiConverterOptionalTypeIndexReadOptions.allocationSize(value.`indexReadOptions`) + + FfiConverterOptionalUInt.allocationSize(value.`removeBundleChunkSize`) ) + + override fun write(value: SourceConfig, buf: ByteBuffer) { + FfiConverterString.write(value.`builtinDir`, buf) + FfiConverterString.write(value.`remoteDir`, buf) + FfiConverterOptionalString.write(value.`builtinManifestFilepath`, buf) + FfiConverterOptionalString.write(value.`remoteManifestFilepath`, buf) + FfiConverterOptionalTypeSourceIntegrityOptions.write(value.`integrity`, buf) + FfiConverterOptionalTypeDataReadOptions.write(value.`dataReadOptions`, buf) + FfiConverterOptionalTypeHeaderReadOptions.write(value.`headerReadOptions`, buf) + FfiConverterOptionalTypeIndexReadOptions.write(value.`indexReadOptions`, buf) + FfiConverterOptionalUInt.write(value.`removeBundleChunkSize`, buf) } +} - + + +/** + * How bundles are checked against the integrity recorded for them in the manifest when + * they are loaded from disk. + */ +data class SourceIntegrityOptions ( /** - * Activates a previously downloaded bundle version. + * How a bundle's integrity metadata is treated (default: [`IntegrityPolicy::Optional`]). * - * The version must already be staged in the remote source (via - * [`download_update`](Updater::download_update)). When integrity/signature - * verification is configured, the staged bundle is verified before activation. - * On success the current version is updated, the cached descriptor is dropped, - * and stale staged versions are pruned. + * [`IntegrityPolicy::Off`] disables the integrity check entirely. */ - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `install`(`bundleName`: kotlin.String, `version`: kotlin.String) { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_updater_install( - uniffiHandle, - FfiConverterString.lower(`bundleName`),FfiConverterString.lower(`version`), - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) - } - + var `policy`: IntegrityPolicy? = null + , + var `checkMode`: SourceIntegrityCheckMode? = null - @Throws(WebviewBundleException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `listRemotes`() : List { - return uniffiRustCallAsync( - callWithHandle { uniffiHandle -> - UniffiLib.uniffi_wvb_ffi_fn_method_updater_list_remotes( - uniffiHandle, - - ) - }, - { future, callback, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_poll_rust_buffer(future, callback, continuation) }, - { future, continuation -> UniffiLib.ffi_wvb_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.ffi_wvb_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterSequenceTypeListRemoteBundleInfo.lift(it) }, - // Error FFI converter - WebviewBundleException.ErrorHandler, - ) - } - +){ - - - /** - * @suppress - */ companion object - } - /** * @suppress */ -public object FfiConverterTypeUpdater: FfiConverter { - override fun lower(value: Updater): Long { - return value.uniffiCloneHandle() +public object FfiConverterTypeSourceIntegrityOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): SourceIntegrityOptions { + return SourceIntegrityOptions( + FfiConverterOptionalTypeIntegrityPolicy.read(buf), + FfiConverterOptionalTypeSourceIntegrityCheckMode.read(buf), + ) } - override fun lift(value: Long): Updater { - return Updater(UniffiWithHandle, value) + override fun allocationSize(value: SourceIntegrityOptions) = ( + FfiConverterOptionalTypeIntegrityPolicy.allocationSize(value.`policy`) + + FfiConverterOptionalTypeSourceIntegrityCheckMode.allocationSize(value.`checkMode`) + ) + + override fun write(value: SourceIntegrityOptions, buf: ByteBuffer) { + FfiConverterOptionalTypeIntegrityPolicy.write(value.`policy`, buf) + FfiConverterOptionalTypeSourceIntegrityCheckMode.write(value.`checkMode`, buf) } +} - override fun read(buf: ByteBuffer): Updater { - return lift(buf.getLong()) + + +data class SourceListItem ( + var `source`: SourceKind + , + var `item`: ManifestBundleItem + +){ + + + + + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeSourceListItem: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): SourceListItem { + return SourceListItem( + FfiConverterTypeSourceKind.read(buf), + FfiConverterTypeManifestBundleItem.read(buf), + ) } - override fun allocationSize(value: Updater) = 8UL + override fun allocationSize(value: SourceListItem) = ( + FfiConverterTypeSourceKind.allocationSize(value.`source`) + + FfiConverterTypeManifestBundleItem.allocationSize(value.`item`) + ) - override fun write(value: Updater, buf: ByteBuffer) { - buf.putLong(lower(value)) + override fun write(value: SourceListItem, buf: ByteBuffer) { + FfiConverterTypeSourceKind.write(value.`source`, buf) + FfiConverterTypeManifestBundleItem.write(value.`item`, buf) } } /** - * Options for the bundle header section. + * An atomically published set of bundle updates. */ -data class BuildHeaderOptions ( - var `checksum`: ChecksumWriteOptions? = null +data class Update ( + /** + * Unique update identifier. + */ + var `id`: kotlin.String + , + /** + * ISO 8601 publication time. + */ + var `createdAt`: kotlin.String + , + /** + * Update-model version required to process this document. + */ + var `runtimeVersion`: kotlin.UByte + , + /** + * Bundles included in this update. + */ + var `bundles`: List + , + /** + * Provider-defined, string-valued update metadata. + */ + var `metadata`: Map? ){ @@ -7798,29 +9329,54 @@ data class BuildHeaderOptions ( /** * @suppress */ -public object FfiConverterTypeBuildHeaderOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BuildHeaderOptions { - return BuildHeaderOptions( - FfiConverterOptionalTypeChecksumWriteOptions.read(buf), +public object FfiConverterTypeUpdate: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): Update { + return Update( + FfiConverterString.read(buf), + FfiConverterString.read(buf), + FfiConverterUByte.read(buf), + FfiConverterSequenceTypeBundleUpdate.read(buf), + FfiConverterOptionalMapStringString.read(buf), ) } - override fun allocationSize(value: BuildHeaderOptions) = ( - FfiConverterOptionalTypeChecksumWriteOptions.allocationSize(value.`checksum`) + override fun allocationSize(value: Update) = ( + FfiConverterString.allocationSize(value.`id`) + + FfiConverterString.allocationSize(value.`createdAt`) + + FfiConverterUByte.allocationSize(value.`runtimeVersion`) + + FfiConverterSequenceTypeBundleUpdate.allocationSize(value.`bundles`) + + FfiConverterOptionalMapStringString.allocationSize(value.`metadata`) ) - override fun write(value: BuildHeaderOptions, buf: ByteBuffer) { - FfiConverterOptionalTypeChecksumWriteOptions.write(value.`checksum`, buf) + override fun write(value: Update, buf: ByteBuffer) { + FfiConverterString.write(value.`id`, buf) + FfiConverterString.write(value.`createdAt`, buf) + FfiConverterUByte.write(value.`runtimeVersion`, buf) + FfiConverterSequenceTypeBundleUpdate.write(value.`bundles`, buf) + FfiConverterOptionalMapStringString.write(value.`metadata`, buf) } } /** - * Pptions for the bundle index section. + * Signature metadata returned with an update document. */ -data class BuildIndexOptions ( - var `checksum`: ChecksumWriteOptions? = null +data class UpdateSignature ( + /** + * Identifier of the public key used to verify the signature. + */ + var `keyId`: kotlin.String + , + /** + * Base64-encoded signature of the raw update document. + */ + var `sig`: kotlin.String + , + /** + * Signature algorithm used for [`Self::sig`]. + */ + var `alg`: kotlin.String ){ @@ -7834,83 +9390,93 @@ data class BuildIndexOptions ( /** * @suppress */ -public object FfiConverterTypeBuildIndexOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BuildIndexOptions { - return BuildIndexOptions( - FfiConverterOptionalTypeChecksumWriteOptions.read(buf), +public object FfiConverterTypeUpdateSignature: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdateSignature { + return UpdateSignature( + FfiConverterString.read(buf), + FfiConverterString.read(buf), + FfiConverterString.read(buf), ) } - override fun allocationSize(value: BuildIndexOptions) = ( - FfiConverterOptionalTypeChecksumWriteOptions.allocationSize(value.`checksum`) + override fun allocationSize(value: UpdateSignature) = ( + FfiConverterString.allocationSize(value.`keyId`) + + FfiConverterString.allocationSize(value.`sig`) + + FfiConverterString.allocationSize(value.`alg`) ) - override fun write(value: BuildIndexOptions, buf: ByteBuffer) { - FfiConverterOptionalTypeChecksumWriteOptions.write(value.`checksum`, buf) + override fun write(value: UpdateSignature, buf: ByteBuffer) { + FfiConverterString.write(value.`keyId`, buf) + FfiConverterString.write(value.`sig`, buf) + FfiConverterString.write(value.`alg`, buf) } } -/** - * Top-level options passed to [`BundleBuilder::build`]. - * All fields are optional; omitting them applies the library defaults. - */ -data class BuildOptions ( - var `header`: BuildHeaderOptions? +data class UpdaterDownloadOptions ( + var `concurrency`: kotlin.UInt? = null , - var `index`: BuildIndexOptions? + var `timeout`: kotlin.ULong? = null , - var `dataChecksum`: ChecksumWriteOptions? + var `cancellation`: Cancellation? = null -){ +): Disposable{ + @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here + override fun destroy() { + + Disposable.destroy( + this.`concurrency`, + this.`timeout`, + this.`cancellation` + ) + } + companion object } /** * @suppress */ -public object FfiConverterTypeBuildOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BuildOptions { - return BuildOptions( - FfiConverterOptionalTypeBuildHeaderOptions.read(buf), - FfiConverterOptionalTypeBuildIndexOptions.read(buf), - FfiConverterOptionalTypeChecksumWriteOptions.read(buf), +public object FfiConverterTypeUpdaterDownloadOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterDownloadOptions { + return UpdaterDownloadOptions( + FfiConverterOptionalUInt.read(buf), + FfiConverterOptionalULong.read(buf), + FfiConverterOptionalTypeCancellation.read(buf), ) } - override fun allocationSize(value: BuildOptions) = ( - FfiConverterOptionalTypeBuildHeaderOptions.allocationSize(value.`header`) + - FfiConverterOptionalTypeBuildIndexOptions.allocationSize(value.`index`) + - FfiConverterOptionalTypeChecksumWriteOptions.allocationSize(value.`dataChecksum`) + override fun allocationSize(value: UpdaterDownloadOptions) = ( + FfiConverterOptionalUInt.allocationSize(value.`concurrency`) + + FfiConverterOptionalULong.allocationSize(value.`timeout`) + + FfiConverterOptionalTypeCancellation.allocationSize(value.`cancellation`) ) - override fun write(value: BuildOptions, buf: ByteBuffer) { - FfiConverterOptionalTypeBuildHeaderOptions.write(value.`header`, buf) - FfiConverterOptionalTypeBuildIndexOptions.write(value.`index`, buf) - FfiConverterOptionalTypeChecksumWriteOptions.write(value.`dataChecksum`, buf) + override fun write(value: UpdaterDownloadOptions, buf: ByteBuffer) { + FfiConverterOptionalUInt.write(value.`concurrency`, buf) + FfiConverterOptionalULong.write(value.`timeout`, buf) + FfiConverterOptionalTypeCancellation.write(value.`cancellation`, buf) } } -/** - * HTTP cache-control fields stored alongside each bundle entry in the manifest. - * Used to avoid re-downloading bundles that haven't changed. - */ -data class BundleManifestMetadata ( - var `etag`: kotlin.String? +data class UpdaterDownloadResult ( + var `name`: kotlin.String + , + var `version`: kotlin.String , var `integrity`: kotlin.String? , - var `signature`: kotlin.String? + var `metadata`: Map? , - var `lastModified`: kotlin.String? + var `result`: UpdaterDownloadResultKind ){ @@ -7924,43 +9490,38 @@ data class BundleManifestMetadata ( /** * @suppress */ -public object FfiConverterTypeBundleManifestMetadata: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleManifestMetadata { - return BundleManifestMetadata( - FfiConverterOptionalString.read(buf), - FfiConverterOptionalString.read(buf), - FfiConverterOptionalString.read(buf), +public object FfiConverterTypeUpdaterDownloadResult: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterDownloadResult { + return UpdaterDownloadResult( + FfiConverterString.read(buf), + FfiConverterString.read(buf), FfiConverterOptionalString.read(buf), + FfiConverterOptionalMapStringString.read(buf), + FfiConverterTypeUpdaterDownloadResultKind.read(buf), ) } - override fun allocationSize(value: BundleManifestMetadata) = ( - FfiConverterOptionalString.allocationSize(value.`etag`) + + override fun allocationSize(value: UpdaterDownloadResult) = ( + FfiConverterString.allocationSize(value.`name`) + + FfiConverterString.allocationSize(value.`version`) + FfiConverterOptionalString.allocationSize(value.`integrity`) + - FfiConverterOptionalString.allocationSize(value.`signature`) + - FfiConverterOptionalString.allocationSize(value.`lastModified`) + FfiConverterOptionalMapStringString.allocationSize(value.`metadata`) + + FfiConverterTypeUpdaterDownloadResultKind.allocationSize(value.`result`) ) - override fun write(value: BundleManifestMetadata, buf: ByteBuffer) { - FfiConverterOptionalString.write(value.`etag`, buf) + override fun write(value: UpdaterDownloadResult, buf: ByteBuffer) { + FfiConverterString.write(value.`name`, buf) + FfiConverterString.write(value.`version`, buf) FfiConverterOptionalString.write(value.`integrity`, buf) - FfiConverterOptionalString.write(value.`signature`, buf) - FfiConverterOptionalString.write(value.`lastModified`, buf) + FfiConverterOptionalMapStringString.write(value.`metadata`, buf) + FfiConverterTypeUpdaterDownloadResultKind.write(value.`result`, buf) } } -/** - * How a [`BundleProtocolHandler`] resolves the request uri. - * - * Defaults to the first hostname segment as the bundle name, and a directory-index path - * (`/about` -> `/about/index.html`). - */ -data class BundleProtocolOptions ( - var `bundleResolver`: BundleResolver? = null - , - var `pathResolver`: PathResolver? = null +data class UpdaterGetUpdateOptions ( + var `expectSignatureKeyId`: kotlin.String? = null ){ @@ -7974,136 +9535,111 @@ data class BundleProtocolOptions ( /** * @suppress */ -public object FfiConverterTypeBundleProtocolOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleProtocolOptions { - return BundleProtocolOptions( - FfiConverterOptionalTypeBundleResolver.read(buf), - FfiConverterOptionalTypePathResolver.read(buf), +public object FfiConverterTypeUpdaterGetUpdateOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterGetUpdateOptions { + return UpdaterGetUpdateOptions( + FfiConverterOptionalString.read(buf), ) } - override fun allocationSize(value: BundleProtocolOptions) = ( - FfiConverterOptionalTypeBundleResolver.allocationSize(value.`bundleResolver`) + - FfiConverterOptionalTypePathResolver.allocationSize(value.`pathResolver`) + override fun allocationSize(value: UpdaterGetUpdateOptions) = ( + FfiConverterOptionalString.allocationSize(value.`expectSignatureKeyId`) ) - override fun write(value: BundleProtocolOptions, buf: ByteBuffer) { - FfiConverterOptionalTypeBundleResolver.write(value.`bundleResolver`, buf) - FfiConverterOptionalTypePathResolver.write(value.`pathResolver`, buf) + override fun write(value: UpdaterGetUpdateOptions, buf: ByteBuffer) { + FfiConverterOptionalString.write(value.`expectSignatureKeyId`, buf) } } -data class BundleSourceConfig ( - var `builtinDir`: kotlin.String - , - var `remoteDir`: kotlin.String - , - var `builtinManifestFilepath`: kotlin.String? = null - , - var `remoteManifestFilepath`: kotlin.String? = null - , - /** - * How bundles are checked against their manifest integrity metadata on load. - */ - var `integrity`: BundleSourceIntegrityOptions? = null +data class UpdaterInstallResult ( + var `name`: kotlin.String , - /** - * How bundle signatures are verified on load. - */ - var `signature`: BundleSourceSignatureOptions? = null + var `targetVersion`: kotlin.String? , - /** - * How each entry's checksum is verified when its data is read. - */ - var `dataReadOptions`: DataReadOptions? = null + var `installVersion`: kotlin.String? , - /** - * How a bundle's header checksum is verified when its descriptor is read on load. - */ - var `headerReadOptions`: HeaderReadOptions? = null + var `result`: UpdaterInstallResultKind + +){ + + + + + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeUpdaterInstallResult: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterInstallResult { + return UpdaterInstallResult( + FfiConverterString.read(buf), + FfiConverterOptionalString.read(buf), + FfiConverterOptionalString.read(buf), + FfiConverterTypeUpdaterInstallResultKind.read(buf), + ) + } + + override fun allocationSize(value: UpdaterInstallResult) = ( + FfiConverterString.allocationSize(value.`name`) + + FfiConverterOptionalString.allocationSize(value.`targetVersion`) + + FfiConverterOptionalString.allocationSize(value.`installVersion`) + + FfiConverterTypeUpdaterInstallResultKind.allocationSize(value.`result`) + ) + + override fun write(value: UpdaterInstallResult, buf: ByteBuffer) { + FfiConverterString.write(value.`name`, buf) + FfiConverterOptionalString.write(value.`targetVersion`, buf) + FfiConverterOptionalString.write(value.`installVersion`, buf) + FfiConverterTypeUpdaterInstallResultKind.write(value.`result`, buf) + } +} + + + +data class UpdaterInstallTarget ( + var `name`: kotlin.String , - /** - * How a bundle's index checksum is verified when its descriptor is read on load. - */ - var `indexReadOptions`: IndexReadOptions? = null + var `version`: kotlin.String? -): Disposable{ +){ - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - - Disposable.destroy( - this.`builtinDir`, - this.`remoteDir`, - this.`builtinManifestFilepath`, - this.`remoteManifestFilepath`, - this.`integrity`, - this.`signature`, - this.`dataReadOptions`, - this.`headerReadOptions`, - this.`indexReadOptions` - ) - } - companion object } /** * @suppress */ -public object FfiConverterTypeBundleSourceConfig: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleSourceConfig { - return BundleSourceConfig( +public object FfiConverterTypeUpdaterInstallTarget: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterInstallTarget { + return UpdaterInstallTarget( FfiConverterString.read(buf), - FfiConverterString.read(buf), - FfiConverterOptionalString.read(buf), FfiConverterOptionalString.read(buf), - FfiConverterOptionalTypeBundleSourceIntegrityOptions.read(buf), - FfiConverterOptionalTypeBundleSourceSignatureOptions.read(buf), - FfiConverterOptionalTypeDataReadOptions.read(buf), - FfiConverterOptionalTypeHeaderReadOptions.read(buf), - FfiConverterOptionalTypeIndexReadOptions.read(buf), ) } - override fun allocationSize(value: BundleSourceConfig) = ( - FfiConverterString.allocationSize(value.`builtinDir`) + - FfiConverterString.allocationSize(value.`remoteDir`) + - FfiConverterOptionalString.allocationSize(value.`builtinManifestFilepath`) + - FfiConverterOptionalString.allocationSize(value.`remoteManifestFilepath`) + - FfiConverterOptionalTypeBundleSourceIntegrityOptions.allocationSize(value.`integrity`) + - FfiConverterOptionalTypeBundleSourceSignatureOptions.allocationSize(value.`signature`) + - FfiConverterOptionalTypeDataReadOptions.allocationSize(value.`dataReadOptions`) + - FfiConverterOptionalTypeHeaderReadOptions.allocationSize(value.`headerReadOptions`) + - FfiConverterOptionalTypeIndexReadOptions.allocationSize(value.`indexReadOptions`) + override fun allocationSize(value: UpdaterInstallTarget) = ( + FfiConverterString.allocationSize(value.`name`) + + FfiConverterOptionalString.allocationSize(value.`version`) ) - override fun write(value: BundleSourceConfig, buf: ByteBuffer) { - FfiConverterString.write(value.`builtinDir`, buf) - FfiConverterString.write(value.`remoteDir`, buf) - FfiConverterOptionalString.write(value.`builtinManifestFilepath`, buf) - FfiConverterOptionalString.write(value.`remoteManifestFilepath`, buf) - FfiConverterOptionalTypeBundleSourceIntegrityOptions.write(value.`integrity`, buf) - FfiConverterOptionalTypeBundleSourceSignatureOptions.write(value.`signature`, buf) - FfiConverterOptionalTypeDataReadOptions.write(value.`dataReadOptions`, buf) - FfiConverterOptionalTypeHeaderReadOptions.write(value.`headerReadOptions`, buf) - FfiConverterOptionalTypeIndexReadOptions.write(value.`indexReadOptions`, buf) + override fun write(value: UpdaterInstallTarget, buf: ByteBuffer) { + FfiConverterString.write(value.`name`, buf) + FfiConverterOptionalString.write(value.`version`, buf) } } -/** - * How bundles are checked against the integrity recorded for them in the manifest when - * they are loaded from disk. - */ -data class BundleSourceIntegrityOptions ( +data class UpdaterIntegrityOptions ( /** * How a bundle's integrity metadata is treated (default: [`IntegrityPolicy::Optional`]). * @@ -8111,84 +9647,53 @@ data class BundleSourceIntegrityOptions ( */ var `policy`: IntegrityPolicy? = null , - /** - * A custom checker that validates bundle bytes against their integrity string - * (default: the built-in checker, which compares the advertised hash). - */ - var `check`: IntegrityCheck? = null - , - /** - * Which bundles are checked on load (default: [`BundleSourceVerifyMode::OnlyRemote`]). - */ - var `checkMode`: BundleSourceVerifyMode? = null + var `algorithm`: IntegrityAlgorithm? = null -): Disposable{ +){ - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - - Disposable.destroy( - this.`policy`, - this.`check`, - this.`checkMode` - ) - } - companion object } /** * @suppress */ -public object FfiConverterTypeBundleSourceIntegrityOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleSourceIntegrityOptions { - return BundleSourceIntegrityOptions( +public object FfiConverterTypeUpdaterIntegrityOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterIntegrityOptions { + return UpdaterIntegrityOptions( FfiConverterOptionalTypeIntegrityPolicy.read(buf), - FfiConverterOptionalTypeIntegrityCheck.read(buf), - FfiConverterOptionalTypeBundleSourceVerifyMode.read(buf), + FfiConverterOptionalTypeIntegrityAlgorithm.read(buf), ) } - override fun allocationSize(value: BundleSourceIntegrityOptions) = ( + override fun allocationSize(value: UpdaterIntegrityOptions) = ( FfiConverterOptionalTypeIntegrityPolicy.allocationSize(value.`policy`) + - FfiConverterOptionalTypeIntegrityCheck.allocationSize(value.`check`) + - FfiConverterOptionalTypeBundleSourceVerifyMode.allocationSize(value.`checkMode`) + FfiConverterOptionalTypeIntegrityAlgorithm.allocationSize(value.`algorithm`) ) - override fun write(value: BundleSourceIntegrityOptions, buf: ByteBuffer) { + override fun write(value: UpdaterIntegrityOptions, buf: ByteBuffer) { FfiConverterOptionalTypeIntegrityPolicy.write(value.`policy`, buf) - FfiConverterOptionalTypeIntegrityCheck.write(value.`check`, buf) - FfiConverterOptionalTypeBundleSourceVerifyMode.write(value.`checkMode`, buf) + FfiConverterOptionalTypeIntegrityAlgorithm.write(value.`algorithm`, buf) } } /** - * How bundle signatures are verified when bundles are loaded from disk. - * - * A bundle's signature signs its integrity string (e.g. `sha256:`), not the - * bundle bytes; verifying it proves the integrity string is authentic. It is verified - * independently of the integrity check, so pair it with an enabled - * [`BundleSourceConfig::integrity`] to also authenticate the bytes — signature - * verification alone does not read them. + * Optional configuration for the [`Updater`]. */ -data class BundleSourceSignatureOptions ( +data class UpdaterOptions ( /** - * Verifies that a bundle's integrity string was signed by the matching key — with a - * declarative public key or a custom function (default: off). + * Release channel (e.g. `"stable"`, `"beta"`). Passed as a query parameter to the remote. */ - var `verify`: SignatureVerification? = null + var `channel`: kotlin.String? = null , - /** - * Which bundles have their signature verified on load - * (default: [`BundleSourceVerifyMode::OnlyRemote`]). - */ - var `verifyMode`: BundleSourceVerifyMode? = null + var `integrity`: UpdaterIntegrityOptions? = null + , + var `signature`: UpdaterSignatureOptions? = null ): Disposable{ @@ -8200,8 +9705,9 @@ data class BundleSourceSignatureOptions ( override fun destroy() { Disposable.destroy( - this.`verify`, - this.`verifyMode` + this.`channel`, + this.`integrity`, + this.`signature` ) } @@ -8211,34 +9717,38 @@ data class BundleSourceSignatureOptions ( /** * @suppress */ -public object FfiConverterTypeBundleSourceSignatureOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleSourceSignatureOptions { - return BundleSourceSignatureOptions( - FfiConverterOptionalTypeSignatureVerification.read(buf), - FfiConverterOptionalTypeBundleSourceVerifyMode.read(buf), +public object FfiConverterTypeUpdaterOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterOptions { + return UpdaterOptions( + FfiConverterOptionalString.read(buf), + FfiConverterOptionalTypeUpdaterIntegrityOptions.read(buf), + FfiConverterOptionalTypeUpdaterSignatureOptions.read(buf), ) } - override fun allocationSize(value: BundleSourceSignatureOptions) = ( - FfiConverterOptionalTypeSignatureVerification.allocationSize(value.`verify`) + - FfiConverterOptionalTypeBundleSourceVerifyMode.allocationSize(value.`verifyMode`) + override fun allocationSize(value: UpdaterOptions) = ( + FfiConverterOptionalString.allocationSize(value.`channel`) + + FfiConverterOptionalTypeUpdaterIntegrityOptions.allocationSize(value.`integrity`) + + FfiConverterOptionalTypeUpdaterSignatureOptions.allocationSize(value.`signature`) ) - override fun write(value: BundleSourceSignatureOptions, buf: ByteBuffer) { - FfiConverterOptionalTypeSignatureVerification.write(value.`verify`, buf) - FfiConverterOptionalTypeBundleSourceVerifyMode.write(value.`verifyMode`, buf) + override fun write(value: UpdaterOptions, buf: ByteBuffer) { + FfiConverterOptionalString.write(value.`channel`, buf) + FfiConverterOptionalTypeUpdaterIntegrityOptions.write(value.`integrity`, buf) + FfiConverterOptionalTypeUpdaterSignatureOptions.write(value.`signature`, buf) } } -/** - * The currently active version of a bundle and where it was loaded from. - */ -data class BundleSourceVersion ( - var `kind`: BundleSourceKind +data class UpdaterRollbackResult ( + var `name`: kotlin.String , - var `version`: kotlin.String + var `targetVersion`: kotlin.String? + , + var `rollbackVersion`: kotlin.String? + , + var `result`: UpdaterRollbackResultKind ){ @@ -8252,50 +9762,37 @@ data class BundleSourceVersion ( /** * @suppress */ -public object FfiConverterTypeBundleSourceVersion: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleSourceVersion { - return BundleSourceVersion( - FfiConverterTypeBundleSourceKind.read(buf), +public object FfiConverterTypeUpdaterRollbackResult: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterRollbackResult { + return UpdaterRollbackResult( FfiConverterString.read(buf), + FfiConverterOptionalString.read(buf), + FfiConverterOptionalString.read(buf), + FfiConverterTypeUpdaterRollbackResultKind.read(buf), ) } - override fun allocationSize(value: BundleSourceVersion) = ( - FfiConverterTypeBundleSourceKind.allocationSize(value.`kind`) + - FfiConverterString.allocationSize(value.`version`) + override fun allocationSize(value: UpdaterRollbackResult) = ( + FfiConverterString.allocationSize(value.`name`) + + FfiConverterOptionalString.allocationSize(value.`targetVersion`) + + FfiConverterOptionalString.allocationSize(value.`rollbackVersion`) + + FfiConverterTypeUpdaterRollbackResultKind.allocationSize(value.`result`) ) - override fun write(value: BundleSourceVersion, buf: ByteBuffer) { - FfiConverterTypeBundleSourceKind.write(value.`kind`, buf) - FfiConverterString.write(value.`version`, buf) + override fun write(value: UpdaterRollbackResult, buf: ByteBuffer) { + FfiConverterString.write(value.`name`, buf) + FfiConverterOptionalString.write(value.`targetVersion`, buf) + FfiConverterOptionalString.write(value.`rollbackVersion`, buf) + FfiConverterTypeUpdaterRollbackResultKind.write(value.`result`, buf) } } -/** - * Result of checking whether a bundle update is available. - * - * `is_available` is `true` when `version` differs from `local_version`. - * The `etag`, `integrity`, `signature`, and `last_modified` fields can be - * passed to [`BundleSource::write_remote_bundle`] after downloading. - */ -data class BundleUpdateInfo ( +data class UpdaterRollbackTarget ( var `name`: kotlin.String , - var `version`: kotlin.String - , - var `localVersion`: kotlin.String? - , - var `isAvailable`: kotlin.Boolean - , - var `etag`: kotlin.String? - , - var `integrity`: kotlin.String? - , - var `signature`: kotlin.String? - , - var `lastModified`: kotlin.String? + var `version`: kotlin.String? ){ @@ -8309,1091 +9806,1271 @@ data class BundleUpdateInfo ( /** * @suppress */ -public object FfiConverterTypeBundleUpdateInfo: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleUpdateInfo { - return BundleUpdateInfo( +public object FfiConverterTypeUpdaterRollbackTarget: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterRollbackTarget { + return UpdaterRollbackTarget( FfiConverterString.read(buf), - FfiConverterString.read(buf), - FfiConverterOptionalString.read(buf), - FfiConverterBoolean.read(buf), - FfiConverterOptionalString.read(buf), - FfiConverterOptionalString.read(buf), - FfiConverterOptionalString.read(buf), FfiConverterOptionalString.read(buf), ) } - override fun allocationSize(value: BundleUpdateInfo) = ( + override fun allocationSize(value: UpdaterRollbackTarget) = ( FfiConverterString.allocationSize(value.`name`) + - FfiConverterString.allocationSize(value.`version`) + - FfiConverterOptionalString.allocationSize(value.`localVersion`) + - FfiConverterBoolean.allocationSize(value.`isAvailable`) + - FfiConverterOptionalString.allocationSize(value.`etag`) + - FfiConverterOptionalString.allocationSize(value.`integrity`) + - FfiConverterOptionalString.allocationSize(value.`signature`) + - FfiConverterOptionalString.allocationSize(value.`lastModified`) + FfiConverterOptionalString.allocationSize(value.`version`) ) - override fun write(value: BundleUpdateInfo, buf: ByteBuffer) { + override fun write(value: UpdaterRollbackTarget, buf: ByteBuffer) { FfiConverterString.write(value.`name`, buf) - FfiConverterString.write(value.`version`, buf) - FfiConverterOptionalString.write(value.`localVersion`, buf) - FfiConverterBoolean.write(value.`isAvailable`, buf) - FfiConverterOptionalString.write(value.`etag`, buf) - FfiConverterOptionalString.write(value.`integrity`, buf) - FfiConverterOptionalString.write(value.`signature`, buf) - FfiConverterOptionalString.write(value.`lastModified`, buf) + FfiConverterOptionalString.write(value.`version`, buf) } } -/** - * How each entry's xxHash-32 data checksum is verified when its data is read. - */ -data class ChecksumReadOptions ( - /** - * Verify each entry's data checksum when its data is read (default: `true`). - */ - var `verify`: kotlin.Boolean? = null - , - /** - * The seed the data checksums were built with (default: `0`). - */ - var `seed`: kotlin.UInt? = null +data class UpdaterSignatureOptions ( + var `keys`: List? = null -){ +): Disposable{ + @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here + override fun destroy() { + + Disposable.destroy( + this.`keys` + ) + } + companion object } /** * @suppress */ -public object FfiConverterTypeChecksumReadOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): ChecksumReadOptions { - return ChecksumReadOptions( - FfiConverterOptionalBoolean.read(buf), - FfiConverterOptionalUInt.read(buf), +public object FfiConverterTypeUpdaterSignatureOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterSignatureOptions { + return UpdaterSignatureOptions( + FfiConverterOptionalSequenceTypeSignatureVerifyKey.read(buf), ) } - override fun allocationSize(value: ChecksumReadOptions) = ( - FfiConverterOptionalBoolean.allocationSize(value.`verify`) + - FfiConverterOptionalUInt.allocationSize(value.`seed`) + override fun allocationSize(value: UpdaterSignatureOptions) = ( + FfiConverterOptionalSequenceTypeSignatureVerifyKey.allocationSize(value.`keys`) ) - override fun write(value: ChecksumReadOptions, buf: ByteBuffer) { - FfiConverterOptionalBoolean.write(value.`verify`, buf) - FfiConverterOptionalUInt.write(value.`seed`, buf) + override fun write(value: UpdaterSignatureOptions, buf: ByteBuffer) { + FfiConverterOptionalSequenceTypeSignatureVerifyKey.write(value.`keys`, buf) } } -data class ChecksumWriteOptions ( +/** + * How the bundle name is resolved from the request uri. + */ +sealed class BundleResolver { + /** - * The seed the data checksums were built with (default: `0`). + * From the uri hostname. + * + * - `segment`: which part of the host to use (default: [`HostnameSegment::First`]). + * - `allow_wvb_suffix_only`: only resolve hosts ending in `.wvb` (default: false). */ - var `seed`: kotlin.UInt? = null + data class Hostname( + val `segment`: dev.wvb.HostnameSegment?, + val `allowWvbSuffixOnly`: kotlin.Boolean?) : BundleResolver() + + { + + + companion object + } -){ + /** + * From the uri pathname. + * + * - `segment_index`: 0-based over non-empty path segments (default: 0). + * e.g. `app://_/my-app/index.html` with index 0 -> bundle `my-app`. + */ + data class Pathname( + val `segmentIndex`: kotlin.UInt?) : BundleResolver() + + { + + + companion object + } + + + companion object } /** * @suppress */ -public object FfiConverterTypeChecksumWriteOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): ChecksumWriteOptions { - return ChecksumWriteOptions( - FfiConverterOptionalUInt.read(buf), - ) +public object FfiConverterTypeBundleResolver : FfiConverterRustBuffer{ + override fun read(buf: ByteBuffer): BundleResolver { + return when(buf.getInt()) { + 1 -> BundleResolver.Hostname( + FfiConverterOptionalTypeHostnameSegment.read(buf), + FfiConverterOptionalBoolean.read(buf), + ) + 2 -> BundleResolver.Pathname( + FfiConverterOptionalUInt.read(buf), + ) + else -> throw RuntimeException("invalid enum value, something is very wrong!!") + } } - override fun allocationSize(value: ChecksumWriteOptions) = ( - FfiConverterOptionalUInt.allocationSize(value.`seed`) - ) + override fun allocationSize(value: BundleResolver) = when(value) { + is BundleResolver.Hostname -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterOptionalTypeHostnameSegment.allocationSize(value.`segment`) + + FfiConverterOptionalBoolean.allocationSize(value.`allowWvbSuffixOnly`) + ) + } + is BundleResolver.Pathname -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterOptionalUInt.allocationSize(value.`segmentIndex`) + ) + } + } - override fun write(value: ChecksumWriteOptions, buf: ByteBuffer) { - FfiConverterOptionalUInt.write(value.`seed`, buf) + override fun write(value: BundleResolver, buf: ByteBuffer) { + when(value) { + is BundleResolver.Hostname -> { + buf.putInt(1) + FfiConverterOptionalTypeHostnameSegment.write(value.`segment`, buf) + FfiConverterOptionalBoolean.write(value.`allowWvbSuffixOnly`, buf) + Unit + } + is BundleResolver.Pathname -> { + buf.putInt(2) + FfiConverterOptionalUInt.write(value.`segmentIndex`, buf) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } } } -/** - * How each entry's data checksum is verified when its data is read. - */ -data class DataReadOptions ( - /** - * How each entry's data checksum is verified. - */ - var `checksum`: ChecksumReadOptions? = null - -){ - - +sealed class EcdsaVerifyingKey { - companion object -} + data class Sec1( + val `der`: kotlin.ByteArray) : EcdsaVerifyingKey() + + { + -/** - * @suppress - */ -public object FfiConverterTypeDataReadOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DataReadOptions { - return DataReadOptions( - FfiConverterOptionalTypeChecksumReadOptions.read(buf), - ) + companion object } + + data class SpkiDer( + val `der`: kotlin.ByteArray) : EcdsaVerifyingKey() + + { + - override fun allocationSize(value: DataReadOptions) = ( - FfiConverterOptionalTypeChecksumReadOptions.allocationSize(value.`checksum`) - ) - - override fun write(value: DataReadOptions, buf: ByteBuffer) { - FfiConverterOptionalTypeChecksumReadOptions.write(value.`checksum`, buf) + companion object } -} - - - -/** - * Result of a bundle download containing the parsed bundle, its raw bytes, - * and the server-provided metadata. - * - * `data` holds the raw `.wvb` bytes as received from the server, which callers - * can persist to disk via [`BundleSource::write_remote_bundle`]. - */ -data class DownloadResult ( - var `info`: RemoteBundleInfo - , - var `bundle`: Bundle - , - var `data`: kotlin.ByteArray -): Disposable{ + data class SpkiPem( + val `pem`: kotlin.String) : EcdsaVerifyingKey() + + { + + + companion object + } - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - - Disposable.destroy( - this.`info`, - this.`bundle`, - this.`data` - ) - } + + companion object } /** * @suppress */ -public object FfiConverterTypeDownloadResult: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DownloadResult { - return DownloadResult( - FfiConverterTypeRemoteBundleInfo.read(buf), - FfiConverterTypeBundle.read(buf), - FfiConverterByteArray.read(buf), - ) +public object FfiConverterTypeEcdsaVerifyingKey : FfiConverterRustBuffer{ + override fun read(buf: ByteBuffer): EcdsaVerifyingKey { + return when(buf.getInt()) { + 1 -> EcdsaVerifyingKey.Sec1( + FfiConverterByteArray.read(buf), + ) + 2 -> EcdsaVerifyingKey.SpkiDer( + FfiConverterByteArray.read(buf), + ) + 3 -> EcdsaVerifyingKey.SpkiPem( + FfiConverterString.read(buf), + ) + else -> throw RuntimeException("invalid enum value, something is very wrong!!") + } } - override fun allocationSize(value: DownloadResult) = ( - FfiConverterTypeRemoteBundleInfo.allocationSize(value.`info`) + - FfiConverterTypeBundle.allocationSize(value.`bundle`) + - FfiConverterByteArray.allocationSize(value.`data`) - ) + override fun allocationSize(value: EcdsaVerifyingKey) = when(value) { + is EcdsaVerifyingKey.Sec1 -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterByteArray.allocationSize(value.`der`) + ) + } + is EcdsaVerifyingKey.SpkiDer -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterByteArray.allocationSize(value.`der`) + ) + } + is EcdsaVerifyingKey.SpkiPem -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterString.allocationSize(value.`pem`) + ) + } + } - override fun write(value: DownloadResult, buf: ByteBuffer) { - FfiConverterTypeRemoteBundleInfo.write(value.`info`, buf) - FfiConverterTypeBundle.write(value.`bundle`, buf) - FfiConverterByteArray.write(value.`data`, buf) + override fun write(value: EcdsaVerifyingKey, buf: ByteBuffer) { + when(value) { + is EcdsaVerifyingKey.Sec1 -> { + buf.putInt(1) + FfiConverterByteArray.write(value.`der`, buf) + Unit + } + is EcdsaVerifyingKey.SpkiDer -> { + buf.putInt(2) + FfiConverterByteArray.write(value.`der`, buf) + Unit + } + is EcdsaVerifyingKey.SpkiPem -> { + buf.putInt(3) + FfiConverterString.write(value.`pem`, buf) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } } } -/** - * How a bundle's header checksum is verified when its header is read. - */ -data class HeaderReadOptions ( - /** - * How the header checksum is verified. - */ - var `checksum`: ChecksumReadOptions? = null - -){ - - +sealed class Ed25519VerifyingKey { - companion object -} + data class Raw( + val `bytes`: kotlin.ByteArray) : Ed25519VerifyingKey() + + { + -/** - * @suppress - */ -public object FfiConverterTypeHeaderReadOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): HeaderReadOptions { - return HeaderReadOptions( - FfiConverterOptionalTypeChecksumReadOptions.read(buf), - ) + companion object } + + data class SpkiDer( + val `der`: kotlin.ByteArray) : Ed25519VerifyingKey() + + { + - override fun allocationSize(value: HeaderReadOptions) = ( - FfiConverterOptionalTypeChecksumReadOptions.allocationSize(value.`checksum`) - ) - - override fun write(value: HeaderReadOptions, buf: ByteBuffer) { - FfiConverterOptionalTypeChecksumReadOptions.write(value.`checksum`, buf) + companion object } -} + + data class SpkiPem( + val `pem`: kotlin.String) : Ed25519VerifyingKey() + + { + + companion object + } + + -/** - * HTTP client options - */ -data class HttpOptions ( - /** - * Headers sent with every request. - */ - var `defaultHeaders`: Map? = null - , - var `userAgent`: kotlin.String? = null - , - /** - * Total request timeout in milliseconds (default: `120000`). - * - * Bounds an otherwise-unbounded download: without it a stalled transfer would hang - * forever and keep holding the updater's per-bundle lock. - */ - var `timeout`: kotlin.ULong? = null - , - /** - * Timeout in milliseconds for reading the response body. - */ - var `readTimeout`: kotlin.ULong? = null - , - /** - * Timeout in milliseconds for establishing the connection. - */ - var `connectTimeout`: kotlin.ULong? = null - , - var `poolIdleTimeout`: kotlin.ULong? = null - , - var `poolMaxIdlePerHost`: kotlin.UInt? = null - , - var `referer`: kotlin.Boolean? = null - , - var `tcpNodelay`: kotlin.Boolean? = null -){ - - companion object } /** * @suppress */ -public object FfiConverterTypeHttpOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): HttpOptions { - return HttpOptions( - FfiConverterOptionalMapStringString.read(buf), - FfiConverterOptionalString.read(buf), - FfiConverterOptionalULong.read(buf), - FfiConverterOptionalULong.read(buf), - FfiConverterOptionalULong.read(buf), - FfiConverterOptionalULong.read(buf), - FfiConverterOptionalUInt.read(buf), - FfiConverterOptionalBoolean.read(buf), - FfiConverterOptionalBoolean.read(buf), - ) +public object FfiConverterTypeEd25519VerifyingKey : FfiConverterRustBuffer{ + override fun read(buf: ByteBuffer): Ed25519VerifyingKey { + return when(buf.getInt()) { + 1 -> Ed25519VerifyingKey.Raw( + FfiConverterByteArray.read(buf), + ) + 2 -> Ed25519VerifyingKey.SpkiDer( + FfiConverterByteArray.read(buf), + ) + 3 -> Ed25519VerifyingKey.SpkiPem( + FfiConverterString.read(buf), + ) + else -> throw RuntimeException("invalid enum value, something is very wrong!!") + } } - override fun allocationSize(value: HttpOptions) = ( - FfiConverterOptionalMapStringString.allocationSize(value.`defaultHeaders`) + - FfiConverterOptionalString.allocationSize(value.`userAgent`) + - FfiConverterOptionalULong.allocationSize(value.`timeout`) + - FfiConverterOptionalULong.allocationSize(value.`readTimeout`) + - FfiConverterOptionalULong.allocationSize(value.`connectTimeout`) + - FfiConverterOptionalULong.allocationSize(value.`poolIdleTimeout`) + - FfiConverterOptionalUInt.allocationSize(value.`poolMaxIdlePerHost`) + - FfiConverterOptionalBoolean.allocationSize(value.`referer`) + - FfiConverterOptionalBoolean.allocationSize(value.`tcpNodelay`) - ) + override fun allocationSize(value: Ed25519VerifyingKey) = when(value) { + is Ed25519VerifyingKey.Raw -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterByteArray.allocationSize(value.`bytes`) + ) + } + is Ed25519VerifyingKey.SpkiDer -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterByteArray.allocationSize(value.`der`) + ) + } + is Ed25519VerifyingKey.SpkiPem -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterString.allocationSize(value.`pem`) + ) + } + } - override fun write(value: HttpOptions, buf: ByteBuffer) { - FfiConverterOptionalMapStringString.write(value.`defaultHeaders`, buf) - FfiConverterOptionalString.write(value.`userAgent`, buf) - FfiConverterOptionalULong.write(value.`timeout`, buf) - FfiConverterOptionalULong.write(value.`readTimeout`, buf) - FfiConverterOptionalULong.write(value.`connectTimeout`, buf) - FfiConverterOptionalULong.write(value.`poolIdleTimeout`, buf) - FfiConverterOptionalUInt.write(value.`poolMaxIdlePerHost`, buf) - FfiConverterOptionalBoolean.write(value.`referer`, buf) - FfiConverterOptionalBoolean.write(value.`tcpNodelay`, buf) + override fun write(value: Ed25519VerifyingKey, buf: ByteBuffer) { + when(value) { + is Ed25519VerifyingKey.Raw -> { + buf.putInt(1) + FfiConverterByteArray.write(value.`bytes`, buf) + Unit + } + is Ed25519VerifyingKey.SpkiDer -> { + buf.putInt(2) + FfiConverterByteArray.write(value.`der`, buf) + Unit + } + is Ed25519VerifyingKey.SpkiPem -> { + buf.putInt(3) + FfiConverterString.write(value.`pem`, buf) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } } } + + /** - * HTTP response returned by protocol handlers. + * Which hostname segment is used as the bundle name. */ -data class HttpResponse ( - var `status`: kotlin.UShort - , - var `headers`: Map - , - var `body`: kotlin.ByteArray +sealed class HostnameSegment { -){ + /** + * First segment. (e.g. `app.mydomain.com` -> `app`) + */ + object First : HostnameSegment() + + + /** + * Full hostname. (e.g. `app.wvb` -> `app.wvb`) + */ + object Full : HostnameSegment() + + + /** + * Strip the last segment. (e.g. `a.b.wvb` -> `a.b`) + */ + object StripSuffix : HostnameSegment() + + + /** + * The nth segment, 0-based. + */ + data class Nth( + val `index`: kotlin.UInt) : HostnameSegment() + + { + + + companion object + } + + + companion object } /** * @suppress */ -public object FfiConverterTypeHttpResponse: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): HttpResponse { - return HttpResponse( - FfiConverterUShort.read(buf), - FfiConverterMapStringString.read(buf), - FfiConverterByteArray.read(buf), - ) +public object FfiConverterTypeHostnameSegment : FfiConverterRustBuffer{ + override fun read(buf: ByteBuffer): HostnameSegment { + return when(buf.getInt()) { + 1 -> HostnameSegment.First + 2 -> HostnameSegment.Full + 3 -> HostnameSegment.StripSuffix + 4 -> HostnameSegment.Nth( + FfiConverterUInt.read(buf), + ) + else -> throw RuntimeException("invalid enum value, something is very wrong!!") + } } - override fun allocationSize(value: HttpResponse) = ( - FfiConverterUShort.allocationSize(value.`status`) + - FfiConverterMapStringString.allocationSize(value.`headers`) + - FfiConverterByteArray.allocationSize(value.`body`) - ) + override fun allocationSize(value: HostnameSegment) = when(value) { + is HostnameSegment.First -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + ) + } + is HostnameSegment.Full -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + ) + } + is HostnameSegment.StripSuffix -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + ) + } + is HostnameSegment.Nth -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterUInt.allocationSize(value.`index`) + ) + } + } - override fun write(value: HttpResponse, buf: ByteBuffer) { - FfiConverterUShort.write(value.`status`, buf) - FfiConverterMapStringString.write(value.`headers`, buf) - FfiConverterByteArray.write(value.`body`, buf) + override fun write(value: HostnameSegment, buf: ByteBuffer) { + when(value) { + is HostnameSegment.First -> { + buf.putInt(1) + Unit + } + is HostnameSegment.Full -> { + buf.putInt(2) + Unit + } + is HostnameSegment.StripSuffix -> { + buf.putInt(3) + Unit + } + is HostnameSegment.Nth -> { + buf.putInt(4) + FfiConverterUInt.write(value.`index`, buf) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } } } + + /** - * Metadata for a single entry stored in the bundle index. - * - * `offset` and `len` refer to the entry's position in the data section. - * `content_length` is the logical (uncompressed) size exposed to HTTP clients, - * which may differ from `len` if the data is stored compressed. + * HTTP request method exposed to FFI consumers. */ -data class IndexEntry ( - var `offset`: kotlin.ULong - , - var `len`: kotlin.ULong - , - var `isEmpty`: kotlin.Boolean - , - var `contentType`: kotlin.String - , - var `contentLength`: kotlin.ULong - , - var `headers`: Map - -){ - +enum class HttpMethod { + GET, + HEAD, + OPTIONS, + POST, + PUT, + PATCH, + DELETE, + TRACE, + CONNECT; + + companion object } + /** * @suppress */ -public object FfiConverterTypeIndexEntry: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): IndexEntry { - return IndexEntry( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - FfiConverterBoolean.read(buf), - FfiConverterString.read(buf), - FfiConverterULong.read(buf), - FfiConverterMapStringString.read(buf), - ) +public object FfiConverterTypeHttpMethod: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + HttpMethod.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) } - override fun allocationSize(value: IndexEntry) = ( - FfiConverterULong.allocationSize(value.`offset`) + - FfiConverterULong.allocationSize(value.`len`) + - FfiConverterBoolean.allocationSize(value.`isEmpty`) + - FfiConverterString.allocationSize(value.`contentType`) + - FfiConverterULong.allocationSize(value.`contentLength`) + - FfiConverterMapStringString.allocationSize(value.`headers`) - ) + override fun allocationSize(value: HttpMethod) = 4UL - override fun write(value: IndexEntry, buf: ByteBuffer) { - FfiConverterULong.write(value.`offset`, buf) - FfiConverterULong.write(value.`len`, buf) - FfiConverterBoolean.write(value.`isEmpty`, buf) - FfiConverterString.write(value.`contentType`, buf) - FfiConverterULong.write(value.`contentLength`, buf) - FfiConverterMapStringString.write(value.`headers`, buf) + override fun write(value: HttpMethod, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) } } + + /** - * How a bundle's index checksum is verified when its index is read. + * Hash algorithm used to compute the [`Subresource Integrity`](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) digest of a bundle. */ -data class IndexReadOptions ( - /** - * How the index checksum is verified. - */ - var `checksum`: ChecksumReadOptions? = null - -){ - +enum class IntegrityAlgorithm { + SHA256, + SHA384, + SHA512; + + companion object } + /** * @suppress */ -public object FfiConverterTypeIndexReadOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): IndexReadOptions { - return IndexReadOptions( - FfiConverterOptionalTypeChecksumReadOptions.read(buf), - ) +public object FfiConverterTypeIntegrityAlgorithm: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + IntegrityAlgorithm.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) } - override fun allocationSize(value: IndexReadOptions) = ( - FfiConverterOptionalTypeChecksumReadOptions.allocationSize(value.`checksum`) - ) + override fun allocationSize(value: IntegrityAlgorithm) = 4UL - override fun write(value: IndexReadOptions, buf: ByteBuffer) { - FfiConverterOptionalTypeChecksumReadOptions.write(value.`checksum`, buf) + override fun write(value: IntegrityAlgorithm, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) } } -data class ListBundleItem ( - var `kind`: BundleSourceKind - , - var `name`: kotlin.String - , - var `version`: kotlin.String - , - var `current`: kotlin.Boolean - , - var `metadata`: BundleManifestMetadata - -){ + + +/** + * How a bundle's integrity metadata is treated when the integrity check runs. + * + * - `Strict`: integrity metadata is required; a bundle without it fails the check. + * - `Optional`: integrity metadata is checked when present and tolerated when missing. + * - `Off`: the integrity check is disabled. + */ + +enum class IntegrityPolicy { + STRICT, + OPTIONAL, + OFF; - + companion object } + /** * @suppress */ -public object FfiConverterTypeListBundleItem: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): ListBundleItem { - return ListBundleItem( - FfiConverterTypeBundleSourceKind.read(buf), - FfiConverterString.read(buf), - FfiConverterString.read(buf), - FfiConverterBoolean.read(buf), - FfiConverterTypeBundleManifestMetadata.read(buf), - ) +public object FfiConverterTypeIntegrityPolicy: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + IntegrityPolicy.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) } - override fun allocationSize(value: ListBundleItem) = ( - FfiConverterTypeBundleSourceKind.allocationSize(value.`kind`) + - FfiConverterString.allocationSize(value.`name`) + - FfiConverterString.allocationSize(value.`version`) + - FfiConverterBoolean.allocationSize(value.`current`) + - FfiConverterTypeBundleManifestMetadata.allocationSize(value.`metadata`) - ) + override fun allocationSize(value: IntegrityPolicy) = 4UL - override fun write(value: ListBundleItem, buf: ByteBuffer) { - FfiConverterTypeBundleSourceKind.write(value.`kind`, buf) - FfiConverterString.write(value.`name`, buf) - FfiConverterString.write(value.`version`, buf) - FfiConverterBoolean.write(value.`current`, buf) - FfiConverterTypeBundleManifestMetadata.write(value.`metadata`, buf) + override fun write(value: IntegrityPolicy, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) } } -/** - * Summary of a bundle returned by the remote listing endpoint. - */ -data class ListRemoteBundleInfo ( - var `name`: kotlin.String - , - var `version`: kotlin.String - -){ - + + +enum class ManifestBundleItemStatus { + CURRENT, + PREVIOUS, + STAGED, + ORPHAN; + + companion object } + /** * @suppress */ -public object FfiConverterTypeListRemoteBundleInfo: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): ListRemoteBundleInfo { - return ListRemoteBundleInfo( - FfiConverterString.read(buf), - FfiConverterString.read(buf), - ) +public object FfiConverterTypeManifestBundleItemStatus: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + ManifestBundleItemStatus.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) } - override fun allocationSize(value: ListRemoteBundleInfo) = ( - FfiConverterString.allocationSize(value.`name`) + - FfiConverterString.allocationSize(value.`version`) - ) + override fun allocationSize(value: ManifestBundleItemStatus) = 4UL - override fun write(value: ListRemoteBundleInfo, buf: ByteBuffer) { - FfiConverterString.write(value.`name`, buf) - FfiConverterString.write(value.`version`, buf) + override fun write(value: ManifestBundleItemStatus, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) } } -/** - * Full metadata returned when fetching or downloading a specific bundle version. - */ -data class RemoteBundleInfo ( - var `name`: kotlin.String - , - var `version`: kotlin.String - , - var `etag`: kotlin.String? - , - var `integrity`: kotlin.String? - , - var `signature`: kotlin.String? - , - var `lastModified`: kotlin.String? - -){ - + + +enum class ManifestRemoveResultKind { + REMOVED, + /** + * The bundle was not exists in the manifest. + */ + NOT_EXISTS, + /** + * The version was not exists in the manifest. + */ + VERSION_NOT_EXISTS, + /** + * The bundle is the current version so that cant be not removed. + * This can be force by enable `force` option. + */ + IN_USE; + + companion object } + /** * @suppress */ -public object FfiConverterTypeRemoteBundleInfo: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): RemoteBundleInfo { - return RemoteBundleInfo( - FfiConverterString.read(buf), - FfiConverterString.read(buf), - FfiConverterOptionalString.read(buf), - FfiConverterOptionalString.read(buf), - FfiConverterOptionalString.read(buf), - FfiConverterOptionalString.read(buf), - ) +public object FfiConverterTypeManifestRemoveResultKind: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + ManifestRemoveResultKind.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) } - override fun allocationSize(value: RemoteBundleInfo) = ( - FfiConverterString.allocationSize(value.`name`) + - FfiConverterString.allocationSize(value.`version`) + - FfiConverterOptionalString.allocationSize(value.`etag`) + - FfiConverterOptionalString.allocationSize(value.`integrity`) + - FfiConverterOptionalString.allocationSize(value.`signature`) + - FfiConverterOptionalString.allocationSize(value.`lastModified`) - ) + override fun allocationSize(value: ManifestRemoveResultKind) = 4UL - override fun write(value: RemoteBundleInfo, buf: ByteBuffer) { - FfiConverterString.write(value.`name`, buf) - FfiConverterString.write(value.`version`, buf) - FfiConverterOptionalString.write(value.`etag`, buf) - FfiConverterOptionalString.write(value.`integrity`, buf) - FfiConverterOptionalString.write(value.`signature`, buf) - FfiConverterOptionalString.write(value.`lastModified`, buf) + override fun write(value: ManifestRemoveResultKind, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) } } -/** - * Options for fetching bundle metadata from the remote. - */ -data class RemoteFetchOptions ( - /** - * Release channel (e.g. `"stable"`, `"beta"`). Passed as a query parameter to the remote. - */ - var `channel`: kotlin.String? = null - -){ - + + +enum class ManifestSetCurrentVersionResultKind { + SETTLED, + NOT_EXISTS, + VERSION_NOT_EXISTS; + + companion object } + /** * @suppress */ -public object FfiConverterTypeRemoteFetchOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): RemoteFetchOptions { - return RemoteFetchOptions( - FfiConverterOptionalString.read(buf), - ) +public object FfiConverterTypeManifestSetCurrentVersionResultKind: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + ManifestSetCurrentVersionResultKind.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) } - override fun allocationSize(value: RemoteFetchOptions) = ( - FfiConverterOptionalString.allocationSize(value.`channel`) - ) + override fun allocationSize(value: ManifestSetCurrentVersionResultKind) = 4UL - override fun write(value: RemoteFetchOptions, buf: ByteBuffer) { - FfiConverterOptionalString.write(value.`channel`, buf) + override fun write(value: ManifestSetCurrentVersionResultKind, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) } } -/** - * Progress reported while a bundle is downloading. - */ -data class RemoteOnDownloadData ( - /** - * Bytes downloaded so far. - */ - var `downloadedBytes`: kotlin.ULong - , - /** - * Total bytes to download, when the server advertised a content length. - */ - var `totalBytes`: kotlin.ULong? - , - /** - * The endpoint the bundle is being downloaded from. - */ - var `endpoint`: kotlin.String - -){ - + + +enum class ManifestStageResultKind { + STAGED, + IN_USE; + + companion object } + /** * @suppress */ -public object FfiConverterTypeRemoteOnDownloadData: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): RemoteOnDownloadData { - return RemoteOnDownloadData( - FfiConverterULong.read(buf), - FfiConverterOptionalULong.read(buf), - FfiConverterString.read(buf), - ) +public object FfiConverterTypeManifestStageResultKind: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + ManifestStageResultKind.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) } - override fun allocationSize(value: RemoteOnDownloadData) = ( - FfiConverterULong.allocationSize(value.`downloadedBytes`) + - FfiConverterOptionalULong.allocationSize(value.`totalBytes`) + - FfiConverterString.allocationSize(value.`endpoint`) - ) + override fun allocationSize(value: ManifestStageResultKind) = 4UL - override fun write(value: RemoteOnDownloadData, buf: ByteBuffer) { - FfiConverterULong.write(value.`downloadedBytes`, buf) - FfiConverterOptionalULong.write(value.`totalBytes`, buf) - FfiConverterString.write(value.`endpoint`, buf) + override fun write(value: ManifestStageResultKind, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) } } + + /** - * Options for creating a [`Remote`] client. + * How the file path in the bundle is resolved from the request uri. */ -data class RemoteOptions ( + +enum class PathResolver { + /** - * HTTP client options. + * Use the uri path as-is (only percent-decoded). */ - var `http`: HttpOptions? = null - , + EXACT, /** - * Download progress callback. + * Directory index: `/` -> `/index.html` and `/about` -> `/about/index.html`. */ - var `onDownload`: RemoteOnDownload? = null - -): Disposable{ - + DIRECTORY_INDEX, + /** + * `.html` extension: `/` -> `/index.html` and `/about` -> `/about.html`. + */ + HTML_EXTENSION; - - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - - Disposable.destroy( - this.`http`, - this.`onDownload` - ) - } - + companion object } + /** * @suppress */ -public object FfiConverterTypeRemoteOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): RemoteOptions { - return RemoteOptions( - FfiConverterOptionalTypeHttpOptions.read(buf), - FfiConverterOptionalTypeRemoteOnDownload.read(buf), - ) +public object FfiConverterTypePathResolver: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + PathResolver.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) } - override fun allocationSize(value: RemoteOptions) = ( - FfiConverterOptionalTypeHttpOptions.allocationSize(value.`http`) + - FfiConverterOptionalTypeRemoteOnDownload.allocationSize(value.`onDownload`) - ) + override fun allocationSize(value: PathResolver) = 4UL - override fun write(value: RemoteOptions, buf: ByteBuffer) { - FfiConverterOptionalTypeHttpOptions.write(value.`http`, buf) - FfiConverterOptionalTypeRemoteOnDownload.write(value.`onDownload`, buf) + override fun write(value: PathResolver, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) } } -/** - * Configuration passed to the updater to enable signature verification. - */ -data class SignatureVerifierOptions ( - var `algorithm`: SignatureAlgorithm - , - var `key`: SignatureVerifyingKey - -){ - - +sealed class RsaVerifyingKey { - companion object -} + data class Pkcs1Der( + val `der`: kotlin.ByteArray) : RsaVerifyingKey() + + { + -/** - * @suppress - */ -public object FfiConverterTypeSignatureVerifierOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): SignatureVerifierOptions { - return SignatureVerifierOptions( - FfiConverterTypeSignatureAlgorithm.read(buf), - FfiConverterTypeSignatureVerifyingKey.read(buf), - ) + companion object } + + data class Pkcs1Pem( + val `pem`: kotlin.String) : RsaVerifyingKey() + + { + - override fun allocationSize(value: SignatureVerifierOptions) = ( - FfiConverterTypeSignatureAlgorithm.allocationSize(value.`algorithm`) + - FfiConverterTypeSignatureVerifyingKey.allocationSize(value.`key`) - ) - - override fun write(value: SignatureVerifierOptions, buf: ByteBuffer) { - FfiConverterTypeSignatureAlgorithm.write(value.`algorithm`, buf) - FfiConverterTypeSignatureVerifyingKey.write(value.`key`, buf) + companion object } -} - - + + data class SpkiDer( + val `der`: kotlin.ByteArray) : RsaVerifyingKey() + + { + -/** - * Key data for signature verification. - * Use `pem` for PEM-encoded text keys, `der` for DER/raw binary keys. - */ -data class SignatureVerifyingKey ( - var `format`: VerifyingKeyFormat - , - var `pem`: kotlin.String? - , - var `der`: kotlin.ByteArray? + companion object + } -){ + data class SpkiPem( + val `pem`: kotlin.String) : RsaVerifyingKey() + + { + + + companion object + } + + + companion object } /** * @suppress */ -public object FfiConverterTypeSignatureVerifyingKey: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): SignatureVerifyingKey { - return SignatureVerifyingKey( - FfiConverterTypeVerifyingKeyFormat.read(buf), - FfiConverterOptionalString.read(buf), - FfiConverterOptionalByteArray.read(buf), - ) +public object FfiConverterTypeRsaVerifyingKey : FfiConverterRustBuffer{ + override fun read(buf: ByteBuffer): RsaVerifyingKey { + return when(buf.getInt()) { + 1 -> RsaVerifyingKey.Pkcs1Der( + FfiConverterByteArray.read(buf), + ) + 2 -> RsaVerifyingKey.Pkcs1Pem( + FfiConverterString.read(buf), + ) + 3 -> RsaVerifyingKey.SpkiDer( + FfiConverterByteArray.read(buf), + ) + 4 -> RsaVerifyingKey.SpkiPem( + FfiConverterString.read(buf), + ) + else -> throw RuntimeException("invalid enum value, something is very wrong!!") + } } - override fun allocationSize(value: SignatureVerifyingKey) = ( - FfiConverterTypeVerifyingKeyFormat.allocationSize(value.`format`) + - FfiConverterOptionalString.allocationSize(value.`pem`) + - FfiConverterOptionalByteArray.allocationSize(value.`der`) - ) + override fun allocationSize(value: RsaVerifyingKey) = when(value) { + is RsaVerifyingKey.Pkcs1Der -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterByteArray.allocationSize(value.`der`) + ) + } + is RsaVerifyingKey.Pkcs1Pem -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterString.allocationSize(value.`pem`) + ) + } + is RsaVerifyingKey.SpkiDer -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterByteArray.allocationSize(value.`der`) + ) + } + is RsaVerifyingKey.SpkiPem -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterString.allocationSize(value.`pem`) + ) + } + } - override fun write(value: SignatureVerifyingKey, buf: ByteBuffer) { - FfiConverterTypeVerifyingKeyFormat.write(value.`format`, buf) - FfiConverterOptionalString.write(value.`pem`, buf) - FfiConverterOptionalByteArray.write(value.`der`, buf) + override fun write(value: RsaVerifyingKey, buf: ByteBuffer) { + when(value) { + is RsaVerifyingKey.Pkcs1Der -> { + buf.putInt(1) + FfiConverterByteArray.write(value.`der`, buf) + Unit + } + is RsaVerifyingKey.Pkcs1Pem -> { + buf.putInt(2) + FfiConverterString.write(value.`pem`, buf) + Unit + } + is RsaVerifyingKey.SpkiDer -> { + buf.putInt(3) + FfiConverterByteArray.write(value.`der`, buf) + Unit + } + is RsaVerifyingKey.SpkiPem -> { + buf.putInt(4) + FfiConverterString.write(value.`pem`, buf) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } } } -data class UpdaterIntegrityOptions ( - /** - * How a bundle's integrity metadata is treated (default: [`IntegrityPolicy::Optional`]). - * - * [`IntegrityPolicy::Off`] disables the integrity check entirely. - */ - var `policy`: IntegrityPolicy? = null - , - /** - * A custom checker that validates bundle bytes against their integrity string - * (default: the built-in checker, which compares the advertised hash). - */ - var `check`: IntegrityCheck? = null + + +sealed class SignatureVerify: Disposable { -): Disposable{ + data class EcdsaSecp256r1( + val `key`: dev.wvb.EcdsaVerifyingKey) : SignatureVerify() + + { + + + companion object + } + + data class EcdsaSecp384r1( + val `key`: dev.wvb.EcdsaVerifyingKey) : SignatureVerify() + + { + + + companion object + } + + data class Ed25519( + val `key`: dev.wvb.Ed25519VerifyingKey) : SignatureVerify() + + { + + + companion object + } + + data class RsaPkcs1V15Sha256( + val `key`: dev.wvb.RsaVerifyingKey) : SignatureVerify() + + { + + + companion object + } + data class RsaPssSha256( + val `key`: dev.wvb.RsaVerifyingKey) : SignatureVerify() + + { + + + companion object + } + + data class Custom( + val `verify`: dev.wvb.SignatureCustomVerify) : SignatureVerify() + + { + + companion object + } @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here override fun destroy() { - + when(this) { + is SignatureVerify.EcdsaSecp256r1 -> { + + Disposable.destroy( + this.`key` + ) + + } + is SignatureVerify.EcdsaSecp384r1 -> { + + Disposable.destroy( + this.`key` + ) + + } + is SignatureVerify.Ed25519 -> { + + Disposable.destroy( + this.`key` + ) + + } + is SignatureVerify.RsaPkcs1V15Sha256 -> { + + Disposable.destroy( + this.`key` + ) + + } + is SignatureVerify.RsaPssSha256 -> { + + Disposable.destroy( + this.`key` + ) + + } + is SignatureVerify.Custom -> { + Disposable.destroy( - this.`policy`, - this.`check` + this.`verify` ) + + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } } + + + + + companion object } /** * @suppress */ -public object FfiConverterTypeUpdaterIntegrityOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): UpdaterIntegrityOptions { - return UpdaterIntegrityOptions( - FfiConverterOptionalTypeIntegrityPolicy.read(buf), - FfiConverterOptionalTypeIntegrityCheck.read(buf), - ) +public object FfiConverterTypeSignatureVerify : FfiConverterRustBuffer{ + override fun read(buf: ByteBuffer): SignatureVerify { + return when(buf.getInt()) { + 1 -> SignatureVerify.EcdsaSecp256r1( + FfiConverterTypeEcdsaVerifyingKey.read(buf), + ) + 2 -> SignatureVerify.EcdsaSecp384r1( + FfiConverterTypeEcdsaVerifyingKey.read(buf), + ) + 3 -> SignatureVerify.Ed25519( + FfiConverterTypeEd25519VerifyingKey.read(buf), + ) + 4 -> SignatureVerify.RsaPkcs1V15Sha256( + FfiConverterTypeRsaVerifyingKey.read(buf), + ) + 5 -> SignatureVerify.RsaPssSha256( + FfiConverterTypeRsaVerifyingKey.read(buf), + ) + 6 -> SignatureVerify.Custom( + FfiConverterTypeSignatureCustomVerify.read(buf), + ) + else -> throw RuntimeException("invalid enum value, something is very wrong!!") + } } - override fun allocationSize(value: UpdaterIntegrityOptions) = ( - FfiConverterOptionalTypeIntegrityPolicy.allocationSize(value.`policy`) + - FfiConverterOptionalTypeIntegrityCheck.allocationSize(value.`check`) - ) + override fun allocationSize(value: SignatureVerify) = when(value) { + is SignatureVerify.EcdsaSecp256r1 -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterTypeEcdsaVerifyingKey.allocationSize(value.`key`) + ) + } + is SignatureVerify.EcdsaSecp384r1 -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterTypeEcdsaVerifyingKey.allocationSize(value.`key`) + ) + } + is SignatureVerify.Ed25519 -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterTypeEd25519VerifyingKey.allocationSize(value.`key`) + ) + } + is SignatureVerify.RsaPkcs1V15Sha256 -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterTypeRsaVerifyingKey.allocationSize(value.`key`) + ) + } + is SignatureVerify.RsaPssSha256 -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterTypeRsaVerifyingKey.allocationSize(value.`key`) + ) + } + is SignatureVerify.Custom -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterTypeSignatureCustomVerify.allocationSize(value.`verify`) + ) + } + } - override fun write(value: UpdaterIntegrityOptions, buf: ByteBuffer) { - FfiConverterOptionalTypeIntegrityPolicy.write(value.`policy`, buf) - FfiConverterOptionalTypeIntegrityCheck.write(value.`check`, buf) + override fun write(value: SignatureVerify, buf: ByteBuffer) { + when(value) { + is SignatureVerify.EcdsaSecp256r1 -> { + buf.putInt(1) + FfiConverterTypeEcdsaVerifyingKey.write(value.`key`, buf) + Unit + } + is SignatureVerify.EcdsaSecp384r1 -> { + buf.putInt(2) + FfiConverterTypeEcdsaVerifyingKey.write(value.`key`, buf) + Unit + } + is SignatureVerify.Ed25519 -> { + buf.putInt(3) + FfiConverterTypeEd25519VerifyingKey.write(value.`key`, buf) + Unit + } + is SignatureVerify.RsaPkcs1V15Sha256 -> { + buf.putInt(4) + FfiConverterTypeRsaVerifyingKey.write(value.`key`, buf) + Unit + } + is SignatureVerify.RsaPssSha256 -> { + buf.putInt(5) + FfiConverterTypeRsaVerifyingKey.write(value.`key`, buf) + Unit + } + is SignatureVerify.Custom -> { + buf.putInt(6) + FfiConverterTypeSignatureCustomVerify.write(value.`verify`, buf) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } } } + + /** - * Optional configuration for the [`Updater`]. + * Which bundles a load-time verification applies to */ -data class UpdaterOptions ( + +enum class SourceIntegrityCheckMode { + /** - * Release channel (e.g. `"stable"`, `"beta"`). Passed as a query parameter to the remote. + * Verify both builtin and remote bundles. + * + * Builtin bundles ship inside the application, so the builtin manifest must carry the + * metadata being verified for the check to have anything to work with. */ - var `channel`: kotlin.String? = null - , - var `integrity`: UpdaterIntegrityOptions? = null - , - var `signature`: UpdaterSignatureOptions? = null - -): Disposable{ - + ALL, + /** + * Verify downloaded (remote) bundles only. This is the default. + */ + ONLY_REMOTE; - - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - - Disposable.destroy( - this.`channel`, - this.`integrity`, - this.`signature` - ) - } - + companion object } + /** * @suppress - */ -public object FfiConverterTypeUpdaterOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): UpdaterOptions { - return UpdaterOptions( - FfiConverterOptionalString.read(buf), - FfiConverterOptionalTypeUpdaterIntegrityOptions.read(buf), - FfiConverterOptionalTypeUpdaterSignatureOptions.read(buf), - ) + */ +public object FfiConverterTypeSourceIntegrityCheckMode: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + SourceIntegrityCheckMode.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) } - override fun allocationSize(value: UpdaterOptions) = ( - FfiConverterOptionalString.allocationSize(value.`channel`) + - FfiConverterOptionalTypeUpdaterIntegrityOptions.allocationSize(value.`integrity`) + - FfiConverterOptionalTypeUpdaterSignatureOptions.allocationSize(value.`signature`) - ) + override fun allocationSize(value: SourceIntegrityCheckMode) = 4UL - override fun write(value: UpdaterOptions, buf: ByteBuffer) { - FfiConverterOptionalString.write(value.`channel`, buf) - FfiConverterOptionalTypeUpdaterIntegrityOptions.write(value.`integrity`, buf) - FfiConverterOptionalTypeUpdaterSignatureOptions.write(value.`signature`, buf) + override fun write(value: SourceIntegrityCheckMode, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) } } -data class UpdaterSignatureOptions ( - /** - * When set, the updater verifies the bundle signature over its integrity string before - * applying an update — with a declarative public key or a custom function. - * Verified independently of `integrity_policy` — keep the policy enabled for the - * signature to also authenticate the bundle bytes. - */ - var `verify`: SignatureVerification? = null - -): Disposable{ - - +/** + * Whether a bundle was loaded from the builtin (read-only, shipped with the app) + * or the remote (writable, downloaded at runtime) directory. + */ + +enum class SourceKind { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - - Disposable.destroy( - this.`verify` - ) - } + BUILTIN, + REMOTE; + + + companion object } + /** * @suppress */ -public object FfiConverterTypeUpdaterSignatureOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): UpdaterSignatureOptions { - return UpdaterSignatureOptions( - FfiConverterOptionalTypeSignatureVerification.read(buf), - ) +public object FfiConverterTypeSourceKind: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + SourceKind.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) } - override fun allocationSize(value: UpdaterSignatureOptions) = ( - FfiConverterOptionalTypeSignatureVerification.allocationSize(value.`verify`) - ) + override fun allocationSize(value: SourceKind) = 4UL - override fun write(value: UpdaterSignatureOptions, buf: ByteBuffer) { - FfiConverterOptionalTypeSignatureVerification.write(value.`verify`, buf) + override fun write(value: SourceKind, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) } } -/** - * How the bundle name is resolved from the request uri. - */ -sealed class BundleResolver { - - /** - * From the uri hostname. - * - * - `segment`: which part of the host to use (default: [`HostnameSegment::First`]). - * - `allow_wvb_suffix_only`: only resolve hosts ending in `.wvb` (default: false). - */ - data class Hostname( - val `segment`: dev.wvb.HostnameSegment?, - val `allowWvbSuffixOnly`: kotlin.Boolean?) : BundleResolver() - - { - - companion object - } + +sealed class UpdaterDownloadResultKind { - /** - * From the uri pathname. - * - * - `segment_index`: 0-based over non-empty path segments (default: 0). - * e.g. `app://_/my-app/index.html` with index 0 -> bundle `my-app`. - */ - data class Pathname( - val `segmentIndex`: kotlin.UInt?) : BundleResolver() + object Downloaded : UpdaterDownloadResultKind() + + + data class Error( + val v1: dev.wvb.WebviewBundleException) : UpdaterDownloadResultKind() { @@ -9414,49 +11091,42 @@ sealed class BundleResolver { /** * @suppress */ -public object FfiConverterTypeBundleResolver : FfiConverterRustBuffer{ - override fun read(buf: ByteBuffer): BundleResolver { +public object FfiConverterTypeUpdaterDownloadResultKind : FfiConverterRustBuffer{ + override fun read(buf: ByteBuffer): UpdaterDownloadResultKind { return when(buf.getInt()) { - 1 -> BundleResolver.Hostname( - FfiConverterOptionalTypeHostnameSegment.read(buf), - FfiConverterOptionalBoolean.read(buf), - ) - 2 -> BundleResolver.Pathname( - FfiConverterOptionalUInt.read(buf), + 1 -> UpdaterDownloadResultKind.Downloaded + 2 -> UpdaterDownloadResultKind.Error( + FfiConverterTypeWebviewBundleError.read(buf), ) else -> throw RuntimeException("invalid enum value, something is very wrong!!") } } - override fun allocationSize(value: BundleResolver) = when(value) { - is BundleResolver.Hostname -> { + override fun allocationSize(value: UpdaterDownloadResultKind) = when(value) { + is UpdaterDownloadResultKind.Downloaded -> { // Add the size for the Int that specifies the variant plus the size needed for all fields ( 4UL - + FfiConverterOptionalTypeHostnameSegment.allocationSize(value.`segment`) - + FfiConverterOptionalBoolean.allocationSize(value.`allowWvbSuffixOnly`) ) } - is BundleResolver.Pathname -> { + is UpdaterDownloadResultKind.Error -> { // Add the size for the Int that specifies the variant plus the size needed for all fields ( 4UL - + FfiConverterOptionalUInt.allocationSize(value.`segmentIndex`) + + FfiConverterTypeWebviewBundleError.allocationSize(value.v1) ) } } - override fun write(value: BundleResolver, buf: ByteBuffer) { + override fun write(value: UpdaterDownloadResultKind, buf: ByteBuffer) { when(value) { - is BundleResolver.Hostname -> { + is UpdaterDownloadResultKind.Downloaded -> { buf.putInt(1) - FfiConverterOptionalTypeHostnameSegment.write(value.`segment`, buf) - FfiConverterOptionalBoolean.write(value.`allowWvbSuffixOnly`, buf) Unit } - is BundleResolver.Pathname -> { + is UpdaterDownloadResultKind.Error -> { buf.putInt(2) - FfiConverterOptionalUInt.write(value.`segmentIndex`, buf) + FfiConverterTypeWebviewBundleError.write(value.v1, buf) Unit } }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } @@ -9467,83 +11137,114 @@ public object FfiConverterTypeBundleResolver : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - BundleSourceKind.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: BundleSourceKind) = 4UL - - override fun write(value: BundleSourceKind, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) + companion object } -} - - - - - -/** - * Which bundles a load-time verification applies to - */ + -enum class BundleSourceVerifyMode { - /** - * Verify both builtin and remote bundles. - * - * Builtin bundles ship inside the application, so the builtin manifest must carry the - * metadata being verified for the check to have anything to work with. - */ - ALL, - /** - * Verify downloaded (remote) bundles only. This is the default. - */ - ONLY_REMOTE; + companion object } - /** * @suppress */ -public object FfiConverterTypeBundleSourceVerifyMode: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - BundleSourceVerifyMode.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) +public object FfiConverterTypeUpdaterInstallResultKind : FfiConverterRustBuffer{ + override fun read(buf: ByteBuffer): UpdaterInstallResultKind { + return when(buf.getInt()) { + 1 -> UpdaterInstallResultKind.Installed + 2 -> UpdaterInstallResultKind.StagedVersionNotMatched + 3 -> UpdaterInstallResultKind.StagedBundleNotExists + 4 -> UpdaterInstallResultKind.VerifyFailed + 5 -> UpdaterInstallResultKind.Error( + FfiConverterTypeWebviewBundleError.read(buf), + ) + else -> throw RuntimeException("invalid enum value, something is very wrong!!") + } } - override fun allocationSize(value: BundleSourceVerifyMode) = 4UL + override fun allocationSize(value: UpdaterInstallResultKind) = when(value) { + is UpdaterInstallResultKind.Installed -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + ) + } + is UpdaterInstallResultKind.StagedVersionNotMatched -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + ) + } + is UpdaterInstallResultKind.StagedBundleNotExists -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + ) + } + is UpdaterInstallResultKind.VerifyFailed -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + ) + } + is UpdaterInstallResultKind.Error -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterTypeWebviewBundleError.allocationSize(value.v1) + ) + } + } - override fun write(value: BundleSourceVerifyMode, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) + override fun write(value: UpdaterInstallResultKind, buf: ByteBuffer) { + when(value) { + is UpdaterInstallResultKind.Installed -> { + buf.putInt(1) + Unit + } + is UpdaterInstallResultKind.StagedVersionNotMatched -> { + buf.putInt(2) + Unit + } + is UpdaterInstallResultKind.StagedBundleNotExists -> { + buf.putInt(3) + Unit + } + is UpdaterInstallResultKind.VerifyFailed -> { + buf.putInt(4) + Unit + } + is UpdaterInstallResultKind.Error -> { + buf.putInt(5) + FfiConverterTypeWebviewBundleError.write(value.v1, buf) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } } } @@ -9551,34 +11252,22 @@ public object FfiConverterTypeBundleSourceVerifyMode: FfiConverterRustBuffer `app`) - */ - object First : HostnameSegment() + object RolledBack : UpdaterRollbackResultKind() - /** - * Full hostname. (e.g. `app.wvb` -> `app.wvb`) - */ - object Full : HostnameSegment() + object PreviousVersionNotMatched : UpdaterRollbackResultKind() - /** - * Strip the last segment. (e.g. `a.b.wvb` -> `a.b`) - */ - object StripSuffix : HostnameSegment() + object PreviousBundleNotExists : UpdaterRollbackResultKind() - /** - * The nth segment, 0-based. - */ - data class Nth( - val `index`: kotlin.UInt) : HostnameSegment() + object VerifyFailed : UpdaterRollbackResultKind() + + + data class Error( + val v1: dev.wvb.WebviewBundleException) : UpdaterRollbackResultKind() { @@ -9599,64 +11288,75 @@ sealed class HostnameSegment { /** * @suppress */ -public object FfiConverterTypeHostnameSegment : FfiConverterRustBuffer{ - override fun read(buf: ByteBuffer): HostnameSegment { +public object FfiConverterTypeUpdaterRollbackResultKind : FfiConverterRustBuffer{ + override fun read(buf: ByteBuffer): UpdaterRollbackResultKind { return when(buf.getInt()) { - 1 -> HostnameSegment.First - 2 -> HostnameSegment.Full - 3 -> HostnameSegment.StripSuffix - 4 -> HostnameSegment.Nth( - FfiConverterUInt.read(buf), + 1 -> UpdaterRollbackResultKind.RolledBack + 2 -> UpdaterRollbackResultKind.PreviousVersionNotMatched + 3 -> UpdaterRollbackResultKind.PreviousBundleNotExists + 4 -> UpdaterRollbackResultKind.VerifyFailed + 5 -> UpdaterRollbackResultKind.Error( + FfiConverterTypeWebviewBundleError.read(buf), ) else -> throw RuntimeException("invalid enum value, something is very wrong!!") } } - override fun allocationSize(value: HostnameSegment) = when(value) { - is HostnameSegment.First -> { + override fun allocationSize(value: UpdaterRollbackResultKind) = when(value) { + is UpdaterRollbackResultKind.RolledBack -> { // Add the size for the Int that specifies the variant plus the size needed for all fields ( 4UL ) } - is HostnameSegment.Full -> { + is UpdaterRollbackResultKind.PreviousVersionNotMatched -> { // Add the size for the Int that specifies the variant plus the size needed for all fields ( 4UL ) } - is HostnameSegment.StripSuffix -> { + is UpdaterRollbackResultKind.PreviousBundleNotExists -> { // Add the size for the Int that specifies the variant plus the size needed for all fields ( 4UL ) } - is HostnameSegment.Nth -> { + is UpdaterRollbackResultKind.VerifyFailed -> { // Add the size for the Int that specifies the variant plus the size needed for all fields ( 4UL - + FfiConverterUInt.allocationSize(value.`index`) + ) + } + is UpdaterRollbackResultKind.Error -> { + // Add the size for the Int that specifies the variant plus the size needed for all fields + ( + 4UL + + FfiConverterTypeWebviewBundleError.allocationSize(value.v1) ) } } - override fun write(value: HostnameSegment, buf: ByteBuffer) { + override fun write(value: UpdaterRollbackResultKind, buf: ByteBuffer) { when(value) { - is HostnameSegment.First -> { + is UpdaterRollbackResultKind.RolledBack -> { buf.putInt(1) Unit } - is HostnameSegment.Full -> { + is UpdaterRollbackResultKind.PreviousVersionNotMatched -> { buf.putInt(2) Unit } - is HostnameSegment.StripSuffix -> { + is UpdaterRollbackResultKind.PreviousBundleNotExists -> { buf.putInt(3) Unit } - is HostnameSegment.Nth -> { - buf.putInt(4) - FfiConverterUInt.write(value.`index`, buf) + is UpdaterRollbackResultKind.VerifyFailed -> { + buf.putInt(4) + Unit + } + is UpdaterRollbackResultKind.Error -> { + buf.putInt(5) + FfiConverterTypeWebviewBundleError.write(value.v1, buf) Unit } }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } @@ -9668,20 +11368,12 @@ public object FfiConverterTypeHostnameSegment : FfiConverterRustBuffer { +public object FfiConverterTypeVersion: FfiConverterRustBuffer { override fun read(buf: ByteBuffer) = try { - HttpMethod.values()[buf.getInt() - 1] + Version.values()[buf.getInt() - 1] } catch (e: IndexOutOfBoundsException) { throw RuntimeException("invalid enum value, something is very wrong!!", e) } - override fun allocationSize(value: HttpMethod) = 4UL + override fun allocationSize(value: Version) = 4UL - override fun write(value: HttpMethod, buf: ByteBuffer) { + override fun write(value: Version, buf: ByteBuffer) { buf.putInt(value.ordinal + 1) } } @@ -9711,668 +11403,768 @@ public object FfiConverterTypeHttpMethod: FfiConverterRustBuffer { + + /** - * Hash algorithm used to compute the [`Subresource Integrity`](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) digest of a bundle. + * WebviewBundle Error. + */ +sealed class WebviewBundleException(message: String): kotlin.Exception(message) { + + class CoreIo(message: String) : WebviewBundleException(message) + + class CoreCompress(message: String) : WebviewBundleException(message) + + class CoreDecompress(message: String) : WebviewBundleException(message) + + class CoreEncode(message: String) : WebviewBundleException(message) + + class CoreDecode(message: String) : WebviewBundleException(message) + + class CoreHttp(message: String) : WebviewBundleException(message) + + class CoreHttpInvalidUri(message: String) : WebviewBundleException(message) + + class CoreCancelled(message: String) : WebviewBundleException(message) + + class CoreTimeout(message: String) : WebviewBundleException(message) + + class CoreInvalidMagicNum(message: String) : WebviewBundleException(message) + + class CoreInvalidVersion(message: String) : WebviewBundleException(message) + + class CoreInvalidHeaderChecksum(message: String) : WebviewBundleException(message) + + class CoreInvalidIndexChecksum(message: String) : WebviewBundleException(message) + + class CoreChecksumMismatch(message: String) : WebviewBundleException(message) + + class CoreBundleNotFound(message: String) : WebviewBundleException(message) + + class CoreInvalidFilepath(message: String) : WebviewBundleException(message) + + class CoreSerdeJson(message: String) : WebviewBundleException(message) + + class CoreCannotResolveProxyServer(message: String) : WebviewBundleException(message) + + class CoreHttpClient(message: String) : WebviewBundleException(message) + + class CoreRemoteHttp(message: String) : WebviewBundleException(message) + + class CoreBadRemoteResponse(message: String) : WebviewBundleException(message) + + class CoreInvalidRemoteConfig(message: String) : WebviewBundleException(message) + + class CoreInvalidUpdaterConfig(message: String) : WebviewBundleException(message) + + class CoreInvalidIntegrity(message: String) : WebviewBundleException(message) + + class CoreIntegrityRequired(message: String) : WebviewBundleException(message) + + class CoreIntegrityVerifyFailed(message: String) : WebviewBundleException(message) + + class CoreInvalidSignature(message: String) : WebviewBundleException(message) + + class CoreInvalidSignatureKey(message: String) : WebviewBundleException(message) + + class CoreExpectSignatureNotFound(message: String) : WebviewBundleException(message) + + class CoreSignatureVerifyFailed(message: String) : WebviewBundleException(message) + + class CoreGeneric(message: String) : WebviewBundleException(message) + + /** + * Invalid HTTP header name. + */ + class BindingInvalidHeaderName(message: String) : WebviewBundleException(message) + + /** + * Invalid HTTP header value. + */ + class BindingInvalidHeaderValue(message: String) : WebviewBundleException(message) + + /** + * The signature verify options passed across the boundary could not be turned into a + * verifier (a key of the wrong shape, or one the algorithm cannot read). + */ + class BindingInvalidSignatureOptions(message: String) : WebviewBundleException(message) + + + companion object ErrorHandler : UniffiRustCallStatusErrorHandler { + override fun lift(error_buf: RustBuffer.ByValue): WebviewBundleException = FfiConverterTypeWebviewBundleError.lift(error_buf) + } +} + +/** + * @suppress */ +public object FfiConverterTypeWebviewBundleError : FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): WebviewBundleException { + + return when(buf.getInt()) { + 1 -> WebviewBundleException.CoreIo(FfiConverterString.read(buf)) + 2 -> WebviewBundleException.CoreCompress(FfiConverterString.read(buf)) + 3 -> WebviewBundleException.CoreDecompress(FfiConverterString.read(buf)) + 4 -> WebviewBundleException.CoreEncode(FfiConverterString.read(buf)) + 5 -> WebviewBundleException.CoreDecode(FfiConverterString.read(buf)) + 6 -> WebviewBundleException.CoreHttp(FfiConverterString.read(buf)) + 7 -> WebviewBundleException.CoreHttpInvalidUri(FfiConverterString.read(buf)) + 8 -> WebviewBundleException.CoreCancelled(FfiConverterString.read(buf)) + 9 -> WebviewBundleException.CoreTimeout(FfiConverterString.read(buf)) + 10 -> WebviewBundleException.CoreInvalidMagicNum(FfiConverterString.read(buf)) + 11 -> WebviewBundleException.CoreInvalidVersion(FfiConverterString.read(buf)) + 12 -> WebviewBundleException.CoreInvalidHeaderChecksum(FfiConverterString.read(buf)) + 13 -> WebviewBundleException.CoreInvalidIndexChecksum(FfiConverterString.read(buf)) + 14 -> WebviewBundleException.CoreChecksumMismatch(FfiConverterString.read(buf)) + 15 -> WebviewBundleException.CoreBundleNotFound(FfiConverterString.read(buf)) + 16 -> WebviewBundleException.CoreInvalidFilepath(FfiConverterString.read(buf)) + 17 -> WebviewBundleException.CoreSerdeJson(FfiConverterString.read(buf)) + 18 -> WebviewBundleException.CoreCannotResolveProxyServer(FfiConverterString.read(buf)) + 19 -> WebviewBundleException.CoreHttpClient(FfiConverterString.read(buf)) + 20 -> WebviewBundleException.CoreRemoteHttp(FfiConverterString.read(buf)) + 21 -> WebviewBundleException.CoreBadRemoteResponse(FfiConverterString.read(buf)) + 22 -> WebviewBundleException.CoreInvalidRemoteConfig(FfiConverterString.read(buf)) + 23 -> WebviewBundleException.CoreInvalidUpdaterConfig(FfiConverterString.read(buf)) + 24 -> WebviewBundleException.CoreInvalidIntegrity(FfiConverterString.read(buf)) + 25 -> WebviewBundleException.CoreIntegrityRequired(FfiConverterString.read(buf)) + 26 -> WebviewBundleException.CoreIntegrityVerifyFailed(FfiConverterString.read(buf)) + 27 -> WebviewBundleException.CoreInvalidSignature(FfiConverterString.read(buf)) + 28 -> WebviewBundleException.CoreInvalidSignatureKey(FfiConverterString.read(buf)) + 29 -> WebviewBundleException.CoreExpectSignatureNotFound(FfiConverterString.read(buf)) + 30 -> WebviewBundleException.CoreSignatureVerifyFailed(FfiConverterString.read(buf)) + 31 -> WebviewBundleException.CoreGeneric(FfiConverterString.read(buf)) + 32 -> WebviewBundleException.BindingInvalidHeaderName(FfiConverterString.read(buf)) + 33 -> WebviewBundleException.BindingInvalidHeaderValue(FfiConverterString.read(buf)) + 34 -> WebviewBundleException.BindingInvalidSignatureOptions(FfiConverterString.read(buf)) + else -> throw RuntimeException("invalid error enum value, something is very wrong!!") + } + + } -enum class IntegrityAlgorithm { - - SHA256, - SHA384, - SHA512; + override fun allocationSize(value: WebviewBundleException): ULong { + return 4UL + } + + override fun write(value: WebviewBundleException, buf: ByteBuffer) { + when(value) { + is WebviewBundleException.CoreIo -> { + buf.putInt(1) + Unit + } + is WebviewBundleException.CoreCompress -> { + buf.putInt(2) + Unit + } + is WebviewBundleException.CoreDecompress -> { + buf.putInt(3) + Unit + } + is WebviewBundleException.CoreEncode -> { + buf.putInt(4) + Unit + } + is WebviewBundleException.CoreDecode -> { + buf.putInt(5) + Unit + } + is WebviewBundleException.CoreHttp -> { + buf.putInt(6) + Unit + } + is WebviewBundleException.CoreHttpInvalidUri -> { + buf.putInt(7) + Unit + } + is WebviewBundleException.CoreCancelled -> { + buf.putInt(8) + Unit + } + is WebviewBundleException.CoreTimeout -> { + buf.putInt(9) + Unit + } + is WebviewBundleException.CoreInvalidMagicNum -> { + buf.putInt(10) + Unit + } + is WebviewBundleException.CoreInvalidVersion -> { + buf.putInt(11) + Unit + } + is WebviewBundleException.CoreInvalidHeaderChecksum -> { + buf.putInt(12) + Unit + } + is WebviewBundleException.CoreInvalidIndexChecksum -> { + buf.putInt(13) + Unit + } + is WebviewBundleException.CoreChecksumMismatch -> { + buf.putInt(14) + Unit + } + is WebviewBundleException.CoreBundleNotFound -> { + buf.putInt(15) + Unit + } + is WebviewBundleException.CoreInvalidFilepath -> { + buf.putInt(16) + Unit + } + is WebviewBundleException.CoreSerdeJson -> { + buf.putInt(17) + Unit + } + is WebviewBundleException.CoreCannotResolveProxyServer -> { + buf.putInt(18) + Unit + } + is WebviewBundleException.CoreHttpClient -> { + buf.putInt(19) + Unit + } + is WebviewBundleException.CoreRemoteHttp -> { + buf.putInt(20) + Unit + } + is WebviewBundleException.CoreBadRemoteResponse -> { + buf.putInt(21) + Unit + } + is WebviewBundleException.CoreInvalidRemoteConfig -> { + buf.putInt(22) + Unit + } + is WebviewBundleException.CoreInvalidUpdaterConfig -> { + buf.putInt(23) + Unit + } + is WebviewBundleException.CoreInvalidIntegrity -> { + buf.putInt(24) + Unit + } + is WebviewBundleException.CoreIntegrityRequired -> { + buf.putInt(25) + Unit + } + is WebviewBundleException.CoreIntegrityVerifyFailed -> { + buf.putInt(26) + Unit + } + is WebviewBundleException.CoreInvalidSignature -> { + buf.putInt(27) + Unit + } + is WebviewBundleException.CoreInvalidSignatureKey -> { + buf.putInt(28) + Unit + } + is WebviewBundleException.CoreExpectSignatureNotFound -> { + buf.putInt(29) + Unit + } + is WebviewBundleException.CoreSignatureVerifyFailed -> { + buf.putInt(30) + Unit + } + is WebviewBundleException.CoreGeneric -> { + buf.putInt(31) + Unit + } + is WebviewBundleException.BindingInvalidHeaderName -> { + buf.putInt(32) + Unit + } + is WebviewBundleException.BindingInvalidHeaderValue -> { + buf.putInt(33) + Unit + } + is WebviewBundleException.BindingInvalidSignatureOptions -> { + buf.putInt(34) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + } - +} - companion object -} /** * @suppress */ -public object FfiConverterTypeIntegrityAlgorithm: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - IntegrityAlgorithm.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) +public object FfiConverterOptionalUInt: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): kotlin.UInt? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterUInt.read(buf) } - override fun allocationSize(value: IntegrityAlgorithm) = 4UL + override fun allocationSize(value: kotlin.UInt?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterUInt.allocationSize(value) + } + } - override fun write(value: IntegrityAlgorithm, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) + override fun write(value: kotlin.UInt?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterUInt.write(value, buf) + } } } - -/** - * How a bundle's integrity metadata is treated when the integrity check runs. - * - * - `Strict`: integrity metadata is required; a bundle without it fails the check. - * - `Optional`: integrity metadata is checked when present and tolerated when missing. - * - `Off`: the integrity check is disabled. - */ - -enum class IntegrityPolicy { - - STRICT, - OPTIONAL, - OFF; - - - - - companion object -} - - /** * @suppress */ -public object FfiConverterTypeIntegrityPolicy: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - IntegrityPolicy.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) +public object FfiConverterOptionalULong: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): kotlin.ULong? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterULong.read(buf) } - override fun allocationSize(value: IntegrityPolicy) = 4UL + override fun allocationSize(value: kotlin.ULong?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterULong.allocationSize(value) + } + } - override fun write(value: IntegrityPolicy, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) + override fun write(value: kotlin.ULong?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterULong.write(value, buf) + } } } - /** - * How the file path in the bundle is resolved from the request uri. + * @suppress */ +public object FfiConverterOptionalBoolean: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): kotlin.Boolean? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterBoolean.read(buf) + } -enum class PathResolver { - - /** - * Use the uri path as-is (only percent-decoded). - */ - EXACT, - /** - * Directory index: `/` -> `/index.html` and `/about` -> `/about/index.html`. - */ - DIRECTORY_INDEX, - /** - * `.html` extension: `/` -> `/index.html` and `/about` -> `/about.html`. - */ - HTML_EXTENSION; + override fun allocationSize(value: kotlin.Boolean?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterBoolean.allocationSize(value) + } + } - + override fun write(value: kotlin.Boolean?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterBoolean.write(value, buf) + } + } +} - companion object -} /** * @suppress */ -public object FfiConverterTypePathResolver: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - PathResolver.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) +public object FfiConverterOptionalString: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): kotlin.String? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterString.read(buf) } - override fun allocationSize(value: PathResolver) = 4UL + override fun allocationSize(value: kotlin.String?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterString.allocationSize(value) + } + } - override fun write(value: PathResolver, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) + override fun write(value: kotlin.String?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterString.write(value, buf) + } } } - /** - * Digital signature algorithm used to verify bundle authenticity. + * @suppress */ +public object FfiConverterOptionalByteArray: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): kotlin.ByteArray? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterByteArray.read(buf) + } -enum class SignatureAlgorithm { - - ECDSA_SECP256R1, - ECDSA_SECP384R1, - ED25519, - RSA_PKCS1_V15, - RSA_PSS; + override fun allocationSize(value: kotlin.ByteArray?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterByteArray.allocationSize(value) + } + } - + override fun write(value: kotlin.ByteArray?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterByteArray.write(value, buf) + } + } +} - companion object -} /** * @suppress */ -public object FfiConverterTypeSignatureAlgorithm: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - SignatureAlgorithm.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) +public object FfiConverterOptionalTypeCancellation: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): Cancellation? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterTypeCancellation.read(buf) } - override fun allocationSize(value: SignatureAlgorithm) = 4UL + override fun allocationSize(value: Cancellation?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterTypeCancellation.allocationSize(value) + } + } - override fun write(value: SignatureAlgorithm, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) + override fun write(value: Cancellation?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterTypeCancellation.write(value, buf) + } } } - /** - * How a bundle's signature is verified: with a declarative public key, or a custom function. + * @suppress */ -sealed class SignatureVerification: Disposable { - - /** - * Verify with a public key of a known algorithm. - */ - data class Key( - val `options`: dev.wvb.SignatureVerifierOptions) : SignatureVerification() - - { - - - companion object +public object FfiConverterOptionalTypeRemoteOnDownload: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): RemoteOnDownload? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterTypeRemoteOnDownload.read(buf) } - - /** - * Verify with a custom function over the integrity string. - */ - data class Custom( - val `verify`: dev.wvb.SignatureVerify) : SignatureVerification() - - { - - companion object + override fun allocationSize(value: RemoteOnDownload?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterTypeRemoteOnDownload.allocationSize(value) + } } - - - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - when(this) { - is SignatureVerification.Key -> { - - Disposable.destroy( - this.`options` - ) - - } - is SignatureVerification.Custom -> { - - Disposable.destroy( - this.`verify` - ) - - } - }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + override fun write(value: RemoteOnDownload?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterTypeRemoteOnDownload.write(value, buf) + } } - +} - - - companion object -} /** * @suppress */ -public object FfiConverterTypeSignatureVerification : FfiConverterRustBuffer{ - override fun read(buf: ByteBuffer): SignatureVerification { - return when(buf.getInt()) { - 1 -> SignatureVerification.Key( - FfiConverterTypeSignatureVerifierOptions.read(buf), - ) - 2 -> SignatureVerification.Custom( - FfiConverterTypeSignatureVerify.read(buf), - ) - else -> throw RuntimeException("invalid enum value, something is very wrong!!") +public object FfiConverterOptionalTypeBuildHeaderOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): BuildHeaderOptions? { + if (buf.get().toInt() == 0) { + return null } + return FfiConverterTypeBuildHeaderOptions.read(buf) } - override fun allocationSize(value: SignatureVerification) = when(value) { - is SignatureVerification.Key -> { - // Add the size for the Int that specifies the variant plus the size needed for all fields - ( - 4UL - + FfiConverterTypeSignatureVerifierOptions.allocationSize(value.`options`) - ) - } - is SignatureVerification.Custom -> { - // Add the size for the Int that specifies the variant plus the size needed for all fields - ( - 4UL - + FfiConverterTypeSignatureVerify.allocationSize(value.`verify`) - ) + override fun allocationSize(value: BuildHeaderOptions?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterTypeBuildHeaderOptions.allocationSize(value) } } - override fun write(value: SignatureVerification, buf: ByteBuffer) { - when(value) { - is SignatureVerification.Key -> { - buf.putInt(1) - FfiConverterTypeSignatureVerifierOptions.write(value.`options`, buf) - Unit - } - is SignatureVerification.Custom -> { - buf.putInt(2) - FfiConverterTypeSignatureVerify.write(value.`verify`, buf) - Unit - } - }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + override fun write(value: BuildHeaderOptions?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterTypeBuildHeaderOptions.write(value, buf) + } } } - /** - * Encoding format of the public key provided in [`SignatureVerifyingKey`]. - * - * Not all combinations of algorithm + format are valid; unsupported pairs - * return [`Error::BindingInvalidSignatureOptions`] at construction time. + * @suppress */ +public object FfiConverterOptionalTypeBuildIndexOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): BuildIndexOptions? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterTypeBuildIndexOptions.read(buf) + } -enum class VerifyingKeyFormat { - - SPKI_DER, - SPKI_PEM, - PKCS1_DER, - PKCS1_PEM, - SEC1, - RAW; + override fun allocationSize(value: BuildIndexOptions?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterTypeBuildIndexOptions.allocationSize(value) + } + } - + override fun write(value: BuildIndexOptions?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterTypeBuildIndexOptions.write(value, buf) + } + } +} - companion object -} /** * @suppress */ -public object FfiConverterTypeVerifyingKeyFormat: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - VerifyingKeyFormat.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) +public object FfiConverterOptionalTypeBuildOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): BuildOptions? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterTypeBuildOptions.read(buf) } - override fun allocationSize(value: VerifyingKeyFormat) = 4UL + override fun allocationSize(value: BuildOptions?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterTypeBuildOptions.allocationSize(value) + } + } - override fun write(value: VerifyingKeyFormat, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) + override fun write(value: BuildOptions?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterTypeBuildOptions.write(value, buf) + } } } - /** - * Bundle format version. Currently only `V1` is defined. + * @suppress */ +public object FfiConverterOptionalTypeBundleProtocolOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): BundleProtocolOptions? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterTypeBundleProtocolOptions.read(buf) + } -enum class Version { - - V1; + override fun allocationSize(value: BundleProtocolOptions?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterTypeBundleProtocolOptions.allocationSize(value) + } + } - + override fun write(value: BundleProtocolOptions?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterTypeBundleProtocolOptions.write(value, buf) + } + } +} - companion object -} /** * @suppress */ -public object FfiConverterTypeVersion: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - Version.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) +public object FfiConverterOptionalTypeBundleSourceVersion: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): BundleSourceVersion? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterTypeBundleSourceVersion.read(buf) } - override fun allocationSize(value: Version) = 4UL + override fun allocationSize(value: BundleSourceVersion?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterTypeBundleSourceVersion.allocationSize(value) + } + } - override fun write(value: Version, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) + override fun write(value: BundleSourceVersion?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterTypeBundleSourceVersion.write(value, buf) + } } } - - - /** - * WebviewBundle Error. + * @suppress */ -sealed class WebviewBundleException(message: String): kotlin.Exception(message) { - - class CoreIo(message: String) : WebviewBundleException(message) - - class CoreCompress(message: String) : WebviewBundleException(message) - - class CoreDecompress(message: String) : WebviewBundleException(message) - - class CoreEncode(message: String) : WebviewBundleException(message) - - class CoreDecode(message: String) : WebviewBundleException(message) - - class CoreHttp(message: String) : WebviewBundleException(message) - - class CoreInvalidMagicNum(message: String) : WebviewBundleException(message) - - class CoreInvalidVersion(message: String) : WebviewBundleException(message) - - class CoreInvalidHeaderChecksum(message: String) : WebviewBundleException(message) - - class CoreInvalidIndexChecksum(message: String) : WebviewBundleException(message) - - class CoreChecksumMismatch(message: String) : WebviewBundleException(message) - - class CoreBundleNotFound(message: String) : WebviewBundleException(message) - - class CoreBundleEntryNotExists(message: String) : WebviewBundleException(message) - - class CoreBundleCannotBeRemoved(message: String) : WebviewBundleException(message) - - class CoreInvalidFilepath(message: String) : WebviewBundleException(message) - - class CoreSerdeJson(message: String) : WebviewBundleException(message) - - class CoreCannotResolveProxyServer(message: String) : WebviewBundleException(message) - - class CoreReqwest(message: String) : WebviewBundleException(message) - - class CoreInvalidRemoteUrl(message: String) : WebviewBundleException(message) - - class CoreInvalidRemoteBundle(message: String) : WebviewBundleException(message) - - class CoreRemoteBundleNotFound(message: String) : WebviewBundleException(message) - - class CoreRemoteForbidden(message: String) : WebviewBundleException(message) - - class CoreRemoteHttp(message: String) : WebviewBundleException(message) - - class CoreInvalidRemoteConfig(message: String) : WebviewBundleException(message) - - class CoreInvalidIntegrity(message: String) : WebviewBundleException(message) - - class CoreIntegrityRequired(message: String) : WebviewBundleException(message) - - class CoreIntegrityVerifyFailed(message: String) : WebviewBundleException(message) - - class CoreInvalidSignature(message: String) : WebviewBundleException(message) - - class CoreInvalidSigningKey(message: String) : WebviewBundleException(message) - - class CoreSignatureSignFailed(message: String) : WebviewBundleException(message) - - class CoreInvalidVerifyingKey(message: String) : WebviewBundleException(message) - - class CoreSignatureNotExists(message: String) : WebviewBundleException(message) - - class CoreSignatureVerifyFailed(message: String) : WebviewBundleException(message) - - class CoreGeneric(message: String) : WebviewBundleException(message) - - /** - * Invalid HTTP header name. - */ - class BindingInvalidHeaderName(message: String) : WebviewBundleException(message) - - /** - * Invalid HTTP header value. - */ - class BindingInvalidHeaderValue(message: String) : WebviewBundleException(message) - - /** - * The `SignatureVerifierOptions` passed across the boundary could not be turned into a - * verifier (unsupported algorithm/format pairing, or a key of the wrong shape). - */ - class BindingInvalidSignatureOptions(message: String) : WebviewBundleException(message) - +public object FfiConverterOptionalTypeChecksumReadOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ChecksumReadOptions? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterTypeChecksumReadOptions.read(buf) + } + + override fun allocationSize(value: ChecksumReadOptions?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterTypeChecksumReadOptions.allocationSize(value) + } + } - companion object ErrorHandler : UniffiRustCallStatusErrorHandler { - override fun lift(error_buf: RustBuffer.ByValue): WebviewBundleException = FfiConverterTypeWebviewBundleError.lift(error_buf) + override fun write(value: ChecksumReadOptions?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterTypeChecksumReadOptions.write(value, buf) + } } } + + + /** * @suppress */ -public object FfiConverterTypeWebviewBundleError : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): WebviewBundleException { - - return when(buf.getInt()) { - 1 -> WebviewBundleException.CoreIo(FfiConverterString.read(buf)) - 2 -> WebviewBundleException.CoreCompress(FfiConverterString.read(buf)) - 3 -> WebviewBundleException.CoreDecompress(FfiConverterString.read(buf)) - 4 -> WebviewBundleException.CoreEncode(FfiConverterString.read(buf)) - 5 -> WebviewBundleException.CoreDecode(FfiConverterString.read(buf)) - 6 -> WebviewBundleException.CoreHttp(FfiConverterString.read(buf)) - 7 -> WebviewBundleException.CoreInvalidMagicNum(FfiConverterString.read(buf)) - 8 -> WebviewBundleException.CoreInvalidVersion(FfiConverterString.read(buf)) - 9 -> WebviewBundleException.CoreInvalidHeaderChecksum(FfiConverterString.read(buf)) - 10 -> WebviewBundleException.CoreInvalidIndexChecksum(FfiConverterString.read(buf)) - 11 -> WebviewBundleException.CoreChecksumMismatch(FfiConverterString.read(buf)) - 12 -> WebviewBundleException.CoreBundleNotFound(FfiConverterString.read(buf)) - 13 -> WebviewBundleException.CoreBundleEntryNotExists(FfiConverterString.read(buf)) - 14 -> WebviewBundleException.CoreBundleCannotBeRemoved(FfiConverterString.read(buf)) - 15 -> WebviewBundleException.CoreInvalidFilepath(FfiConverterString.read(buf)) - 16 -> WebviewBundleException.CoreSerdeJson(FfiConverterString.read(buf)) - 17 -> WebviewBundleException.CoreCannotResolveProxyServer(FfiConverterString.read(buf)) - 18 -> WebviewBundleException.CoreReqwest(FfiConverterString.read(buf)) - 19 -> WebviewBundleException.CoreInvalidRemoteUrl(FfiConverterString.read(buf)) - 20 -> WebviewBundleException.CoreInvalidRemoteBundle(FfiConverterString.read(buf)) - 21 -> WebviewBundleException.CoreRemoteBundleNotFound(FfiConverterString.read(buf)) - 22 -> WebviewBundleException.CoreRemoteForbidden(FfiConverterString.read(buf)) - 23 -> WebviewBundleException.CoreRemoteHttp(FfiConverterString.read(buf)) - 24 -> WebviewBundleException.CoreInvalidRemoteConfig(FfiConverterString.read(buf)) - 25 -> WebviewBundleException.CoreInvalidIntegrity(FfiConverterString.read(buf)) - 26 -> WebviewBundleException.CoreIntegrityRequired(FfiConverterString.read(buf)) - 27 -> WebviewBundleException.CoreIntegrityVerifyFailed(FfiConverterString.read(buf)) - 28 -> WebviewBundleException.CoreInvalidSignature(FfiConverterString.read(buf)) - 29 -> WebviewBundleException.CoreInvalidSigningKey(FfiConverterString.read(buf)) - 30 -> WebviewBundleException.CoreSignatureSignFailed(FfiConverterString.read(buf)) - 31 -> WebviewBundleException.CoreInvalidVerifyingKey(FfiConverterString.read(buf)) - 32 -> WebviewBundleException.CoreSignatureNotExists(FfiConverterString.read(buf)) - 33 -> WebviewBundleException.CoreSignatureVerifyFailed(FfiConverterString.read(buf)) - 34 -> WebviewBundleException.CoreGeneric(FfiConverterString.read(buf)) - 35 -> WebviewBundleException.BindingInvalidHeaderName(FfiConverterString.read(buf)) - 36 -> WebviewBundleException.BindingInvalidHeaderValue(FfiConverterString.read(buf)) - 37 -> WebviewBundleException.BindingInvalidSignatureOptions(FfiConverterString.read(buf)) - else -> throw RuntimeException("invalid error enum value, something is very wrong!!") +public object FfiConverterOptionalTypeChecksumWriteOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ChecksumWriteOptions? { + if (buf.get().toInt() == 0) { + return null } - + return FfiConverterTypeChecksumWriteOptions.read(buf) } - override fun allocationSize(value: WebviewBundleException): ULong { - return 4UL + override fun allocationSize(value: ChecksumWriteOptions?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterTypeChecksumWriteOptions.allocationSize(value) + } } - override fun write(value: WebviewBundleException, buf: ByteBuffer) { - when(value) { - is WebviewBundleException.CoreIo -> { - buf.putInt(1) - Unit - } - is WebviewBundleException.CoreCompress -> { - buf.putInt(2) - Unit - } - is WebviewBundleException.CoreDecompress -> { - buf.putInt(3) - Unit - } - is WebviewBundleException.CoreEncode -> { - buf.putInt(4) - Unit - } - is WebviewBundleException.CoreDecode -> { - buf.putInt(5) - Unit - } - is WebviewBundleException.CoreHttp -> { - buf.putInt(6) - Unit - } - is WebviewBundleException.CoreInvalidMagicNum -> { - buf.putInt(7) - Unit - } - is WebviewBundleException.CoreInvalidVersion -> { - buf.putInt(8) - Unit - } - is WebviewBundleException.CoreInvalidHeaderChecksum -> { - buf.putInt(9) - Unit - } - is WebviewBundleException.CoreInvalidIndexChecksum -> { - buf.putInt(10) - Unit - } - is WebviewBundleException.CoreChecksumMismatch -> { - buf.putInt(11) - Unit - } - is WebviewBundleException.CoreBundleNotFound -> { - buf.putInt(12) - Unit - } - is WebviewBundleException.CoreBundleEntryNotExists -> { - buf.putInt(13) - Unit - } - is WebviewBundleException.CoreBundleCannotBeRemoved -> { - buf.putInt(14) - Unit - } - is WebviewBundleException.CoreInvalidFilepath -> { - buf.putInt(15) - Unit - } - is WebviewBundleException.CoreSerdeJson -> { - buf.putInt(16) - Unit - } - is WebviewBundleException.CoreCannotResolveProxyServer -> { - buf.putInt(17) - Unit - } - is WebviewBundleException.CoreReqwest -> { - buf.putInt(18) - Unit - } - is WebviewBundleException.CoreInvalidRemoteUrl -> { - buf.putInt(19) - Unit - } - is WebviewBundleException.CoreInvalidRemoteBundle -> { - buf.putInt(20) - Unit - } - is WebviewBundleException.CoreRemoteBundleNotFound -> { - buf.putInt(21) - Unit - } - is WebviewBundleException.CoreRemoteForbidden -> { - buf.putInt(22) - Unit - } - is WebviewBundleException.CoreRemoteHttp -> { - buf.putInt(23) - Unit - } - is WebviewBundleException.CoreInvalidRemoteConfig -> { - buf.putInt(24) - Unit - } - is WebviewBundleException.CoreInvalidIntegrity -> { - buf.putInt(25) - Unit - } - is WebviewBundleException.CoreIntegrityRequired -> { - buf.putInt(26) - Unit - } - is WebviewBundleException.CoreIntegrityVerifyFailed -> { - buf.putInt(27) - Unit - } - is WebviewBundleException.CoreInvalidSignature -> { - buf.putInt(28) - Unit - } - is WebviewBundleException.CoreInvalidSigningKey -> { - buf.putInt(29) - Unit - } - is WebviewBundleException.CoreSignatureSignFailed -> { - buf.putInt(30) - Unit - } - is WebviewBundleException.CoreInvalidVerifyingKey -> { - buf.putInt(31) - Unit - } - is WebviewBundleException.CoreSignatureNotExists -> { - buf.putInt(32) - Unit - } - is WebviewBundleException.CoreSignatureVerifyFailed -> { - buf.putInt(33) - Unit - } - is WebviewBundleException.CoreGeneric -> { - buf.putInt(34) - Unit - } - is WebviewBundleException.BindingInvalidHeaderName -> { - buf.putInt(35) - Unit - } - is WebviewBundleException.BindingInvalidHeaderValue -> { - buf.putInt(36) - Unit - } - is WebviewBundleException.BindingInvalidSignatureOptions -> { - buf.putInt(37) - Unit - } - }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + override fun write(value: ChecksumWriteOptions?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterTypeChecksumWriteOptions.write(value, buf) + } + } +} + + + + +/** + * @suppress + */ +public object FfiConverterOptionalTypeDataReadOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): DataReadOptions? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterTypeDataReadOptions.read(buf) + } + + override fun allocationSize(value: DataReadOptions?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterTypeDataReadOptions.allocationSize(value) + } } + override fun write(value: DataReadOptions?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterTypeDataReadOptions.write(value, buf) + } + } } @@ -10381,28 +12173,28 @@ public object FfiConverterTypeWebviewBundleError : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): kotlin.UInt? { +public object FfiConverterOptionalTypeHeaderReadOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): HeaderReadOptions? { if (buf.get().toInt() == 0) { return null } - return FfiConverterUInt.read(buf) + return FfiConverterTypeHeaderReadOptions.read(buf) } - override fun allocationSize(value: kotlin.UInt?): ULong { + override fun allocationSize(value: HeaderReadOptions?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterUInt.allocationSize(value) + return 1UL + FfiConverterTypeHeaderReadOptions.allocationSize(value) } } - override fun write(value: kotlin.UInt?, buf: ByteBuffer) { + override fun write(value: HeaderReadOptions?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterUInt.write(value, buf) + FfiConverterTypeHeaderReadOptions.write(value, buf) } } } @@ -10413,28 +12205,28 @@ public object FfiConverterOptionalUInt: FfiConverterRustBuffer { /** * @suppress */ -public object FfiConverterOptionalULong: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): kotlin.ULong? { +public object FfiConverterOptionalTypeHttpOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): HttpOptions? { if (buf.get().toInt() == 0) { return null } - return FfiConverterULong.read(buf) + return FfiConverterTypeHttpOptions.read(buf) } - override fun allocationSize(value: kotlin.ULong?): ULong { + override fun allocationSize(value: HttpOptions?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterULong.allocationSize(value) + return 1UL + FfiConverterTypeHttpOptions.allocationSize(value) } } - override fun write(value: kotlin.ULong?, buf: ByteBuffer) { + override fun write(value: HttpOptions?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterULong.write(value, buf) + FfiConverterTypeHttpOptions.write(value, buf) } } } @@ -10445,28 +12237,28 @@ public object FfiConverterOptionalULong: FfiConverterRustBuffer { /** * @suppress */ -public object FfiConverterOptionalBoolean: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): kotlin.Boolean? { +public object FfiConverterOptionalTypeIndexEntry: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): IndexEntry? { if (buf.get().toInt() == 0) { return null } - return FfiConverterBoolean.read(buf) + return FfiConverterTypeIndexEntry.read(buf) } - override fun allocationSize(value: kotlin.Boolean?): ULong { + override fun allocationSize(value: IndexEntry?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterBoolean.allocationSize(value) + return 1UL + FfiConverterTypeIndexEntry.allocationSize(value) } } - override fun write(value: kotlin.Boolean?, buf: ByteBuffer) { + override fun write(value: IndexEntry?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterBoolean.write(value, buf) + FfiConverterTypeIndexEntry.write(value, buf) } } } @@ -10477,28 +12269,28 @@ public object FfiConverterOptionalBoolean: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): kotlin.String? { +public object FfiConverterOptionalTypeIndexReadOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): IndexReadOptions? { if (buf.get().toInt() == 0) { return null } - return FfiConverterString.read(buf) + return FfiConverterTypeIndexReadOptions.read(buf) } - override fun allocationSize(value: kotlin.String?): ULong { + override fun allocationSize(value: IndexReadOptions?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterString.allocationSize(value) + return 1UL + FfiConverterTypeIndexReadOptions.allocationSize(value) } } - override fun write(value: kotlin.String?, buf: ByteBuffer) { + override fun write(value: IndexReadOptions?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterString.write(value, buf) + FfiConverterTypeIndexReadOptions.write(value, buf) } } } @@ -10509,28 +12301,28 @@ public object FfiConverterOptionalString: FfiConverterRustBuffer /** * @suppress */ -public object FfiConverterOptionalByteArray: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): kotlin.ByteArray? { +public object FfiConverterOptionalTypeManifestVersionData: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ManifestVersionData? { if (buf.get().toInt() == 0) { return null } - return FfiConverterByteArray.read(buf) + return FfiConverterTypeManifestVersionData.read(buf) } - override fun allocationSize(value: kotlin.ByteArray?): ULong { + override fun allocationSize(value: ManifestVersionData?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterByteArray.allocationSize(value) + return 1UL + FfiConverterTypeManifestVersionData.allocationSize(value) } } - override fun write(value: kotlin.ByteArray?, buf: ByteBuffer) { + override fun write(value: ManifestVersionData?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterByteArray.write(value, buf) + FfiConverterTypeManifestVersionData.write(value, buf) } } } @@ -10541,28 +12333,28 @@ public object FfiConverterOptionalByteArray: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): IntegrityCheck? { +public object FfiConverterOptionalTypeRemoteGetUpdateOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): RemoteGetUpdateOptions? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeIntegrityCheck.read(buf) + return FfiConverterTypeRemoteGetUpdateOptions.read(buf) } - override fun allocationSize(value: IntegrityCheck?): ULong { + override fun allocationSize(value: RemoteGetUpdateOptions?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeIntegrityCheck.allocationSize(value) + return 1UL + FfiConverterTypeRemoteGetUpdateOptions.allocationSize(value) } } - override fun write(value: IntegrityCheck?, buf: ByteBuffer) { + override fun write(value: RemoteGetUpdateOptions?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeIntegrityCheck.write(value, buf) + FfiConverterTypeRemoteGetUpdateOptions.write(value, buf) } } } @@ -10573,28 +12365,28 @@ public object FfiConverterOptionalTypeIntegrityCheck: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): RemoteOnDownload? { +public object FfiConverterOptionalTypeRemoteOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): RemoteOptions? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeRemoteOnDownload.read(buf) + return FfiConverterTypeRemoteOptions.read(buf) } - override fun allocationSize(value: RemoteOnDownload?): ULong { + override fun allocationSize(value: RemoteOptions?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeRemoteOnDownload.allocationSize(value) + return 1UL + FfiConverterTypeRemoteOptions.allocationSize(value) } } - override fun write(value: RemoteOnDownload?, buf: ByteBuffer) { + override fun write(value: RemoteOptions?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeRemoteOnDownload.write(value, buf) + FfiConverterTypeRemoteOptions.write(value, buf) } } } @@ -10605,28 +12397,28 @@ public object FfiConverterOptionalTypeRemoteOnDownload: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BuildHeaderOptions? { +public object FfiConverterOptionalTypeRemoteUpdateResponse: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): RemoteUpdateResponse? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeBuildHeaderOptions.read(buf) + return FfiConverterTypeRemoteUpdateResponse.read(buf) } - override fun allocationSize(value: BuildHeaderOptions?): ULong { + override fun allocationSize(value: RemoteUpdateResponse?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeBuildHeaderOptions.allocationSize(value) + return 1UL + FfiConverterTypeRemoteUpdateResponse.allocationSize(value) } } - override fun write(value: BuildHeaderOptions?, buf: ByteBuffer) { + override fun write(value: RemoteUpdateResponse?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeBuildHeaderOptions.write(value, buf) + FfiConverterTypeRemoteUpdateResponse.write(value, buf) } } } @@ -10637,28 +12429,28 @@ public object FfiConverterOptionalTypeBuildHeaderOptions: FfiConverterRustBuffer /** * @suppress */ -public object FfiConverterOptionalTypeBuildIndexOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BuildIndexOptions? { +public object FfiConverterOptionalTypeSignatureVerifyKey: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): SignatureVerifyKey? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeBuildIndexOptions.read(buf) + return FfiConverterTypeSignatureVerifyKey.read(buf) } - override fun allocationSize(value: BuildIndexOptions?): ULong { + override fun allocationSize(value: SignatureVerifyKey?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeBuildIndexOptions.allocationSize(value) + return 1UL + FfiConverterTypeSignatureVerifyKey.allocationSize(value) } } - override fun write(value: BuildIndexOptions?, buf: ByteBuffer) { + override fun write(value: SignatureVerifyKey?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeBuildIndexOptions.write(value, buf) + FfiConverterTypeSignatureVerifyKey.write(value, buf) } } } @@ -10669,28 +12461,28 @@ public object FfiConverterOptionalTypeBuildIndexOptions: FfiConverterRustBuffer< /** * @suppress */ -public object FfiConverterOptionalTypeBuildOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BuildOptions? { +public object FfiConverterOptionalTypeSourceIntegrityOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): SourceIntegrityOptions? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeBuildOptions.read(buf) + return FfiConverterTypeSourceIntegrityOptions.read(buf) } - override fun allocationSize(value: BuildOptions?): ULong { + override fun allocationSize(value: SourceIntegrityOptions?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeBuildOptions.allocationSize(value) + return 1UL + FfiConverterTypeSourceIntegrityOptions.allocationSize(value) } } - override fun write(value: BuildOptions?, buf: ByteBuffer) { + override fun write(value: SourceIntegrityOptions?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeBuildOptions.write(value, buf) + FfiConverterTypeSourceIntegrityOptions.write(value, buf) } } } @@ -10701,28 +12493,28 @@ public object FfiConverterOptionalTypeBuildOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleManifestMetadata? { +public object FfiConverterOptionalTypeUpdate: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): Update? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeBundleManifestMetadata.read(buf) + return FfiConverterTypeUpdate.read(buf) } - override fun allocationSize(value: BundleManifestMetadata?): ULong { + override fun allocationSize(value: Update?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeBundleManifestMetadata.allocationSize(value) + return 1UL + FfiConverterTypeUpdate.allocationSize(value) } } - override fun write(value: BundleManifestMetadata?, buf: ByteBuffer) { + override fun write(value: Update?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeBundleManifestMetadata.write(value, buf) + FfiConverterTypeUpdate.write(value, buf) } } } @@ -10733,28 +12525,28 @@ public object FfiConverterOptionalTypeBundleManifestMetadata: FfiConverterRustBu /** * @suppress */ -public object FfiConverterOptionalTypeBundleProtocolOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleProtocolOptions? { +public object FfiConverterOptionalTypeUpdateSignature: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdateSignature? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeBundleProtocolOptions.read(buf) + return FfiConverterTypeUpdateSignature.read(buf) } - override fun allocationSize(value: BundleProtocolOptions?): ULong { + override fun allocationSize(value: UpdateSignature?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeBundleProtocolOptions.allocationSize(value) + return 1UL + FfiConverterTypeUpdateSignature.allocationSize(value) } } - override fun write(value: BundleProtocolOptions?, buf: ByteBuffer) { + override fun write(value: UpdateSignature?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeBundleProtocolOptions.write(value, buf) + FfiConverterTypeUpdateSignature.write(value, buf) } } } @@ -10765,28 +12557,28 @@ public object FfiConverterOptionalTypeBundleProtocolOptions: FfiConverterRustBuf /** * @suppress */ -public object FfiConverterOptionalTypeBundleSourceIntegrityOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleSourceIntegrityOptions? { +public object FfiConverterOptionalTypeUpdaterDownloadOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterDownloadOptions? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeBundleSourceIntegrityOptions.read(buf) + return FfiConverterTypeUpdaterDownloadOptions.read(buf) } - override fun allocationSize(value: BundleSourceIntegrityOptions?): ULong { + override fun allocationSize(value: UpdaterDownloadOptions?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeBundleSourceIntegrityOptions.allocationSize(value) + return 1UL + FfiConverterTypeUpdaterDownloadOptions.allocationSize(value) } } - override fun write(value: BundleSourceIntegrityOptions?, buf: ByteBuffer) { + override fun write(value: UpdaterDownloadOptions?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeBundleSourceIntegrityOptions.write(value, buf) + FfiConverterTypeUpdaterDownloadOptions.write(value, buf) } } } @@ -10797,28 +12589,28 @@ public object FfiConverterOptionalTypeBundleSourceIntegrityOptions: FfiConverter /** * @suppress */ -public object FfiConverterOptionalTypeBundleSourceSignatureOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleSourceSignatureOptions? { +public object FfiConverterOptionalTypeUpdaterGetUpdateOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterGetUpdateOptions? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeBundleSourceSignatureOptions.read(buf) + return FfiConverterTypeUpdaterGetUpdateOptions.read(buf) } - override fun allocationSize(value: BundleSourceSignatureOptions?): ULong { + override fun allocationSize(value: UpdaterGetUpdateOptions?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeBundleSourceSignatureOptions.allocationSize(value) + return 1UL + FfiConverterTypeUpdaterGetUpdateOptions.allocationSize(value) } } - override fun write(value: BundleSourceSignatureOptions?, buf: ByteBuffer) { + override fun write(value: UpdaterGetUpdateOptions?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeBundleSourceSignatureOptions.write(value, buf) + FfiConverterTypeUpdaterGetUpdateOptions.write(value, buf) } } } @@ -10829,28 +12621,28 @@ public object FfiConverterOptionalTypeBundleSourceSignatureOptions: FfiConverter /** * @suppress */ -public object FfiConverterOptionalTypeBundleSourceVersion: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleSourceVersion? { +public object FfiConverterOptionalTypeUpdaterIntegrityOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterIntegrityOptions? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeBundleSourceVersion.read(buf) + return FfiConverterTypeUpdaterIntegrityOptions.read(buf) } - override fun allocationSize(value: BundleSourceVersion?): ULong { + override fun allocationSize(value: UpdaterIntegrityOptions?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeBundleSourceVersion.allocationSize(value) + return 1UL + FfiConverterTypeUpdaterIntegrityOptions.allocationSize(value) } } - override fun write(value: BundleSourceVersion?, buf: ByteBuffer) { + override fun write(value: UpdaterIntegrityOptions?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeBundleSourceVersion.write(value, buf) + FfiConverterTypeUpdaterIntegrityOptions.write(value, buf) } } } @@ -10861,28 +12653,28 @@ public object FfiConverterOptionalTypeBundleSourceVersion: FfiConverterRustBuffe /** * @suppress */ -public object FfiConverterOptionalTypeChecksumReadOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): ChecksumReadOptions? { +public object FfiConverterOptionalTypeUpdaterOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterOptions? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeChecksumReadOptions.read(buf) + return FfiConverterTypeUpdaterOptions.read(buf) } - override fun allocationSize(value: ChecksumReadOptions?): ULong { + override fun allocationSize(value: UpdaterOptions?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeChecksumReadOptions.allocationSize(value) + return 1UL + FfiConverterTypeUpdaterOptions.allocationSize(value) } } - override fun write(value: ChecksumReadOptions?, buf: ByteBuffer) { + override fun write(value: UpdaterOptions?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeChecksumReadOptions.write(value, buf) + FfiConverterTypeUpdaterOptions.write(value, buf) } } } @@ -10893,28 +12685,28 @@ public object FfiConverterOptionalTypeChecksumReadOptions: FfiConverterRustBuffe /** * @suppress */ -public object FfiConverterOptionalTypeChecksumWriteOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): ChecksumWriteOptions? { +public object FfiConverterOptionalTypeUpdaterSignatureOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): UpdaterSignatureOptions? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeChecksumWriteOptions.read(buf) + return FfiConverterTypeUpdaterSignatureOptions.read(buf) } - override fun allocationSize(value: ChecksumWriteOptions?): ULong { + override fun allocationSize(value: UpdaterSignatureOptions?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeChecksumWriteOptions.allocationSize(value) + return 1UL + FfiConverterTypeUpdaterSignatureOptions.allocationSize(value) } } - override fun write(value: ChecksumWriteOptions?, buf: ByteBuffer) { + override fun write(value: UpdaterSignatureOptions?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeChecksumWriteOptions.write(value, buf) + FfiConverterTypeUpdaterSignatureOptions.write(value, buf) } } } @@ -10925,28 +12717,28 @@ public object FfiConverterOptionalTypeChecksumWriteOptions: FfiConverterRustBuff /** * @suppress */ -public object FfiConverterOptionalTypeDataReadOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DataReadOptions? { +public object FfiConverterOptionalTypeBundleResolver: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): BundleResolver? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeDataReadOptions.read(buf) + return FfiConverterTypeBundleResolver.read(buf) } - override fun allocationSize(value: DataReadOptions?): ULong { + override fun allocationSize(value: BundleResolver?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeDataReadOptions.allocationSize(value) + return 1UL + FfiConverterTypeBundleResolver.allocationSize(value) } } - override fun write(value: DataReadOptions?, buf: ByteBuffer) { + override fun write(value: BundleResolver?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeDataReadOptions.write(value, buf) + FfiConverterTypeBundleResolver.write(value, buf) } } } @@ -10957,28 +12749,28 @@ public object FfiConverterOptionalTypeDataReadOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): HeaderReadOptions? { +public object FfiConverterOptionalTypeHostnameSegment: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): HostnameSegment? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeHeaderReadOptions.read(buf) + return FfiConverterTypeHostnameSegment.read(buf) } - override fun allocationSize(value: HeaderReadOptions?): ULong { + override fun allocationSize(value: HostnameSegment?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeHeaderReadOptions.allocationSize(value) + return 1UL + FfiConverterTypeHostnameSegment.allocationSize(value) } } - override fun write(value: HeaderReadOptions?, buf: ByteBuffer) { + override fun write(value: HostnameSegment?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeHeaderReadOptions.write(value, buf) + FfiConverterTypeHostnameSegment.write(value, buf) } } } @@ -10989,28 +12781,28 @@ public object FfiConverterOptionalTypeHeaderReadOptions: FfiConverterRustBuffer< /** * @suppress */ -public object FfiConverterOptionalTypeHttpOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): HttpOptions? { +public object FfiConverterOptionalTypeIntegrityAlgorithm: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): IntegrityAlgorithm? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeHttpOptions.read(buf) + return FfiConverterTypeIntegrityAlgorithm.read(buf) } - override fun allocationSize(value: HttpOptions?): ULong { + override fun allocationSize(value: IntegrityAlgorithm?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeHttpOptions.allocationSize(value) + return 1UL + FfiConverterTypeIntegrityAlgorithm.allocationSize(value) } } - override fun write(value: HttpOptions?, buf: ByteBuffer) { + override fun write(value: IntegrityAlgorithm?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeHttpOptions.write(value, buf) + FfiConverterTypeIntegrityAlgorithm.write(value, buf) } } } @@ -11021,28 +12813,28 @@ public object FfiConverterOptionalTypeHttpOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): IndexEntry? { +public object FfiConverterOptionalTypeIntegrityPolicy: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): IntegrityPolicy? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeIndexEntry.read(buf) + return FfiConverterTypeIntegrityPolicy.read(buf) } - override fun allocationSize(value: IndexEntry?): ULong { + override fun allocationSize(value: IntegrityPolicy?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeIndexEntry.allocationSize(value) + return 1UL + FfiConverterTypeIntegrityPolicy.allocationSize(value) } } - override fun write(value: IndexEntry?, buf: ByteBuffer) { + override fun write(value: IntegrityPolicy?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeIndexEntry.write(value, buf) + FfiConverterTypeIntegrityPolicy.write(value, buf) } } } @@ -11053,28 +12845,28 @@ public object FfiConverterOptionalTypeIndexEntry: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): IndexReadOptions? { +public object FfiConverterOptionalTypePathResolver: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): PathResolver? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeIndexReadOptions.read(buf) + return FfiConverterTypePathResolver.read(buf) } - override fun allocationSize(value: IndexReadOptions?): ULong { + override fun allocationSize(value: PathResolver?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeIndexReadOptions.allocationSize(value) + return 1UL + FfiConverterTypePathResolver.allocationSize(value) } } - override fun write(value: IndexReadOptions?, buf: ByteBuffer) { + override fun write(value: PathResolver?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeIndexReadOptions.write(value, buf) + FfiConverterTypePathResolver.write(value, buf) } } } @@ -11085,28 +12877,28 @@ public object FfiConverterOptionalTypeIndexReadOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): RemoteFetchOptions? { +public object FfiConverterOptionalTypeSourceIntegrityCheckMode: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): SourceIntegrityCheckMode? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeRemoteFetchOptions.read(buf) + return FfiConverterTypeSourceIntegrityCheckMode.read(buf) } - override fun allocationSize(value: RemoteFetchOptions?): ULong { + override fun allocationSize(value: SourceIntegrityCheckMode?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeRemoteFetchOptions.allocationSize(value) + return 1UL + FfiConverterTypeSourceIntegrityCheckMode.allocationSize(value) } } - override fun write(value: RemoteFetchOptions?, buf: ByteBuffer) { + override fun write(value: SourceIntegrityCheckMode?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeRemoteFetchOptions.write(value, buf) + FfiConverterTypeSourceIntegrityCheckMode.write(value, buf) } } } @@ -11117,28 +12909,28 @@ public object FfiConverterOptionalTypeRemoteFetchOptions: FfiConverterRustBuffer /** * @suppress */ -public object FfiConverterOptionalTypeRemoteOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): RemoteOptions? { +public object FfiConverterOptionalTypeVersion: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): Version? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeRemoteOptions.read(buf) + return FfiConverterTypeVersion.read(buf) } - override fun allocationSize(value: RemoteOptions?): ULong { + override fun allocationSize(value: Version?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeRemoteOptions.allocationSize(value) + return 1UL + FfiConverterTypeVersion.allocationSize(value) } } - override fun write(value: RemoteOptions?, buf: ByteBuffer) { + override fun write(value: Version?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeRemoteOptions.write(value, buf) + FfiConverterTypeVersion.write(value, buf) } } } @@ -11149,28 +12941,28 @@ public object FfiConverterOptionalTypeRemoteOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): UpdaterIntegrityOptions? { +public object FfiConverterOptionalSequenceTypeSignatureVerifyKey: FfiConverterRustBuffer?> { + override fun read(buf: ByteBuffer): List? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeUpdaterIntegrityOptions.read(buf) + return FfiConverterSequenceTypeSignatureVerifyKey.read(buf) } - override fun allocationSize(value: UpdaterIntegrityOptions?): ULong { + override fun allocationSize(value: List?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeUpdaterIntegrityOptions.allocationSize(value) + return 1UL + FfiConverterSequenceTypeSignatureVerifyKey.allocationSize(value) } } - override fun write(value: UpdaterIntegrityOptions?, buf: ByteBuffer) { + override fun write(value: List?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeUpdaterIntegrityOptions.write(value, buf) + FfiConverterSequenceTypeSignatureVerifyKey.write(value, buf) } } } @@ -11181,28 +12973,28 @@ public object FfiConverterOptionalTypeUpdaterIntegrityOptions: FfiConverterRustB /** * @suppress */ -public object FfiConverterOptionalTypeUpdaterOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): UpdaterOptions? { +public object FfiConverterOptionalMapStringString: FfiConverterRustBuffer?> { + override fun read(buf: ByteBuffer): Map? { if (buf.get().toInt() == 0) { return null } - return FfiConverterTypeUpdaterOptions.read(buf) + return FfiConverterMapStringString.read(buf) } - override fun allocationSize(value: UpdaterOptions?): ULong { + override fun allocationSize(value: Map?): ULong { if (value == null) { return 1UL } else { - return 1UL + FfiConverterTypeUpdaterOptions.allocationSize(value) + return 1UL + FfiConverterMapStringString.allocationSize(value) } } - override fun write(value: UpdaterOptions?, buf: ByteBuffer) { + override fun write(value: Map?, buf: ByteBuffer) { if (value == null) { buf.put(0) } else { buf.put(1) - FfiConverterTypeUpdaterOptions.write(value, buf) + FfiConverterMapStringString.write(value, buf) } } } @@ -11213,28 +13005,24 @@ public object FfiConverterOptionalTypeUpdaterOptions: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): UpdaterSignatureOptions? { - if (buf.get().toInt() == 0) { - return null +public object FfiConverterSequenceString: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterString.read(buf) } - return FfiConverterTypeUpdaterSignatureOptions.read(buf) } - override fun allocationSize(value: UpdaterSignatureOptions?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeUpdaterSignatureOptions.allocationSize(value) - } + override fun allocationSize(value: List): ULong { + val sizeForLength = 4UL + val sizeForItems = value.map { FfiConverterString.allocationSize(it) }.sum() + return sizeForLength + sizeForItems } - override fun write(value: UpdaterSignatureOptions?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeUpdaterSignatureOptions.write(value, buf) + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.iterator().forEach { + FfiConverterString.write(it, buf) } } } @@ -11245,28 +13033,52 @@ public object FfiConverterOptionalTypeUpdaterSignatureOptions: FfiConverterRustB /** * @suppress */ -public object FfiConverterOptionalTypeBundleResolver: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleResolver? { - if (buf.get().toInt() == 0) { - return null +public object FfiConverterSequenceTypeBundleUpdate: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterTypeBundleUpdate.read(buf) } - return FfiConverterTypeBundleResolver.read(buf) } - override fun allocationSize(value: BundleResolver?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeBundleResolver.allocationSize(value) + override fun allocationSize(value: List): ULong { + val sizeForLength = 4UL + val sizeForItems = value.map { FfiConverterTypeBundleUpdate.allocationSize(it) }.sum() + return sizeForLength + sizeForItems + } + + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.iterator().forEach { + FfiConverterTypeBundleUpdate.write(it, buf) + } + } +} + + + + +/** + * @suppress + */ +public object FfiConverterSequenceTypeManifestPruneResult: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterTypeManifestPruneResult.read(buf) } } - override fun write(value: BundleResolver?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeBundleResolver.write(value, buf) + override fun allocationSize(value: List): ULong { + val sizeForLength = 4UL + val sizeForItems = value.map { FfiConverterTypeManifestPruneResult.allocationSize(it) }.sum() + return sizeForLength + sizeForItems + } + + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.iterator().forEach { + FfiConverterTypeManifestPruneResult.write(it, buf) } } } @@ -11277,28 +13089,24 @@ public object FfiConverterOptionalTypeBundleResolver: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BundleSourceVerifyMode? { - if (buf.get().toInt() == 0) { - return null +public object FfiConverterSequenceTypeManifestRemoveResult: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterTypeManifestRemoveResult.read(buf) } - return FfiConverterTypeBundleSourceVerifyMode.read(buf) } - override fun allocationSize(value: BundleSourceVerifyMode?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeBundleSourceVerifyMode.allocationSize(value) - } + override fun allocationSize(value: List): ULong { + val sizeForLength = 4UL + val sizeForItems = value.map { FfiConverterTypeManifestRemoveResult.allocationSize(it) }.sum() + return sizeForLength + sizeForItems } - override fun write(value: BundleSourceVerifyMode?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeBundleSourceVerifyMode.write(value, buf) + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.iterator().forEach { + FfiConverterTypeManifestRemoveResult.write(it, buf) } } } @@ -11309,28 +13117,24 @@ public object FfiConverterOptionalTypeBundleSourceVerifyMode: FfiConverterRustBu /** * @suppress */ -public object FfiConverterOptionalTypeHostnameSegment: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): HostnameSegment? { - if (buf.get().toInt() == 0) { - return null +public object FfiConverterSequenceTypeManifestSetCurrentVersionResult: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterTypeManifestSetCurrentVersionResult.read(buf) } - return FfiConverterTypeHostnameSegment.read(buf) } - override fun allocationSize(value: HostnameSegment?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeHostnameSegment.allocationSize(value) - } + override fun allocationSize(value: List): ULong { + val sizeForLength = 4UL + val sizeForItems = value.map { FfiConverterTypeManifestSetCurrentVersionResult.allocationSize(it) }.sum() + return sizeForLength + sizeForItems } - override fun write(value: HostnameSegment?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeHostnameSegment.write(value, buf) + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.iterator().forEach { + FfiConverterTypeManifestSetCurrentVersionResult.write(it, buf) } } } @@ -11341,28 +13145,24 @@ public object FfiConverterOptionalTypeHostnameSegment: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): IntegrityPolicy? { - if (buf.get().toInt() == 0) { - return null +public object FfiConverterSequenceTypeManifestStageResult: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterTypeManifestStageResult.read(buf) } - return FfiConverterTypeIntegrityPolicy.read(buf) } - override fun allocationSize(value: IntegrityPolicy?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeIntegrityPolicy.allocationSize(value) - } + override fun allocationSize(value: List): ULong { + val sizeForLength = 4UL + val sizeForItems = value.map { FfiConverterTypeManifestStageResult.allocationSize(it) }.sum() + return sizeForLength + sizeForItems } - override fun write(value: IntegrityPolicy?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeIntegrityPolicy.write(value, buf) + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.iterator().forEach { + FfiConverterTypeManifestStageResult.write(it, buf) } } } @@ -11373,28 +13173,24 @@ public object FfiConverterOptionalTypeIntegrityPolicy: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): PathResolver? { - if (buf.get().toInt() == 0) { - return null +public object FfiConverterSequenceTypeSignatureVerifyKey: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterTypeSignatureVerifyKey.read(buf) } - return FfiConverterTypePathResolver.read(buf) } - override fun allocationSize(value: PathResolver?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypePathResolver.allocationSize(value) - } + override fun allocationSize(value: List): ULong { + val sizeForLength = 4UL + val sizeForItems = value.map { FfiConverterTypeSignatureVerifyKey.allocationSize(it) }.sum() + return sizeForLength + sizeForItems } - override fun write(value: PathResolver?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypePathResolver.write(value, buf) + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.iterator().forEach { + FfiConverterTypeSignatureVerifyKey.write(it, buf) } } } @@ -11405,28 +13201,24 @@ public object FfiConverterOptionalTypePathResolver: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): SignatureVerification? { - if (buf.get().toInt() == 0) { - return null +public object FfiConverterSequenceTypeSourceListItem: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterTypeSourceListItem.read(buf) } - return FfiConverterTypeSignatureVerification.read(buf) } - override fun allocationSize(value: SignatureVerification?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeSignatureVerification.allocationSize(value) - } + override fun allocationSize(value: List): ULong { + val sizeForLength = 4UL + val sizeForItems = value.map { FfiConverterTypeSourceListItem.allocationSize(it) }.sum() + return sizeForLength + sizeForItems } - override fun write(value: SignatureVerification?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeSignatureVerification.write(value, buf) + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.iterator().forEach { + FfiConverterTypeSourceListItem.write(it, buf) } } } @@ -11437,28 +13229,24 @@ public object FfiConverterOptionalTypeSignatureVerification: FfiConverterRustBuf /** * @suppress */ -public object FfiConverterOptionalTypeVersion: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): Version? { - if (buf.get().toInt() == 0) { - return null +public object FfiConverterSequenceTypeUpdaterDownloadResult: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterTypeUpdaterDownloadResult.read(buf) } - return FfiConverterTypeVersion.read(buf) } - override fun allocationSize(value: Version?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeVersion.allocationSize(value) - } + override fun allocationSize(value: List): ULong { + val sizeForLength = 4UL + val sizeForItems = value.map { FfiConverterTypeUpdaterDownloadResult.allocationSize(it) }.sum() + return sizeForLength + sizeForItems } - override fun write(value: Version?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeVersion.write(value, buf) + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.iterator().forEach { + FfiConverterTypeUpdaterDownloadResult.write(it, buf) } } } @@ -11469,28 +13257,24 @@ public object FfiConverterOptionalTypeVersion: FfiConverterRustBuffer /** * @suppress */ -public object FfiConverterOptionalMapStringString: FfiConverterRustBuffer?> { - override fun read(buf: ByteBuffer): Map? { - if (buf.get().toInt() == 0) { - return null +public object FfiConverterSequenceTypeUpdaterInstallResult: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterTypeUpdaterInstallResult.read(buf) } - return FfiConverterMapStringString.read(buf) } - override fun allocationSize(value: Map?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterMapStringString.allocationSize(value) - } + override fun allocationSize(value: List): ULong { + val sizeForLength = 4UL + val sizeForItems = value.map { FfiConverterTypeUpdaterInstallResult.allocationSize(it) }.sum() + return sizeForLength + sizeForItems } - override fun write(value: Map?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterMapStringString.write(value, buf) + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.iterator().forEach { + FfiConverterTypeUpdaterInstallResult.write(it, buf) } } } @@ -11501,24 +13285,24 @@ public object FfiConverterOptionalMapStringString: FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { +public object FfiConverterSequenceTypeUpdaterInstallTarget: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { val len = buf.getInt() - return List(len) { - FfiConverterString.read(buf) + return List(len) { + FfiConverterTypeUpdaterInstallTarget.read(buf) } } - override fun allocationSize(value: List): ULong { + override fun allocationSize(value: List): ULong { val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterString.allocationSize(it) }.sum() + val sizeForItems = value.map { FfiConverterTypeUpdaterInstallTarget.allocationSize(it) }.sum() return sizeForLength + sizeForItems } - override fun write(value: List, buf: ByteBuffer) { + override fun write(value: List, buf: ByteBuffer) { buf.putInt(value.size) value.iterator().forEach { - FfiConverterString.write(it, buf) + FfiConverterTypeUpdaterInstallTarget.write(it, buf) } } } @@ -11529,24 +13313,24 @@ public object FfiConverterSequenceString: FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { +public object FfiConverterSequenceTypeUpdaterRollbackResult: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { val len = buf.getInt() - return List(len) { - FfiConverterTypeListBundleItem.read(buf) + return List(len) { + FfiConverterTypeUpdaterRollbackResult.read(buf) } } - override fun allocationSize(value: List): ULong { + override fun allocationSize(value: List): ULong { val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeListBundleItem.allocationSize(it) }.sum() + val sizeForItems = value.map { FfiConverterTypeUpdaterRollbackResult.allocationSize(it) }.sum() return sizeForLength + sizeForItems } - override fun write(value: List, buf: ByteBuffer) { + override fun write(value: List, buf: ByteBuffer) { buf.putInt(value.size) value.iterator().forEach { - FfiConverterTypeListBundleItem.write(it, buf) + FfiConverterTypeUpdaterRollbackResult.write(it, buf) } } } @@ -11557,24 +13341,24 @@ public object FfiConverterSequenceTypeListBundleItem: FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { +public object FfiConverterSequenceTypeUpdaterRollbackTarget: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { val len = buf.getInt() - return List(len) { - FfiConverterTypeListRemoteBundleInfo.read(buf) + return List(len) { + FfiConverterTypeUpdaterRollbackTarget.read(buf) } } - override fun allocationSize(value: List): ULong { + override fun allocationSize(value: List): ULong { val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeListRemoteBundleInfo.allocationSize(it) }.sum() + val sizeForItems = value.map { FfiConverterTypeUpdaterRollbackTarget.allocationSize(it) }.sum() return sizeForLength + sizeForItems } - override fun write(value: List, buf: ByteBuffer) { + override fun write(value: List, buf: ByteBuffer) { buf.putInt(value.size) value.iterator().forEach { - FfiConverterTypeListRemoteBundleInfo.write(it, buf) + FfiConverterTypeUpdaterRollbackTarget.write(it, buf) } } } @@ -11660,6 +13444,84 @@ public object FfiConverterMapStringTypeIndexEntry: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): Map { + val len = buf.getInt() + return buildMap(len) { + repeat(len) { + val k = FfiConverterString.read(buf) + val v = FfiConverterTypeManifestRemoveData.read(buf) + this[k] = v + } + } + } + + override fun allocationSize(value: Map): ULong { + val spaceForMapSize = 4UL + val spaceForChildren = value.map { (k, v) -> + FfiConverterString.allocationSize(k) + + FfiConverterTypeManifestRemoveData.allocationSize(v) + }.sum() + return spaceForMapSize + spaceForChildren + } + + override fun write(value: Map, buf: ByteBuffer) { + buf.putInt(value.size) + // The parens on `(k, v)` here ensure we're calling the right method, + // which is important for compatibility with older android devices. + // Ref https://blog.danlew.net/2017/03/16/kotlin-puzzler-whose-line-is-it-anyways/ + value.forEach { (k, v) -> + FfiConverterString.write(k, buf) + FfiConverterTypeManifestRemoveData.write(v, buf) + } + } +} + + + + +/** + * @suppress + */ +public object FfiConverterMapStringTypeManifestStageData: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): Map { + val len = buf.getInt() + return buildMap(len) { + repeat(len) { + val k = FfiConverterString.read(buf) + val v = FfiConverterTypeManifestStageData.read(buf) + this[k] = v + } + } + } + + override fun allocationSize(value: Map): ULong { + val spaceForMapSize = 4UL + val spaceForChildren = value.map { (k, v) -> + FfiConverterString.allocationSize(k) + + FfiConverterTypeManifestStageData.allocationSize(v) + }.sum() + return spaceForMapSize + spaceForChildren + } + + override fun write(value: Map, buf: ByteBuffer) { + buf.putInt(value.size) + // The parens on `(k, v)` here ensure we're calling the right method, + // which is important for compatibility with older android devices. + // Ref https://blog.danlew.net/2017/03/16/kotlin-puzzler-whose-line-is-it-anyways/ + value.forEach { (k, v) -> + FfiConverterString.write(k, buf) + FfiConverterTypeManifestStageData.write(v, buf) + } + } +} + + + + @@ -11728,5 +13590,45 @@ public object FfiConverterMapStringTypeIndexEntry: FfiConverterRustBuffer + UniffiLib.uniffi_wvb_ffi_fn_func_extension( + + _status) +} + ) + } + + fun `mimeType`(): kotlin.String { + return FfiConverterString.lift( + uniffiRustCall() { _status -> + UniffiLib.uniffi_wvb_ffi_fn_func_mime_type( + + _status) +} + ) + } + + fun `runtimeVersion`(): kotlin.UByte { + return FfiConverterUByte.lift( + uniffiRustCall() { _status -> + UniffiLib.uniffi_wvb_ffi_fn_func_runtime_version( + + _status) +} + ) + } + + fun `updateProtocolVersion`(): kotlin.String { + return FfiConverterString.lift( + uniffiRustCall() { _status -> + UniffiLib.uniffi_wvb_ffi_fn_func_update_protocol_version( + + _status) +} + ) + } + diff --git a/lib/src/main/kotlin/dev/wvb/WvbRustlsPlatformVerififer.kt b/lib/src/main/kotlin/dev/wvb/WvbRustlsPlatformVerififer.kt new file mode 100644 index 0000000..8e0abaf --- /dev/null +++ b/lib/src/main/kotlin/dev/wvb/WvbRustlsPlatformVerififer.kt @@ -0,0 +1,22 @@ +package dev.wvb + +import android.content.Context +import androidx.annotation.Keep + +/** Android runtime required by the native HTTPS certificate verifier. */ +@Keep +object WvbAndroid { + init { + System.loadLibrary("wvb_ffi") + } + + /** + * Call before using Remote or ProxyProtocolHandler directly. Repeated calls are safe. + * Only the application context is retained for the lifetime of the process. + */ + fun initialize(context: Context) { + initializeAndroid(context.applicationContext) + } + + private external fun initializeAndroid(context: Context) +} diff --git a/scripts/install.mjs b/scripts/install.mjs index 314ce42..4caec7c 100755 --- a/scripts/install.mjs +++ b/scripts/install.mjs @@ -5,16 +5,16 @@ // webview-bundle/webview-bundle repository into this Gradle project. // // It fetches the GitHub release asset `android.zip` for a given tag and installs -// the Android variant (`lib-android`) into the `lib` module: +// the Android variant (`lib`) into the `lib` module: // - the Kotlin bindings -> lib/src/main/kotlin/... (overwrite only — // existing files that are not part of the asset are kept) // - the JNI libraries -> lib/src/main/jniLibs/... (replaced wholesale, // so stale ABIs from a previous install are removed) // // `android.zip` is laid out as (see .output/android/ for a reference): -// lib-android/src/main/jniLibs//libwvb_ffi.so -// lib-android/src/main/kotlin/dev/wvb/wvb_ffi.kt -// lib-jvm/src/main/kotlin/dev/wvb/wvb_ffi.kt +// lib/src/main/jniLibs//libwvb_ffi.so +// lib/src/main/kotlin/dev/wvb/WvbFFI.kt +// lib/src/main/kotlin/dev/wvb/WvbRustlsPlatformVerififer.kt // // Tags follow the upstream convention: // - release: ffi/ e.g. ffi/0.1.0 @@ -53,9 +53,7 @@ import { fileURLToPath } from "node:url"; const REPO_DEFAULT = "webview-bundle/webview-bundle"; const ANDROID_ASSET = "android.zip"; -// The Android library variant inside android.zip (ships Kotlin + jniLibs). The -// sibling `lib-jvm` variant is Kotlin-only and not used by this module. -const ANDROID_MODULE = "lib-android"; +const ANDROID_MODULE = "lib"; const USER_AGENT = "webview-bundle-android-install-ffi"; const ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), ".."); @@ -256,7 +254,7 @@ function walkFiles(dir) { } // Locate the `src/` directory of the Android variant inside the extracted -// android.zip (e.g. lib-android/src). BFS for the shallowest `lib-android` +// android.zip (e.g. lib/src). BFS for the shallowest `lib` // directory so an extra wrapping folder in the archive wouldn't break this. function findAndroidSrcDir(root) { const queue = [root]; @@ -300,12 +298,23 @@ function installAndroidBindings(androidZipPath, tmpDir) { if (files.length === 0) die(`no files found under '${ANDROID_MODULE}/src' in ${ANDROID_ASSET}`); + for (const required of [ + "main/kotlin/dev/wvb/WvbRustlsPlatformVerififer.kt", + "main/kotlin/dev/wvb/WvbFFI.kt", + ]) { + if (!existsSync(join(srcDir, required))) + die(`FFI asset is missing ${required}; build/install an updated android.zip`); + } + // jniLibs is fully managed by this script (see .gitignore); wipe it so a // dropped or renamed ABI from a previous install does not linger. rmSync(JNILIBS_DIR, { recursive: true, force: true }); + // Remove former filenames to avoid duplicate Kotlin declarations. + rmSync(join(LIB_SRC_DIR, "main/kotlin/dev/wvb/wvb_ffi.kt"), { force: true }); + rmSync(join(LIB_SRC_DIR, "main/kotlin/dev/wvb/WvbAndroid.kt"), { force: true }); - // Mirror lib-android/src/ into lib/src/, so e.g. - // main/kotlin/dev/wvb/wvb_ffi.kt and main/jniLibs//libwvb_ffi.so + // Mirror lib/src/ into lib/src/, so e.g. + // main/kotlin/dev/wvb/WvbFFI.kt and main/jniLibs//libwvb_ffi.so // land in the right places. Kotlin files overwrite in place; any other // hand-written source under lib/src is left untouched. const installed = []; diff --git a/settings.gradle.kts b/settings.gradle.kts index 4ba3fed..7564979 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,6 +10,10 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + maven { + url = uri("https://raw.githubusercontent.com/rustls/rustls-platform-verifier/maven-archive/android-release-support/maven/") + content { includeGroup("org.rustls") } + } } } diff --git a/testapp/src/main/assets/bundles/manifest.json b/testapp/src/main/assets/bundles/manifest.json index 4a0ffca..9c95e12 100644 --- a/testapp/src/main/assets/bundles/manifest.json +++ b/testapp/src/main/assets/bundles/manifest.json @@ -1,13 +1,13 @@ { "manifestVersion": 1, - "entries": { + "bundles": { "testbed": { + "currentVersion": "0.4.0", "versions": { - "0.2.0": { - "integrity": "sha256:jdi4x7cstlNgnReMU7NS1OE5EYmiLF87GYBSYh9tqxE=" + "0.4.0": { + "integrity": "sha256:J63kXbcRISIgXrj9gR/2E8O3XsLdybzE+bwa2nMgio8=" } - }, - "currentVersion": "0.2.0" + } } } } diff --git a/testapp/src/main/assets/bundles/testbed/0.4.0.wvb b/testapp/src/main/assets/bundles/testbed/0.4.0.wvb new file mode 100644 index 0000000000000000000000000000000000000000..236fddb140e6eb5fc45ec006bfe4cfb8515b3491 GIT binary patch literal 99816 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 literal 0 HcmV?d00001 diff --git a/testapp/src/main/assets/bundles/testbed/testbed_0.2.0.wvb b/testapp/src/main/assets/bundles/testbed/testbed_0.2.0.wvb deleted file mode 100644 index 8e25fc8e2f02f19010b8764ac0896d9cc73b628c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100846 zcmY(r349yXxi|L^bLPoDkEnX|mh z^FHr+;nsJ(=Y?CZJHv?RZS#Ns(sNSN7QIj~?826;nK4d=w_h5b$#l&gj-(3(dfj!E z+cr+xTX67tbuF#=|KDpZ&JX8jmiI-jD7@}^>)(mJ`AW~~?N{hadLeCP7wx~36uLka z7YmQf+Y33|cCGt$HdeNJr#SOEGwHe6AH7yvP4u!aVdeAoN;o_-muQ{sneCnJPlm&V zqBX0h4V;;s_0WrZ!*T3JXXCToxHyxyGKQ6CHR49Mq2q)oInf$R$D{Es94-`XBa>+D zndy!9MRBaB(}sx~I%m3PGBf}8L^z!{ZA&lMIeLNe#}cja*?yx-6ku30^qXjBtTTp# zMZ?Tw%}>$(XkR)SHAv5!`9v}6rWYy;i>RX-m>AoGbK!Agu9(v;HPA9K7Vyzajuthr z$1rnwH87kv3;CR0Py@Y0zo)2f7A~Rlo&vVvf?;K6)j(mnU>ghJVpa`=^~J@U5%v)s zrk5pSE^h=5?LuMW`I)?p(h~G)U1{5bk;&?T(4u9`8dd=xlgnG-LVDg}>j7%(cQCXNaz3`@3W5*`6cBusb{3Gt+aJoL90J*leb&bEZ_< zyfTwN87^c`y_azhKUaeuJ6W2^XO>rH^z^YgD_=A-i6z|%iC$NdE+*9w-A9ip$yszs zcvfG?=9U|Y#@OvjazVG|vSuP0q?ZCH(n!R*7EdN~SDfmlL~5x`yWMterDf_R_W)y?;L}SLnA;!qVc!v2-9Ace4-O-!qx+QdVHgB%n!Kha%{}E#rmwwFnXIWi* zhV5T4Y<;DXt?&CM?On{~a+|qSlDCKBO7dprc@J~C_jBoL;&Hk^Z0VV7v5@HR?|&Ps zNf2jy7<-IdpCY=J`T{d6`^a-EcW_MJA(G?lEzxKxGNYU34(|CpIjia<*R$j`iR zX^=@z@M~iD^guKa)j4bB-at@dA0Yk|)pTCX`1@%(kj`g}?=v5JmBvdEv#^q*#ju@U zOvL*xp*FK{C#Q|{x-*O!X%=Sl)2CnCMOsHSTd?Vq!E#6{?0a3-pl!IoU-!mnXF}`ZFEoK4$KT9Czl_Q**R9eU#52u|8Kc$zwG*mOhe@9 zBDMvCj63O0xg`|r)zbu_~l&77aF}c|Tlupd%)5XHqh@!INTW$zuH&Ftw4_upL7r={{=v3$YS?PB3p$%z@@xz6Dq>@pUvadwpNchR5S6$7j$ zra1ZMX=XB`+j_W?c4_%zQ{~cN{+JRXez{>;d8>m4u~kWu&X$))NU9@UDMe6|0l|Pg`0eu#pL0yj}oKzF`lJt;Uelw>q zqtBL050%H_T~VBTwp{*HIp@x@H%c|hkIMUJbNLgATb=SRh^=Ev-nxuYgvm?9_c@r8 z9t$sIPlmE)pkU3-s6Q*G4jKt`MK{*aM=$D6MJbj4WmDa+FN7qz&;)55Ftm`N+KRR{ zB!2ek(;*`ivb7cC~ooC03hP6Clfx9=uz6J zt{kHmc_J89A(d>)FtA%vl8bD6zz!J&Hjc(EC4=l`7BC`N(>5TT?B(|MkX=m;S`(e; zXlRrV1#cPFe(0sNO67i9r?t_!I&m#+XveQRo{JQSm0r{{6QJnE;K3dTlANiu23){M|Aj91Zl@5J)LOgjQOypbyTl07b2zfr6AjN=`rH6DXPb%sMFrc+rtN8r3Io^m z7NY-JwDOQ@hK1ui?$Ke%L1Z;kbD^1}>xDUpE9nj5X}tUdWc4&^iE}yebfH*)QCAS9 z74h`-3&uho`RY{jX^Ot5*THR7$1|1Q>RZGlc{anE}^c}l-fsK=&2+hN(~x`AIUYP zGRIHnwUumP56q%f!xe+)B)#ei#!9MTVwe|=vAmTw4lP0m86k26=+(#+GR=SxfCPCGBYFp@2di|!lFFD&Lw3@#L{2FPFd7}aWLh_yRM z#9-OQoM+gk_V!wmqckq&Mo@L0ciHn+{zPC@h{{mP){hy5fNlo)rx$D$*0(TA@NPB_`cLL zv^k|PRzLekR;yh_ts&!0tl|i{d|bYq8dBS6Q%dbuM)BZ*-@kov&a?dSdA+djgt;G`0o8Pv zK57~0B~!JOmCv~gSyrLD~)2NU*hr1S2Cp&m1q6S&EkO=L0Om-4V7qU+^g zR_&I05>{!z*^)IL=lmc)5D*3a#kCzRV`QXyB}`j%<83n4X>XI?+uj}wu8Q6FQ7_8* zq|=+Gq^C zRZ>iLQ^C--9Np5fWv-hwrGHL*BP;(bu{$Y_3V%#GKA1N`sZ@uLULhp`DUhGQoHpk| zojsggH7q7m;Y9Wgt_x)s^|7)d%)ZoYF()iBBa=$pPG^kBlAbFX$!5CUGQ?C9=d0uk zTu4a0kT;Y6wqFv5N&XB;Q#6EmR=Ad4Vwh~HjSi!+L%UP+!-*3nujbu#BW_`^BT zdzPA2bv1vGw-vMm3noL)(?jrdj9K_983+b1Jw21p=M3FUDG>68_KK7JL>fwK!Pwg? zS~RVm?!bODY2*q<;Jr-#9Xm54Bs)a(BTTwk$ka1tabX5>FOj72Rg2+e#((FiP5qKN z*Gk;Sf&qJw)PUJfZGp@vf2ks^84*}pSzARF(-7Cwi~6$As|vAnsf1aZey!}BGmzKv z=ms^d=|YdB22+t#N40cbiYf_RHH=VR84P7{K~2BLF>Xjuifh zcjT|2_Ef5lJKya#475}tzl+Yhbx{Hy%WGNeso9QDK7yff8EZRzx*)~y&PF;jm`WwY zFsY(uz2-owBV>s#n!KKTfk7IcLSK35(GyvFK7bZ21XfZV8|ZwGXx;*99=Rf)HA5*i zAo{43N?6z^k-)Cmz;eDASioSkh0L{pau`D90!I)g6*M%Y^R;xL8duZ+3=1K&bs^{i zSx83s%@D?=zf*m>LG~73b_7+koB50-E;mYE7A#da$T{^l1op%|@ zXIUrI!*+5hbbehZwQBvR)Rmdyj8I*|&P(fN+Q1Zs~JI;#gVLm>ZsVs}Tv2 z>pqwKCP`jPZA!vNK}9`5HzqJsgee@nh0X}{K!<7dO==6-FM7SD>zz(FdlQs#8pV|{ zlA7p}h_R5BnY)QDxI-zJ-g*&0$mq$mu_$^jr5Z}TbhadP801al4tm9fp-@r}YQRVq z6GcPCgc;S-^S3c>N$j-bSaII~`#_*5xn)mf448|@|9An9z_L@*!ZH~q`F7@yUPbEz zd+D~04s@SgQtf=PqHKVqA4=t2RQ56F2Tq@!oGPh#GuKZ;;*0*f+^0kxY>)>-sWL}v zWcZBy+XyZ%2jEt$O7VN--=&T05(XIX9diy%=a8Dbe7mD9lG@9z4j83U(ufQ_;~CXv zRpTff)(*n*y}Ej037fHv-n3?6XaBjHn>2{3h)~ZeU2x1`U#OY1y$6<~mKBG$)tv9u za@d=m9^Socbnn6GU3(9Xj_)1XJuyA9Z+h>(gVTp5MyL0UPahc?-z)aVC#DbX9KCFM zcxdnH)%`ohhek%Vi4i&{@7uF~*Y45r>G4B*5ANDCs;x{<-&+}+p8i242H^oFy@S10 zH4)hv!Q|Nk6LT2tEreAGW*y{8dVWu}6eCw3mwe&m|z*+V>8#y=+sX?C!EEEfNUik|5SZ@YG z!{~rS-|IIlZ7#G@+FBLE)I3-@E(V<)Dr5cb86ERFV86Li`b5>4RRIr4Tu&BySqYlP zo1D6>>dNWUkCY8VvZN|#ZhE?4_Jw1&PQOzie{TUajc>PO%{T9f*eX5*hy z<7(5j=V39nmRaGrc&q+vM{VS-u8o@MFB^&t=t@gHX$`)7rRQZwNLL_EqshX6o-9C8 z$(Z6#Ixnv^K0(?Cif zy6nTkXh@)X55-|WO~V+4=o&sQ-yT(Ad@W@m^}fc_0-!A}RFki?D6wV=YQ`-R-$1V8 z)R|YObH2lvp{j8=dFHv}A)#oX>xB`lzp=8?&6_PZ=WBE~ce?mv7WAAIgoM7>Op=Q` ztBJe#y`oVkEzuy>bF4mp11o!udGpV)=InD!s>hDM@Em(p$_p(VO{NF($uz#bFqwXi z9Z6sO@&g5-vHw;ft#cm`rei^t*xzY$9=>7>n(T|@u9psocjUiI(gvB;dV6&>+sS*z zjoCj+Ey{{Q{*`9V{)e%vW$90KWf5{rap&c{C9U3E9!wZLWM>O#vTWv$>?Ym7Kot&= zMX-RVEu;QNRcm4;P(GXp$JDy=A<-yLRJ9~B8QLM7sQqwx?QfCe1@0}rz4BXV?=(CH z$k~Mbl*E7M_1d2!116qjAN)dt>k#p?{G7wqCL#w_Q+O-b(afZ6El>G|EVz*$Y8a19 zB(uS76&R7((5iJzJK;1~MR%R`Pq)|lWx0E)tVOsSXN7ThLMf&7ZtqJB2Dq4Mu)LtZ zmsjjrDw?YvexZh1LbjLu+8UZsRPn~Ag_VX{L1<57s_M=qv=)@l{9i~OVDdYi?soEq zdia)N;T=vcMTx!SW*f;n^u*fD6Sk|4zv$)$mowzrOj3}fVS4K@-YTH(Tgmk+CT}Hq zBeB<*+)o~t!zF!+B*o#piw=w!GsU@ThtnkX!G<3QIWcn4*UtpVa~)CHW^&&M^)owb zq~GtT{yy9Gd9%G)p4oUt5iJ|us zR=G>6Oc4u^YYTkxWivf`65cdAX9E7!6RfH#O68v>Po0BRUPaC};&0?H`u`1fYpGa& zBp&Ha3A@|Gum}lH2@utaSr&-FI(VhF);h3We$Wl;H_KSYvC-7t?eiJ`?fb2*Bz53~ zRwx-AFq3yU&UxV7hGSc|ZjGtJFtnzGEQ$_fVHnzqV*K27k7`VsQ_v))W__rPova-V z=%vyLavKt`wN98`HH~A&WOPcUIDn)EF&8IrHmW==ugdBz<>%lftlojjMi%pnp|4aw zWU&5p^=e^EOGk@Yq zw_okA`65OYT695JI!9gA8z>kt|6U{in)y;4e3PyXQA2XQYXtXeDmi>*2*E^f5K>AL zHS7j{dtMuX#}E0QUn|6lM^b28xKJu>Z#_q8VKe5$zoHgXGk-3>P^r#pohq=Wn7SbT z&5M6?;@`CP1ic@D%!AnlBVYVfP5od4u`iUhsY_9sPQ%Nx;>6r*h<8!#au>bIaqq5$ z)u@?m45W&wK`$|tYLhK&EHD!?o+~%9nin#KSH-^X;_J%B6f2aQ<^Jq) znT~f2XvR8fSF=HW!L4N@uwt@emgP-R5!FW5WVaFfn&eBKAlJi^|Kv>)+r}<1yh2ff z3j%C3Lun=W7CP_1Cq?0138d8!yKs|~!!)xZG>rWNa$Zq33!mkrYJZmEVFT#J%T94) z3|=fOTy;Y02A+VuAZxdbF@9`rFc?h)f6md`Vbd6xh^~#n-%2nDpBBTon{T$YkMpa5 z)8Q#Q`RjGTVD$R(xSm+)=MbX*pz$S0QRTzb1;p#!>tnh$yM=-?i5sgQn+XB*&7ghK zN-`5*lRywcnH6?ms|{_piSZk$JFjJa!PxCp{4f5-{7=f;m6bMp6?GJ?UZHcxb_{qV52$`(| zhJqH9nOE6L4){_c5g&OW7aEb)^jYDkB@?WSyf7ErAva%e?LSwwg;e5W&Q0Z*VjP6q=t#xoY$WfjD~d zhL#>foBnx&23vcL#9ys-y~-zcWpc*;0p5qsY$Ew?+0jgmv1aPtojsS2oDERt{;v<9$`zG2~Wm^)atQ zUh+Hs?EEU`ZQ2+^6vr4pU-sqbR*bw@ynDbHyrQ0WwK%%TALwX(Wj=tf07wgy9N+`2 zO8bOin25MRjRbJN8W82+l>y+i3q}CLAHdx_VzKZbZ8V#>DKNWO2xJSZE-k=)(O>{U zl*EJ<#ryW?_Iv~Z3`pjf(YXbfOm|8Bf@}MnVGn10avNhWNX{zen0Bq1?V~Z_b8jT) zK;X97i`Y;gmH?_2cTr1c;`5GRXL3n$tPR9$ZT6#%)ht z%hYRVonNL#;Zi+-=Qb+09UlUYMrC2#f&dtCf~x?c{&+SKnrw7{q)mr z?8TzJoxNSLzWBEo3sa#>oHDw?B$fFy*zYzvE9zck1x@cLK=7|XW&`(7FPDZBS#=Rp z#pc}!OPzBk0Od@$QKEXb`~jG7FkiRk2Se6d+>b%~g>o>bRWZTO2M%D~X`0_T51Vy< zFm%kNCm?vEYQVXyhk?W>iGAcPe9L_oo>(}2dJzKbU2eWhpf!v9&xQGXF_*avk->~+ z84L1W5k7y3Ir`o5t8I0Gx5~VNdzvKXd2^F2f6vJx5|Ai7oBdJMArXFB`B}I{vwl?7 zq;KfhWQML(SyvUm4`@?r-nJJDi7i`T!y}lGF_yM$T{~0Q0{14_+aNpEN;6e2m1Ayo zXsJQCu!YGf4cAN&dOkG1>kn({LLY7znv6{;e+z$Dw799oljc6UD`~>g7EiU0lfToP zQcm;truG0fLwGau=f(>S0=y2iuDVglz)}{o13s1AkrFcjL*57gtIP$)T`>UHcQn6O zoI#W&ys!shSX47$m`hnJZ;B9@M%%^BDY{YM;6QpZnak9d<-oj=Tf~I`q7^3QRw0u5 zFkBo!$-CtHb!{b+SOMs(TFXs1_02WsH(H_YcpO1Ibp)qHSMSDe0l!P}o4{{RB3n`y z6HygbaAsuR9)z%59bNFvukLXDQ#~POBqA=a!0k9x^X^rZV`-i5WbN(kUt$|Or=DKZ zI9N~K#32v=j>CDf-pa3{&(@O_{HJwkIER*+e)qcbu2n1Fv~_@-)-H24pRm>J!r~vA z&a5iMgjrJ9U|O@Kbz$dg6~5eSKHj~yprr)UBekxkCzVQmx~$&5t6gfQEdjfAi}YY3 zl`07r9d*8K-NrgS(&XKLJFB?gDJhcYL#(_x{vpQw`1dzAR6!7dN*fkN#yE&j15D~c|ZROL29Hw*6>?BvM5}S z6%?6{9DK0M#tAL18foU1-RESSgTfN$O#IJ8W`@5tVX2M~QG39rmV zMnXm(^ZZx|ep zOMiw4%ft40f*hBWxqGM#9z%j+NmKg(eRDZyU~1qs6CtCv`UV@i*+?T`*yNL}P$Ntz zX^b|hEC0cS$e_*y#?caO&zF=$0fzf3#S1>ZDB#kNF-(mG72_O4Et>`Hh=NxqGk zkE9P#UFjzhU0oMymJ) z%Oyqmd;W(S%s6vAPL5Gl_7pklNq(L2=g84RAUTxb3-Ou=`Af_G7+~+`S^b_TcrUQ+ z=NjeX4(U=V`=}(lv&jHCZ2LM`K z(Z5aZMaPoBQS?IaHfeMaJKrJqmt@b|q>#VF{L*#AB2I}s*N(w17&i(=ud|@6h&Hnn zIE0>;Xhr~tM&@F~u%OLWvg;)NA7w*OhV~ojI2=v!=-o|lFkRhi`_qw~A(Hyb;fFZ~ zsiAlUIp#=8(xIKOW)8l&_xQvrO(OJW^4!wI8uLG>l>d`j0_I!lYJ-p0UZ|U%9v>YV zJ~%xxdidbJeZbOo?B2J1X!rEaefut%p56*u`}FCflBa_zJ{3W|(#J-IM}s#tPAg)* z^;7Mpkl7R{uksxb;F#IlAy7D7c^@YA_&qu2MwYxt3 zo%JihP!Ki@6kyrKDZeQei#68g?qiRjqcuY~cSc0d@eMK9wVT$r*pYWiqe;cqD(NM! zh_kE+LpYNUz++Qw$OV0_(B2j8@54McH;cJtRMIs`1R(+58|9uy0*GGebMW^%2hGG| zwT@BAUwp?JztKW1vCalZi>-a7^$a>bBbw}9sFpx+0Fh?YSIvRA)7PiG%o-M7 zX3oDdex{0VDEDeJG5H95ZDMk!Ds7`;#^56!XWR=A)WWAF?_*tXYlitSnj-sI=2N~} zdrGK2UCUlewR#&Bgv7J7>>#zO)=$`Pl@)ltNk}z2YZi^3HN(Q*KD=o=iq^}Cr`NUW zz)oUGJ^3`T6v;2M11MQhck;_%;AmC@J!U6`;Zw-Yfb221rkg*Y%7;;n+0d_O7++(3 zhR!G^5LrvpkFH_r+K-D0<%M69emD8>`(_#?Az+PjQMFg_Tl}T<!~}f zrQc8EC^MLUj%w4dlV_TwX&q%|r^(R`|9w4GPm}u!&sC!3paM$&B1cT#Eh-S^I)lq_LOtw=utcx(hTQxtk8zNkcQTjF&rjojfDHb6)#xr#zxH&pqlR zpAq#53dA(13U#*WE_;5kc?om~d98nox(p_S3nym4Q7FguB)ALy#eYPm@ql3j{x9tz z0Gl*Gvh>1W^Tbx%imf?B%yn+C*$PnEt<$+xGK-o82qiUaYu48h`(K_z9{d0G|L`0N z0fBA_EN@yM)CM44F%AsD%t&|8`~H^$k$l+6zLt=pT>5pHXLOt6`m_iqPo|9>+k$nu ztI0K5H<8X|4Rc4;EbK2(EpDN@;*M_qh(~^gvtV7hkFX*VnWxC!(w^HTEOUe%{{}IN3=2vh}To)kw87 z2;zJohWN-v(}0XV%y}oRx7JV>=sfdOmtRk{Lmu8#@}V8Pe&w|$XAP4crrMhF#;DZ zuG-oaylt@f!q$6$w5J5%mP#P-V(U<_z>*#gx2eXpNp@YG^gSvRA(7yff1|Q)CE3pI zD4P_cE2D1Pr=HNPn;XLW5bn_v4Vx#_Swmat-g+j12_|@Ke=WBVnzHBfnM%?{=jY8z z`T%T2Lm)2;HErJcFG{?E`B&DtHYJR&wVfFij%0M}!Ux+NTV2+oF}Yw&sh~7yzv%?< z8xDKQeItvWkp-ipV-q<_t~#|G7`pvT8)PlAr5~> zkgs9YD}#qKs9qwzL3V5iW@lgLw_hY_pc6*hiSkUG}UEu*NXqK<}^ z0w8p}$SNi)rQ6(KevO2dMg5ch^>uT1vR2jn8cnPM&hKhZK$qlLgK{l-hcODLx08@A zB8dJ0+N!9_8)$hMWgeJ~)OW#gHEKEYrUIdX|K)`hwkj!jhXYx1Y@rLzZQ}B_qInD;9%$H!DB|v^y6U@A z<*AMn1_Db5!D`+y0$A$HoBaEp<>-Q2J+&jrrh_o;M~q801x;;d<-cTMX^L0?6l40> zK?|LiY$VyIy}cIr1TSCz@s$w+m@sId#y2+bAGMAkEgOw95N$s>wAp}wvyU{Sm|P+MO`Uil$Gs{&A{mph+0elT&qkFM>Qmi5u!urAKsd{IK0vQUnI3OvM%zx zlgnQa=TILE3Z=Rcnq#KZy|Jvdy}gbaLOSayDW!LH@(*^`S)(LDr>DfVX>7gK}$DdzJM93TfE!;D~WCuL3}cN#z%+ z)D%dz5U0s3A|isMG@G3(S|WDQ-hLTX9N?aPRB@(7+=K@B?oe~$K`Qr^(3QG2PJpIi zM*(ma_%#57$=LIe?fLfhf?s+?N^AKDi0fI9G|M5J06N2-@2!ZjCLBuw3A6+zqT!Q7+0e)+U@EC(!{HB;vxUBsw8CK(M*=%l5jFB& zkZ@BBWr~dg^9>gwlvM$SpFSO>+k_-6YGQ=On0##K-6@RnfM9l|)CzKaU2}hh_qMke zGt70r<1MRXoT66z_DI=!TBDvXABTljEUnZ!pC+I3NmqU84IS*;j%M{rhr@4PBV8ss zesG%DnYvd9?TqQ542~KhQ%R)uwEB-#JLO^burL#!aJR&Jp2RXW1L4>b#wNX{8I33+ab)DO&2c^Bes!lmj#-)l) zsnqM`2Zo?Ily5kAW!Vq}gZW%JyR|;ppskMDDMLzoZ_p4jQ{hv&M5 z*!RjSgA@!M`fCLXxqT~Z_#ZDZv{eK%ojcVTAFQB>#ASwhd>u`Q*T12XdUo%P-$UNZ z?jinuemr0xzN1C&^t|AX4tB;9ow0D}xOm-`HyEd*%IRon;mFICmqPV)_o$a+(-V(% zb*Tpott-~w)fw%&l)P=167QFWjP|bhzPg6sdwS~ZPkCG0!@SD*u;hz1$+huEC11RT zYP)IxTVmUXEq=~N<(62F4?|%X(AEgm^^B6gXAHy{uw`0f@i$OwynD>i+8ui$+!>3; zgW>J*Xjk98awpuNP1MyF>km<9PiLP(!=3Top3OAe)7{*`x(L{H=E8aIq=@^RX?j54Jc(lJa*1esq&i>ALtS5S&K#kxdl+6Y*Qo5(N zw>ORvJlomX-QCp{VAmM!J@NinSNBeu>*Jl5NrU^23n0`!)6 zPhVGOcUO1X>9}~JyR)aSH`*WjHeEd7xH|0k99tW`)6p3CL`|z<-_$I9s}fwC1N#o{ z){zqN8+UEAt+sHH&YAsX-`R@~>wz-b&{sxNz-Zcta2yVL($` zHS?yCm!-})cePK|7IMcDLBJ<>hg?DU@Xc?N^5OF{S;=y)34*+gFnF00M&t zNdCUtb2!GO1CsPT?tC8~wmah8u%CX;JVKryNc%irk=n=Y4nWT5xaR?>%^%+E zaC}Z;?;`ob9D!)ZyCt@P9LFTyCRZJkI>NDI(xz~%zASFpp`SZoB^)sR!9Q`p7#Ki+ zFBm$oLmmTPBGlyUfjRslI|S`xC=acB)E1_w3BA)9+iLPHm#vZjN6O?;S8G6z^72zW zc3QKIwT@>g+}{39-j8VNyEv;M$CJ_~E&3IoH*XL)X?ynj^cRRV%I?E*`BgLk^zQGR z>EyaZ#?7wdbTv2*t1-MQWN-U0or1rkgM?H|-w>GZIN&{mp_i|B^UukMB+e87)8;0= zBYTJcahczmKG0r(QoCB6z(6rs07VgBkQSS8Y`x>aEOcP8!sDMkSj&d0x8q={yR2}L-Z=aD zYWCv;?0(19i`pzMBo}+=n`p#EkTp!BT?^mCZ|_t}X1jJ}reyt8zNdgj1v@ng2*pdj z?31$JsNc{Wb#i*(w5DS`pklTHMD7iUnD&+bjA>}w$5|W&Jd^NSHEaNJ^e-~rM{+H( zQ)~cQ`rT{{|4+Y3y6Lnj1VDdhRFS0J+gX4$m>?7XgNY9MG;1)L&?wZJG&ct2+v(*7 zd~{ZPv;`B!1~J~6YKPhji5)xu{e!n=ODD;jBSgqJZ+6Omk^ad|4TCy97)umw(QYAg zUW%QzmESvh0mS?O)b$soia$Em#$TwP3G~5??;7>+Mu)evl^T2e~RjJjb|8QMk8U<46P(+*Z1SVeo=E87+p&#c{~ByONJizD2| zBLZW-o!*K)fjEVJ*sY6Qv!qwHEZC1!-iri@OkNoEcD``n<+{!uB~n1%hY^Y^>b$L` zrooGZ&o-6?4H&fS1zVk~r}0%XP(9U8*Hi8A=}z86^-E~?UygxcR=?apSBr!ep}KKl zris$4j1Z%d>ft`yfDi?R3zsIEXm}5DM^BMct?~|kmHjuOSFp+h#^k&`g_MEDIsBIJ zTdtx_Gq!8QdtyKFzHMwDu~L=%k8<86^J{t@g2;raZ`uM*>6XuN*k`t8PEOnKMNCna zpI)L>AY0_7nSu~t*ZPVV;b8L~#~QPVez3~wMJ5q2Jf)%4XWl~cjnrx;!GH7kPX+7} zl91GolDrN9LKxEzaPL6`;m)jO6I2u_7kJyhXh;}h|03rPTBYx|0;7AfwWTu#b1dFS zu*;w)J3m66QzUywPmwgsJaa^q`=KR~rg?o^V{(1t&H{1(-|K7~(P?Dzq&@XMDUOJP zm&Ub~`FwWAB>+bOsuvQw2Yd8IwUG|3g5+yxQ%Sv~%)3t~%b1+7r?lWNJg?9(v;xdf zK*lN(WmZCi{ww}V^6hccD_q@!S=x~r))$fOWFQY`k4pmG=jpN+X-M$w5q*G9nluLN zmtE%=JZZ=-`K_94e_xtak;ZMO!3a%5f2?73*$s=Yy*wQ@f05Nj<|@2>9%;RPuv?HjtSON1f4VOgu2sJ3uXC{Tt}Ev5{eMVQge9Ow(gyV%cJ{bgFxuJ8UgIz~qEnmO;BxF=O)MGnbMLNgTzb^uzrwlpy<@^<7rX|DuKixR zP!>z<1qh2EE{4V6*lN?@YG{8s^}-h3&b_-0E;%m0W0wtdWR-4lA&5*WktVPJVLk+1 zm<2!b>as52zdxUr-sJb0d#dB0(JD<#$SDxH2RHb+hpsQI<}alZM~L%Z6MMJY@uoNW z60E#Z{1GEeRkrP8WR`2@|W zh@6?qnX1O|bqvvmJ!}heyh5HYQJLq9RP8_YMd2pCu*UNv=4v*7#K0~55nJo|GF8=@ zPq7M%eR1yuKnzn9V=Xc7_UCxT)4Fs66Y&pFFspTfx2S7DlDk~eUGRft$5nw#Aq&)%%Qs{N zg?i3-CcsaymO%FP=4FxVgMjBJ0`~)sGLenYVW|2>sCnbAfafH+4;V;~Fn+QDsW8Y> zJPwBP3cTnm8a>w%SWP9WfN63S{kfzTka#R02*H~eq!@yYf7<|RNsD2<%j1|U-(kZs z&&aKZ?C&l=*@~ zH66AF&wnJ!uVudASL*mj>bO08SX8l*?d(FXMEP)Kg!XY{WA0-$isdmTbX;`3*DW}}oesGn}R7JkT!<-!^ zUP01t=+b}?ijJxq5C8$jw`7by;Q2koVnih3gC+^cXmb_fV=P$qvrBAk%j8kB<>*o5 z_H3*49{(UrYql|9d~HM5CAQilIRo^HoH}|`xO?e&-5SPN8L~ssRMp#JJpe@msnV3E;im>=sYt0&2Dl_5LqGN*5FTlE$#f3=JBl)!9K zeQ{k>cQF5!zB9!ck(SurUQO45hG~eLx?7#n&3p{WB-+L?@8Eg4Guqzn;OzaITTh>^ zrk0@g`X(Oa2gH!r{${t_$u|ZyeA*j*Y$taB@%a=?+|N_3)xta)yCS|I@G-?M6lm2Ej$n21t6DZ`87RkageBcs>4sB2^e7=~ukD z&`G==|3onDhfG?}^BpL-pghLdPJzQ5bv9p?;nWQs2+!v%HS1@Ii+^Aqoh24yH;5L{ z@E-g3V6auMvIys?AL%BCIIAZ45SQX~SW#9E**Da8l}znv7DuYgb2I}(2%+Y?SnHr! zN84dM6((F#2j7Tr8%IWy`UA$kFMmw4OV;3^h_}qh&DI6tjrcbf3|i`Wwy`i+iE#rL ze9-MwA#LuLy4YomJ;!1oBClu8n?3BCeC}fU;3?^`_VyiUy9l5_!x|ZI3@H#3Pj3OG zm6tJQNM2KAWkBSsD&)uIje*PiIr|<6Ey8a?@;89y0>Ivt&s9o)t+7RJNOr(HQ+-Yn z=^*x`NHa5m{6JSmH@-iSXC% z9%C~5Hrg&CPK!uAT_i4TakZB0|Ma$;KE0V<;{VQ;hfgQyo!Kd+g@Wzv0T=%xv3tq+ z0`nhuJ7c$y)G4vaat;cieM*ugiS={d=jcRt?35Dd#pf8e+nj%u`5N9}KTv@r6KMyz zua}&+lE3X%l0sbixWqn7(nrCDCAm&6zsVcxk1O|;do!1=Uk%sA2CNod*+J*3Iu#{p zX%_H2j0>w|qMtG8U}fNNr?c|BbdC!)DDvMxp&CX|4@TMF*89zPwUnsl%<1Tp*IRk1 z#V~JL?>^gcp%UbXtW~gwMHaMxSxtD8=wm?yucvMtH13$eai_9UL(&yYZsBW6A0_GD zEp4``_#O(SbQ6Jb(bc;?-}Pz(w!+LKwMT z`sop|eh2P2IfjKd+{p9Zf#pIL#fg0u)?v|eH8g=7rQ*VIaNUr&9006)(m_MG?L--! z+5d0mS{3ON^T;tgRzahCjXAxMp2qXwr-=n1hG0UMQP+6>gqylX%?$3DSkw^&84}#| zD&X|*AXf#}y&%2@u|$}g#QNx*LvU7(tgbl{5bKW&=k$fea`Nr0qci_rXdYy`k8pFl)7fR#y*R1`S1O12H0%I6mXBE4~6k`4+cT&9Y~gkGwo?^^j| z#)w{koVD~CO6O;1(MDc+?;a6;%Yl9E6*L2WC)>9te`*NJOblbY{d>$=LHw(16bHLs zrY#GF?c@k96kI&LP`Dhgt0wj&gK}_u6KgUM|29JdI*_W36(jz}x)%yN-p0zRh~2@s zmpain2%){e@<)uBV_ExS)LDNg_ZOYg zY3c&#a-Fy0&klc&-+4xeu>!39CtLaca(U5x#u7O*rV{)Qa{V*X9kP@|jC2t)&IXMoifT>VmpDNIwO;UnF9M z=W@%7^LP5!1zTEMg3;SNzI?swf@_by7@C|+iA9wbAtrQ7XlS*DTEl5r=X^??1Wj)t z2ZBjT1jvg;&mvBNb_`*gN?GX3Dok%0TU}kH&_z;fcoz00a^b265IB^Rm&fXwJ;V;48Q8Sf!igB!K0e`Dz!2!F*84uxdItv&D0Xs{p1)V1ItywLTnYX&)H1g<+n0?FmfyS1N1&T ze^_`o<`nstJR~Q`cjN-nUCDQ%nubJ&eN&ndu0#qdnm+?-E{$W63d1}le|#r8`xFY$ zb1K#6H{d(xo5=%`RQ~Yl8Z86Bn3mQPdtSO-+&Pq4x>S^+hxN=-_~f(144Oq*7%$C) zBz&m_WQN`D_i*N=iudr_FRr5mX`a51b1^8<@!u3R!U)1rOy-=*W#E2W_;D<7bIh|yb|fC0tGd)Uo>E}`Rs;l+C@q@PK0 zJ>7B;YZM$4YwK(Vi=&Rtiu)Hqh_vN$^h6bnhsBCzQ;suGGI=b~5nD?VWMnsB;y1|G ztY()M#1v`?Lj#VArw_5WiZKU@r0OH~HrZ!BM!TR87H?$YLvLgwLRIA04zZbMuG=c) z_2kQcuY%R`$Hjsf#}MC#;+&iB74}&904rOIWF7`%qhgFztgB@2q>NBA;mnpw`FfHz z5$~)l2Mes0ocn2MH@+0pq@DaZ=$cjvUXg#=DOpsT^N|xw>>Decmr&ipCo5eaAnDFZ zXFFBwX7bl7ok8k^o_mk;jIcL`%=G+L$Bftw>o8>3)4>q{s@8j)EJ*cOiVS&C?h4M@ z$gz{NPtpzlck!n`OYP#zrDr9!i8Lh9Z{=|M-G_!)*yFHu9 ziWxEyp1~gC5Y2RC93U<@GF}T-$~MC4HT%)mSRv#pyMPtlEF@F7U*=0gIVfuI+p}p1 zfu*qSp&LZ&F99B#d#~KN6rQgnwuWxpl`-;jATiJPQ$w}}E)&^%AT5jq-Ej0Rg%==P zTIn&^H^{0L$y(?(kLXQAEP{k_gi(CidJ0YL|qFavv4WK<1sP5f{ByWbV$_v zzJfnq5f2p1=Y!`Z;qQVH@c*s(m+15l2~^A7i!|d+;{u)x zK*t7VL}miAPxU~QUaAWm8sB{}t-W9%Hxua~0l_`R`fRWfBb&fEFiO|ZfgG5AEAOfZ z7R}$UgGJn!xs}dW2*r!}2xne6u+Uxq&V)UE`Y)jivPh@RBiXZn#Ytcy&LJzJg`}Fj z9vCeBaH0?T8wb;ye2FIqeX}0`yt0TO;O*pHxSg$VFgt(+u~lg0w96zAp|{+s$7{CA-u(3TwmI`%-4m$Ty!j{36M~(Jytd@L z2`9|ad=HGY|IOfxQ&G?ZL$l^BjkSvv^kSum8qE~JW|)PC0*`DK%L-_Vrn+cq6VfLZ zu+|599-+2T<~RPM;ay3zl7h4E2zaOOCjKhxK@Vpo*3%$=vQ2s%OCB}ze{F0sTR3}j zx^gqk!ka>Senj%y@075($S~ZKW2b5)NU=Q%Pqw`yZ;s@P(Ao;QY6)4D*I@Y`j!7RA9vmOAA_WXc`h0qv<#! z2rM`g`qkS1L)Lr1w^iM5P@(S?9R32jha~9c0|34{hma8VhT5Ju=l$$s{w^(+Ro^nZ_xstO; z0IE`u>{=*GZsG+#saiNraL9>p0e6-bO$!jH*4VVa4UqlYBI#K@l_R_;ga{DH=X2#BB!@(B{vIQ0^_IqVaj7oAIX z>+?Vh1)$|0w6PH|)vmG|f+{@jI`6!w?JXawop@s1@Z4jd^qqX#vW#ti=g8)V;=( z?n)==P3a`fTS}BwcLDp5UL1EnQOM3T9ODfSpbI$qT*YazH_8nxBn51QrJMS}=8bDn zocaud4UVi5F3YA>tUwFd3G))5sS)NH2yRxoH=5XdwlL@kHs-k^0D;s8H$*npARB`Z zEnXJ~l-C!+_VpB&VtFXm^l#z8_Glulg;0vS#ULZDwuS6MOcJ*jDt%rE&rs}D0upL- z*w^$r*$I&(K(O?omy*x!f`KxMU&1_@UP4;)hu!h#+y#@c!-Xq}@j`*#53YbHUxOfC znJg$;9x*gQJ16)~uDPA$0FLi;r)G*n4WKjZhsxFr4((zWyNl3l(t(Z-1`@Gql$1B4 zZ@UfO660a4#+O>}B$B0;yE<6hBj^4v|1n5R)yW05y9IO)`p?U zpl%MUlM|ufR_qyokY{KFyLr?ViTyTVzm;LNJ*>8b)iy6Vwh21}83tb7TI_Eo4eRi+ zr^~t4O!o6lG=6Zck-)Ut8;_1GgKSipx<4Jxf1y5NA`M(A)J*nbL4{=jmeWmkSZTho zuzIxt0m&0w3>BJ~Wl);p8kvFD1go10ra~fpBFEru4@M(f79`UFi@Yhhi;F9P%bV%* zkZfN&d2(9iX%?OmUkedTXCJJboloUmK(mIc?J*=%nP!LU^Aq>7U?75!gt-GF>a`3iy9sbNG zoaLpgqYbUqX+~(3eQj(*L-CXh!u`+{1xFo?3ha=JwDBg~N?8F(YbYSiy!Eej9jAO$ z;l^>_qfpN14#BX(#&}>irTrvp^-b$M_w?(2huSY#{T>{EM2Pye@P zv`_8iZVN2VBmxr^t4r7m44CDIJhdq};_Jg3va@~a8VKa}t?KFn*eX2k>r2G>w#E5> z`qcW-(U_g=>Vi}tMCtmB@w^d+0l*&Swi@LcaXlGaA?u5jtiXg);F!L!ByA# z`nXr>>^x!aQy2L9y7m4()mx4;aDFv5E3kv*nCh+e^__OMy)|c%6E>U+f5p%1M{q}8 z{Ip%iBkogyW#K8#*af7dyrzn}D$3{6V-+||J31`C;fhMQj0SpAuBbwTg#-8o2VWB5 z&SXW6hqCpwumVX;tgH3~$z*?`f%??SGFVY7$tRWg)r_(MzN_w|Cx*TJdRx4>3VyXM z<$R+O8x?#5d$&~b4VFsYA&p(dzQI;UH>kp2<$bwEG~Djbsk-5 zW=&)}f}7k{ageU@(Z0Gh*OMK5am2x|BtJ`>(g!h+03TZL3KH?*ujC9&U|e-~A=oR% zq|0d&pMjpSm|B7LqNaO^fQ*?)8~JpZn`EseDb^o|L@%RC<6p>`(`rH*a2ZK4olQ)? zCRRWcSM#yGu8uYGQE$zDtqoJHI->|(rnZ4JT&9MJYdor%Zsq=ALM5}^38A8q_}cZR zzm3HC)EVE3#Q>i@q`n(Q(|l7D!-UJ>J|w5D-8nP$LiPyxP(DMRk>u=7B@;-B;eY`k z&>j;^e>MtF2Z&(!^iR(8*ItC0gq!Cic zxxT)w!@=N4Q#PrY7c6yo7kt!E=lfW&Q2u1{`a} zg~*Y4)S}PHNr9gZP8pUZOzdT9WCT&}};@eabz~mi{YAHmTQX05p`lHnz4UuKt@CNz` zLYS^F3^Nz`&IWE~6v113RlAVCd%eJ@fikQ|H9qr2qDLJU#vr1>b3(Tiu{%WRmKtaD zqasHv#J!yq=KDg*CKh=;_>;XTf*sQC7L9y5l=LWUmZx35bm!0K$TG3Rq7!V@9olaX|}cpGNk8;68{aM zBl3GX?lKac(fVvA`xq zH$-<4mqmLoG?VeuT6lLE5Eh|F4PcNW-e_AB)8tw~SrDEhVl5A^D*b&TGvH~YySGJCNPic` zNhF+?VX(;5?knhu9-`@uPT3Sr|H|a<3m~BG@i#^}SowcJEkSoDzu=2Vj~}c{8UD4@;z~r_E$#43j+BQ6Nw`;%5R!vPUSO~ckLo0oHYZO74Vg8rl z==G9oVEA`(lN*L?w(3XX?~K{!{47=4z+aoqfi(N4L`9ujNHh47Qtt! z9)FA^mw0^5SmE3Ro&Dudz>JMCCs^^-zWV4s!MCwa)55V=m!Ew<;C z!v)>WI>6rC5Z#{VaH&muL9OS5eN*(s>TaZiLL1w}LYH?urNmYC231y;unr!y@n0<& zKB`>c3G&F&#!};@qOH9Y*?AE#lx3tIDCV-?lAYY2jQYd=HT0wKf9dmllCzUGB#h@C z$P&IMH>uFO;Sw*4v}JVHB!>v0^iw1we2-vn`3n)hCH0tT>MmmS9M?6E8s8)n9OHr$IfI-ESW(x+ zvAGRkR1B0Zv&GaT`eyw}k&6uQ)E(e4r-j9u4q5E&A6iyn^t*G3d^`SF0ze9wZf~Y4 zZST`^L=NW`%tc4`S<~^zO9e~AYWP@%B)e__F(OQOp+h=FgK>?+8kQxUO!D~SaI#|n z0S_dUolBQ=uUOue&`h~Kt?TM8A{1aoOHGa6FQQHw!U&s{=LbBca6r5EkbVq$ z{OpA}6C5v^0B_kq)CsOiB))+avl2!9*&tL93C;l$&IJS*T^LNS!r@zuGp~rcS4Y)U zdV;P77y`$_tlkji)B5FvzHSzmp!u2nx*6Fbv6-Hp%pazf*Ujni*Ugv_;px9Sdkt<1 zles}zgQWN+auaOz>1Ql}q~x{w5F7d2(x8Sn^sT5lVL${Nx!kRZ%s6yF5?@Dlq!I6y z$PUC{o@YvHh`K^se&g7U7Vxc!y89N@BBPN+f@llO%qnb7ME|OYwH)!ownAd8C2R$= z4q{&0$&TT2xVxGVN{0qDadz9FUX4iqg3j^qd?NR8jWm;&r1YagOD-&5Q>c-5*b&qr zQ7Mih(o*`f1OzIYby^+c?|yFNxyR(Bq~SyoJGhm40agZ?^+%x|vB!Xt)tuiNExnjl zwttGWmQal$&ASg1y20I5A8N>5tkWIJZHAtVT{p*7ewg`{~>&B{_)J{yzavM5}T z3r$KsrsI<2jMj9b1oz|vKIi1oaHBy9Clx534XNh#{tlq8N=ovGbF50C>j?BU0PI-= z_1G`insrfaY1Fc4630h#txY3RDP4xG^*IRc&<1REAfEnjG6m@#{HF+my9{u;5Rl7d zgiWyJ_%e>3bAY(Qp<3$+C(_nx6bi;V!s|ezjBiUsbRlkp88g@&85#moV;MOoAN-_t z2=>4u>ORx!${+B;wjV*#E`Ojp=Od`&j&9E%fVd`D`$H0`z|%C6ay&^hDcA;>SjY09 zJ`uE&!c`Nv&^BQgc8rYX2gZ{?jz~RD^HFsq4*wd0Fay!;qe!tF1I;8qp(L9>6p6P# znl3DLEBPN9j!T$^X0(8@YH-h=%lqD!en0={8O`Uy5fXuP2aiHnNH_N$13-1`BMWi4 zI-xf?Xb+)gQi15RgOmekgPsY6Ei;r$@%w~niFbJA`OuQzE|Es0PV#dzm{r8ALDzDf zS-PGGNu(0L1rqreL`WwmDgSL0*)Vv93FW6sdR$Dv^q?V(baF6128%5iFpy{X@i37> zPdJd@zT~z1)3h3h^lb^6Hz@fRWFQ2H$Oa;Jm?fBlc296%&t@cDvu70`FT+q)g#M1W zw(2M6pGbxjJba_R{|iFmkA`N%q5J&6x{LDr!bhRdj5`qyakB{HP2B_-uv*boq=)vH zk*wgdH=XlMEh*6IaQLW6u;&Oq!ieP5GE<})4-h|G^OxQ%8rKolYGC&nl3|^?`}VOv z{@+@^+X^3miTb|^zq^U)vNUJ#w8XEx#In2Yw8VKfv3o{x>iMb&7$+elG5q{}?%a*L&GfHQUKHkWUI=e?Jrg=JgYK0?%ASVe z6UdZ0>OhfW5eOulR!8@9X;D{Q=-HG;E#z~Xk+B908`N)?7Gn3YD)OzMg{F0GIhB-* za*0kzpud!&jDLfgFOKIXapo9Mb{oNUu#mJkUWn=)Cy)*bO(kS|Ib$VeA%BC5rvH}; z5PTN-|0J=981l^Nfvv#DSq&GEHR={TTm;4kONn_7p$E-WBDwK0qKuXiy2>t217a~Z zBYHDQ^G9j1JL-nSvOk#tXo%F2u6;JU(67HiZh&Yv{3BlE<3}KVuvq9n64M%D@(_gO z^|GCN%F8iP0+31P!x4L*p3LS+$6l`b)E@#pc6$jBNf1DHA&-VEjE+O=z?LU!lq^8D zshbQ{aOtmLC#mFM#3fJTaDY0`P<}w|I;U?x&dMQ#YV;g(sE%)Gcuu}(rBsi!RlE=p zSQKISUm_crANp09vfD}@p*Qg8O;y>#z_{nk%CP_lLF(Z68%$A;i4$%vbW!CQp=t3U zDn3A5e$VW=$VMiAL7-07;o}}mt|>6gm23UKlnV2QENmyQIKVRsDzydg@pNKx(eQ|D zu~{~Do~JX>PC%8#2~ZOIFqq>IqYDHPe%s&z&B7HN@y>4GQ4%0mAr_oqmTP6nT%v!F z#|XpYqZ+Bff94Y5L!moCQ^{7J2_Wnf1~F3pv<_UDB65N5-n|@rFsdJ&u$jJbe9g00sP2Zc<%8OmN zL}mMC!pfwq@W%q3D-`92-(j5~_VtDiM-KA(3Oo~}2JE1Xl}fRi09F)@J-Z@u+$aQi5d;IU=?||1hu{pi1e1L z;cLa&8qiW$IiD5^W7=K`uqJ>e=BnXEEDP`gLq4H9^Z9!!!O1UW{N(g`!T1`ZuMy)% zOhg`MV3so3$53cbEQqO-X{ZWNiy{nG0xhucdpnD?pc&FH;c3AJe_mI^T)(+Zv<(7# zdXYfyeMU$|93F?Z_qQ|g7u2rxQl;{g!P?J~jN`OOD=RWoAPCf!1WVkX~3SOc@(izRA5Gu%mp zr=V2IfQ=AdBvRrIlinhxI>xrbQwzJDZJ6=GUy!8#5bFw70wVF@3}qWM|E?OQ-(|L9 zfT_zZ;uhJ}=XqX)jfmOw~+M6@1cOu?iWf;Tav744}juYM%k3;5bG z=Q*&3`*?Y{yQ*&Bi21&{@)}>&1~NzYSzVR+k>JAo?|^IW4Y`qC4Wk!?0Jj#aAP)II z^d?;m`Uj9M%HQrI6uvZH>Ix4@5885_b=8CkvqRJt%ZFPOGVhO{D%=`H(~ z6y)pQlT9cz6wwwq$fhp-wbwzE5a1uK*ORcV!FeuR!QYMUjqM zP4>o>Xg_lUmz}FJUXX|_!$X9|^K?;#t=VKhXAMeg!T$4Ch_3vmBbXT+;yb*P2~D|c znnQ3xoNMT7#&5D`B-*%q8|3g3vf*li83*+hP)3jl@qsxHP=eb0W@;nc&~O>ER>L}8 zWl>huTBDo!%3L3URQe{qwIYKItmGTWVcpUK&ZFAKD`qBT;{O&xabXJg^JT9^#v|(ixscPZ7q_-uJ{`QdC9@0Y2y-}?t1n0Xgbfe)! z5|);7&L)jQG1wmkd(sVdC3<6cu69n#r7b>oTN|QTU z!uO8gr<`$^q@0i|u-rt!S4WkOd3fU&zHng}bYp&$sW1*b73FuL&kcT?%8Ec@fGWqZ ztn9D|;$VspNa0?spF(>N4q2-gtkW;eN&|2qzeb1!gQ`B{_uyu6|MTW;4sGnG&W&&~ zG6Tk-Ro&h2*tl5J6rcDyyHP)@P~xgc%pdHH!J= z#7j9s4|w3Myp8@R&Az0U+3SyzF24BpU__1P&4CyzkB~1{xQfKkr6YzhF)$$3W6SkOL$g9unv~SAtZFM{Nxe zs*G(X?60EXJQJpD?L}iXmiJT>Ei>8ur{A&L%O?i}@O0<}^!Ym6 zeFJ~Y9OE)L*mR^LGZi0LOs|!LeIc%}8U?7lKzMgfM-a7N00|ZZ2DqC)Uw0f8-Q1Z>pM$?TGtr~PeUgap)Etbgf3nN^L{CZkyXOq zJ4nQ_nK*e2p|T&H$^EE400LM(LEMGAA|JzWTgT1m=vNDi)#&eY0S-mxBf*r(B+f>_ z;Lb_8AzI2|OQ0!v?l7<*-I$-cK{tgA~f|Q3&@wUnN!(N5=)}Nblh;HTWTg zAc2LSr}g~L3$w%m(t(J3B%GUd+@OshtsLiOAJ9$cRKTJw3%MVp@>PO!=>o~MB&4M5 zPgsFV(1Xtu`j><__cSiJ@~bL@@_bG^w9p(~^+3 z#HAUI&sNr*2Xfrt<0a)wLMTGVuJaVSHo>>M2eI8v4}={4mEsA8L#tsvs$q$6X@H7< z*gMK=;7~irZZuOB2S{xZ_X3FH2L4^^TiXe?N)mbfG^z-}q@IBL3ej@8c$}agV?W#; zLIeoSt(0u)0pv6m*0E<%6R8F(aI!=DlY6W4)H?iwF++yP(}6H zIM!2-*Fdu)t8h79oix9Z5KF7!GitrM{%5iu&n2jh45}m7+P@L7ns|Km;C$n4NvLjM z(A{OFqv>K#3ALDoAE>c;c6=>YK&{uFmED`P4b|oAL+(PkF{t%mK`Y4*Qe#oa$aChg z4sh*#9U~Vy56@IEad)7|hsS8h@a(fF9UE8?-~Vj9@$_iVf3k!R&Mzqn87^`K^%qc~ zD~`DR?@dKvy|?A;CU+5BsnXuNQ$`ZF7LS5bd=1KusE(VP43;C7UKIKwWeqf-ibAg$ zouSR3Z}6b2f!ZJ?ZvI_57o~FtuS)$}LR_K*inb7TsY9%X9!-~TNN>P5F3%ie8u5eg zt|+VUuA;a~CuhHVE=aa{DiH-;BpTil?D}(9;Y#N@LTA`%*pwFf4Fr?KCN!HLLY<$H z1pqpIkctWkD5n7*HW^hVlCSgt>tlEM^?MA4J2L2^q%qWV!b~69O@7Nmz9aAtM;6Ba zWY|B#Syf!@bZy=Z{1TxY@0rw(Cr?aanZ=Jjbrz}vmyu@lBX9+}!K~8V*Jj)gV~0nu z;Q~d@U_z}yhd#cp4fqA;^uGiel{#=NZ|(mGMG z@-9Yb9SJqvKJBBP3s;$J(D-0~SfXeP7*I3XhJlT~wcZ*O zwW?*^Xr=AJ4v~ICOb=l0y$`h%?j*~);9)%@H)FhpUL@9h0wMWq`5-Co7`%Z5I|dFzlr-2=$DF}DfDBIK{)$%RrY3iRymiyPJ-$o5DOA6}H zupl`%KY$Sc!xm(K@jr8JLt#+AOU9hC2eVQjpUA%^0v3Rm#kpbaON6{jjr4H^6~^N* zp3Y(PKV%G<11{u!g8_~Q6z^y=6%W&-K5?w97QG{`Kag&Oj=OesMt0f$jKXQvlY#ph z6WAGIM_kw9`Y4=@_+JhC!_&MY59-<@mLH^g9?v@sq(DW;1QXVQ$j&^QG~d{OcuD;H z?BfPr5m6t}CKJ&=0I+yKAP}f8urI&O1IbgtmPe(+gS?8RKLM}vW6%dc424Vq1$YgS z9)O-^N+uyCm==5l(G^rQ;I({4acQL}ATpq)B%PlLh`D*&gZ=6X4VC+n>C-+a8{q0t zTqYR3kH$~eoJk=j)cpc~$_^m{2RTJfm!;uMIi&pkkB}xHsNIhdTFO`qN8#}5K|P_~ zm&CE>h-C%`7ZZ68Q-1FNP71yi;?wZFpC)vup(kPAccO}DioAwb#^jX-L8-o%)WL#VMNI5H+L0(- zwo$xgtFCQDff~Ht5?+IShe*n})&@~HH7vLh=1gA$L?&LPK0#fk(TB)u1ruE>T(8APU0*do@tcpY{4ALx~{# zlr5Q=e72~xJ82^~VDC%4oI~WpT%_kNhpR3)I|aG~aEZQ2Vz1E#_efYRR2sR#v;z#F zRKp|@U(UA0FEr7QQ|0r?jlP+xpa8I5!@O#zZgERrrgDkcUM36>!#6pm^P&5eQ0T>% zKsc>8uJV?ZEgam6aZJBK*(!un~ss)}>xu{LA5jp4g%T&6Hu_dfQ(>Ci1g?XFw`OIE%mS)>=0a>j! zV0c~%Ow%kn&c|b1Ylx7u8}zwwBzNd{i{FSCF+2DWg>Z(BEjZut?V_^eTHzo%A|@q- z=|?{)W(7uixgAqKJb<+L>w=>Pg1Q@oqIhYFiLXLKbt&|ms`4s8l-hD2H&rsDwRH! zZ1HvGqR?Oitww^srNrs)cBi``F|YYD*v%>3QyMb2A-5^Cpd*Yxa&$eZaRoi$20Gj5 z@ZY6)A=~UGO>nKE+pX#TQX+4rj_6NL_9+#f-f38$+?9yK{hI>B%wzxVrlLyNmkJq6 zMCUx}Qxf~sDn^Lm&s3nQM+L8iZQM*9pqyvM2{?)b$h z@K)T&pv*CkLSMh0h-VQ~joC4|)R}Svj}G6FB>g9UDQ+ZeBj$O;{EvK! zfW4jc^84!k6IIv|C#teT9BsqKR4N}O!h;m;r|ON&IGpemx+W0e)VqkanOSXvemdJT z`6pur@bf)5YG0ojh@UTZa3^spTYN;M@3AIulHz}1mOn81e5&T$Q4Ig7ZiK zjf1@^=+}gsn*kmInwo0F`;R1}3#p$m@nDCff5~>p=hOFNMRQiz8k42u+57mwT zKn!jozeo3kHQWm(>feVhp3Pe~bg->Ku$ zfeCz?2qkhxv_y7Bj|k4_ha~BUAVMrT`U)N6kw-udKt~YlN zJxcmTgN-d#|0G8aTr%WL0AoyGpXrYD$XUH z<9t{nz}{ma*6*pXZ`d8;Iwk7B@^to97QG!kd5h`(03u$)S_F2{kOEHkOO?Y5V-G3) z!!R}`1B*?27oi%;kQPt_Wu(XB!`6RH^kw_2+9-||P-7d0H>v|7v-jqjAdnC+lNjdc zfHvg$CUXL#49Lme$FignC=&DiPu#HI-HVGDhTLcl3Dk{fy}t1sM-a#lq-9@>vqI$69oS3K z@#+cSaDj2fxRpGfV0ED^i#y;MB?pF3^fsX4U~!T*6elgX#-TzgWe{6nAgMO!9-1S# zhVe|8pXLtTLDKQ+8N$Kvxr*Pc?Z;ykZ2qDonSdT1-g&k z6P42Wj9$q)(6H*zWlZVq1H)%xc5X8YQ4OCW54$F2is=r`oeTOXfM++dokMl*UTs(% zZiM6M!yg8Rq>w_edHzB^xVt*mRTGK&Zgp5yv>cB%xctFn3ACT!g zNe^@47N6QN89hV>aH~i+{wq~%PL?*NnOlhToh+9m<6XF?jol+f@k8(MF zcZN_fqU}XqXM`W*5fFW%JSv6%z}yStj8f_)yy9dCd_ZJQ2YxnVSsb`jD#_P3%*u@V z=2t^d$Sm3nft-;e2Kk$0eS|L-(d{YLeZ+DpRtg{%X{-jd+Zpw&zKtnd4Y-f|@%?${ZA_H|^2+Tmr?ND+S?9lT+Vrat>#coqn`hBfe%r z&xnSNDWPcuoTHD0+`ip%ahd0oSgva$HI8Qd61m5;=?5%%`V%IZ-Guq(KEf`>BrBL6 zGx5pt0SlijAFw#}3TBuPg(sFl2VB>!dHMll!SQxE&kxY)Ge}^cY zn`RGe?FJ zjUcDG%uLq73K-LGU1nc;-)v*&!Z#-S5x3?s3RF}r?QtRL$cU?hi!$^e1z(@CNFC7~ z5aj{-7*6)&rL#m2IW8l^4+Q#*6lNm~%6?>0V!6ZzMga6_@0IQA=A;8BcN^A|xLT`d z-*;rW9mI5mNN)~0{Lx{^g3a$?nb6l_z)r5QWHsf9Jr$=h_Y6v5Syf}1*oE| zqG#h&`~NG=I^0tZGx%rL&2u)vSIGm3aG@IJ zxQILixe$YkdsF4Jp#6Im5O7EoucY)Auk>aEJ%dU|Omuy^p;Vw}=h~xtiSV0pML*y2 z2=BpA%H$e83z6x-8za-`gU^gl-sYZ;JVJVUq`LI5tMqT9b?&i} zK|YZV!izAIa?j+Tx%CG+h;O#THg^Yk`4f-&Bcb1lW0M>y<14#aqBTv@xk6s-Tp^_% zW~A?R;r=Zcze8HgkD_-m+SrR4x|_<=heGAMy_}=IMH^a2gftU2j+p8v1KJj^FaX0N zpblmvft`<1i@?(Fn@Lz#__mypYuAkAP{};5^IU@hfwcuBrz}bwgZtd3ILWMo2#Ift zI+zA=D{yNPse&N=L0 z)6YqWN&7P$=c1wcG1W#SCkNE+VxATxq+(pvdnx3u+@pYkneIn>G;J+yS^)~UERC}n6uy-I%N2pt-D-A*m zTa(Z=1w0S}km_u@FB)-k`W7N3o?<<6Z;4Bd0CQL7@gKGn=Eo+Pa9al|1ttAI+zf5`&! z^I9UTpg#Dkf44x>U>naf4MwyFOv)DUu*Qfr0&$aCq72u<>AhE3RSwb&WXIRST#mTU zCbpq4HJl{EF4TirLk)4N^lm4O&F=Atm7-)X3JlTmX7|c%G-suSDHD(!{*A2*Q5b(b zjk0TO^wE4CwD4Bk!7NSGcn{;_6*Hct#vb0vajg2_@~oZ+WR9T0j|J;_mYlF zr!hu>1Oy2Xmx-W0a_(-M_HoTH6ukqnzE`Pr4q>Z!r=3e2(g*X-9D#1w!1j55lCduy zfkT7xmVI}o3cCuLN-!u6CK*BCxI_^Gw2+ITiTBtW`MO|-PB@2#^Xf_Wq&DJWgJ(#A zghd%YZWTbkxreedlLaT4&1GaIWJw;(QQ{xDdwA|}dKr;+6Tw6vsV&ql7vECo8MAKS zbJ%u1y|nK(*;=6D+GZeQ{fUZ6+hi0Xk;$P({^zayt$QD8=&l(QIf52Ova?L3bay8W43Prr>}ctucHy zhyJ@_8@k61%t=S7v%Z(YUugfZ;ULFIVpYV!J|n^$W_SP*&1PM83)n_wmDD2>FNfCF45{NS-v^Ca~>W@LlEusw2o4zekp zNoLB{yh4#b_yS{Z(LMaiAWbsu?-1cE zCVxoMT8z{&`5SHnus_INBDPUS+d-Puel6XpwIP4ornUV} zXu(p}b}_MazHSxYqYSHEP-}llaRwtI`;J-~DElF`AgHB@!aUJ>0qRTp&LK^_TjUwf z^|YZo;sAdyWzUJi^@0Ha!Vih~ffT5$M!L?ILheS%0(2C9l6BN_7c&H?(sI3-o=JgT zWSgPv$??cSl-gR-m|>jesUJf{0LJ!l8UL$ni@42l+GLf8W?>b8nn7EEzf1E)Y9)px zNy_q%8M}3E(bkBC#kWRy7wUd{0W(Tp8F-i$x1zhdF^lv0JFc~>?iWZ_yqXDjQM+e? zw(;g|pWDQ{O0&UOZ-P3oP(|4Ja)#&7T&43qA}`3y2tHZN?h%CR#N6O@qHvdiog%w< z0|ZNpib{!TX(sJiAbdDq%wtWJ6}9S3>oPn|K<6M*P!HSJP#f0T?{!$NHlyri*iGr< zSe;Q{n$g3lnb8G;9L!8+P7725v5S6R9gtxve5zSJz02H*CZbH{}$LB|!`XE$31HO;?pBBE4&5W%ZL3p7gCtBJNI{uSE^Mo}?^Ujr?Tz?}!#1lX!x`Q7VjxqRPf908xT_ z`WjZFPL4)}%hEYh$3AaeK}egGi5dp!dQOJvE_*e8_x2+8NE&lw*!%`m>P#z8^=qW+ z$TX5ets0Et+6?No@btC5gTxXpk{SxZCJBOga+YAz+j(uk5TRE|g#%i(MH%^{dd8We=OBd>KNls5(eN~j)5@3&f6m{M}^NlhPr$25bvlq-kV$*jW1v87{Ibp z<{9ZTaAeDfHu4Y99~@vl?1%R=1dWyP=s)CrGo`(J<8YSts!*?J2E{)56uOq--J^-~!v1q$0Tzq& zAx5nZ`2%6jh}tV6*5J@fjpEyaJhpKIH}5v|lRp3zG=s|@7~c4XN$Igk>{Z!|zVwVl z|6rs?WTB1;4-&Ddnw=2y!Y4$=)+AR!dT9$16Cmn2EPmYL0%NBCYf(ZFATHAL#7ej) zSBf{HN)fU{XokpVLI*Fx6qyYt=Ch~_=aHBE?#w&SF{r|a4#-f{HLBzc3+@(X%nWf_O$z%Fu6rGr%* zcYBiP9UKhcZb8)~PmBRkd^1xbo6<}dGcSBileH1Y3SDKse>mnIiYlR`-FdX5KBOst6Q&VlObeh$+jp*E`Eb+byndLtv|Cr3H$f)Bn_X6l| zphrd{+fSFk1uJPQ)RCZj?96N3!o(9IxQg&(rl!#KyyZ1hQ>_vAiCMxlP0x22ovk~l zBX|i?DyTtIi79jv@DN}8$+~r6pa`@#j1Kh{a~)C(CylfL_E+g<3vyz(THk9%E;~Bk z>7YPK)CjeJ8Bu=mmg$`f}VFTII83cq_PUujWD>qe)2Y#~6$Hq(+i zQzbFHZA@SKsgMyICRi2UxUHTSarl)PY*JQYDo3-vn@1n>2{A&?%7SniG&=J^2d^au zAWMb#fLiW(#d#}Yed8Rkp_X4r($`){ye$#;BTV?cDI@rM6T4FMA|d&Nh-n+sdiA4o>@2ePIvirp)yvtv1CG;dP_xT(u! z@k*&2g?kRzDv+FYNm81fEvJxFn!IpfkuNl1Fnn*^n~<{M1hsbDewj)a6+*cj>DLBU zB^plFzdLfe+CD zV|U4!aUjWS$v&Q)@5Vrzd<(RdYuQ&4H=}_eM!*_rgn>lveqfDp?|_>NmZLuFiF|wP z7XqODh>Y4nRe6Uw^=ka~$2sDo`BZWY@K|gN)lYYU%!7K+`t${+ZXSWtk=ffs@^k<# zzYRPVrtEGV`r@E>)Qh?o$IE4FIu^L~9%sLt=l?AybbG)tyX%eI2Ys?bn zv%QIcRTvHSooIYmk{%%R3o$L~D5f`v;;oE!qHwaO1K0;Gx`R~1{{4Nu;j$)2{Hev_ zOQ=@~y#1TTv-zlo#9QzUf%X#l0qTmz`rQam4GL-g_1V@hnoZJXs)TN%($&;ZX0_{C zGYsg@8Q=h|3E8kfkzl>atnW-Ka58vcvxj(q*L2Ar(6 zHH!|cpdTP?DZ6a_U#}<}7`!;N=YN^RFQg=a-!uKmsuL8p2WBW8_ubdxg3ySWc>C%? zuSm3q)Bz&67R6DciwL+}!np|1A)Ee7glDG|?TG$Bvp>+tFM8$MQV}CUVbfBX0NyxP={a zNCQwr(IuF()#wYv7W%x^51_coAeGBPufW==xR?y{YPDL&N2Z%GRU@1(St$)mJLgmRTRPA3N&D28g|oOSJs-)*!$YB9_&DS6ByvK7XCBapyI_*>9GF#CX- z6ziAE|vBBc-hMA$#wJ$T|CzatxS z?~&v%mLwDoTAf^l$GIYfZOu%N{zb|i9VT1wGh{0uWiXI3S4SapLD*@eT-NcY5*LDwB%v(5u$l9cWuRjvT9Uy ztGyxvtQb4=biJc7cdL+;Pn4k_m^4L{%KssjpOWe3EFoad^ZcAF3%BQ%V*#siGDALS z&ZwMJPxu;UmQxIuWGcP;iFAozUP{FZF@~zpNTs9Z>E&%(Mmf~SqdsA{7H&Vr9D>>{ zsg(#M9Zo(5$sn*JMoyz+FS6bS1(VY_cckVgnyGNFxEWVgsg{Rm-u5E!O)86sy}t+n z^i0!L>0N-nYC*4r+2_mw!=H)p2uk#1Bm<}c$Mm68muLFM0w!egZ2TI6!uR(U*W$r+ z5XiOCymZ;mlx_C_JkBRpE&Lb8S_OJXWBq_0cV{a!g<@gvzd%Bj0azO0JJ{;37;a$$ zbl5i_a0g9e2|YM|x4@pAQxMgbWlEU_kuv$P`3Htaf&mPSK8WWSA3k?E;V54<3iNCT6JuDf4O6XAlHC9QL?%#qll`460 zaN7^YvYLt-kOFwZ$%Rnu=^|HkUpkiaynvnT;gXYEph5Xvni6DbnZd)<-n^3-9<@FP zON&DbkkHm6tqdg@E_HFk8epqm6EmH1?NU*Xh36Z$u?>e~Ke^f6CsNx&!MrrjQ9L{XXRw z&*XWK?Bl?+5!b)Te&p=jNOODC>x7Jf{1hi%v+%NY-bhZ8MxMCCc}K?+$)z;Rp%Bd7 zV&dypCdryynOX%<*nw2JKAF`e@A1Lr!mgg}9M37uTelFULl;9A94H#BifpkpJ#vGT zwd!TTw)rp-7cH{~^|qX856{k2H?xIpT*`{6z9p|!~C zfPMT2D%=NunA-gtQ^n;%rh7AV^^64`RS+n!h4ol~Cy${N2b-P0V#K^=M0a-#UMQ!p z%punE%*MR-*~G52+-($(SP`}g-sf@nua}+vOBT^5=N_D4eB?t340V_)e6vl5cryZq zyB2tiLZLpYt=9|FfS2<}pJ83Ns^RfZ(ks*si1eKjc&q@75sm}|C=~Rg;Enb|E#A)3 zr~Bu~#nj#g^jnr>1DW(UxbZ}yAi92*)e}}5XVn7h+A@o#IORpN6!jXXt8R5;GR@C$ z1wx2s1;s<30^JA%HI!2MK1@8mszs69qcOpSOv8zow`U^Ui0wQM|VA38Gc+Ai-d z*)=)6u~vAL(r2CGqf~4m)+Z@x0Oe#lKuTQRTPb8P+0T@2tru>g8Rgs9Dvn^a7!%+(QIwSm}aejr%w>$-U@`NT6)|CoI`U#5Z zJyCacA_#5jz(_ESbS>AnN5&^pnT=f47LIZ8bYL1u1FssMM}!s>U?jFd1dZ-=Qaf)gotp5ofuxtq_=|9-?cqF(7X*g20%$eu`}u;1#N*&(y}BH}xRHRi23liH+m6WzIi#f63WEs-W`fwtjjE<2gR-bh}HNjNzDd&)kd@N-eV z;wj3Eqr~b?Nx2tG=}axu+R_v$IfWVfh;)>gmH_&*1`$O|B{KuEJ$T3}?xAv@8R{48 zJj48!$VDk;Tky(U%V@6kt>k>;3(R;Yj|N=;Msu3D+re(9j9akq2__3Idq_28gFm*2 z8&lZ(B6P5gIr&3rhK*V;thk`|iMbd#9*DDsr1TaP`__`3Zmn%zs?t8_vRsWqR>6&z zvKrhz4k}P3zBgTtNcKthCc0^sdA`}Ey~)^KK@L$E!;mV@bNFi@!=98FYie28z38s*yHY(UG z^w}-*J?akv>4r_|@@}LlUyH^Yhg6myh!%Eu%*Q4YRJt;v@2gE$Cum?Fw2+-D(xbBVQh{W)gfTsCL8Tu9{f|M`naN9U#vM#@E6f-CjwLlxwD4KP zYDu@|g)Qkxbr6T7W}DI|GIOz+9fhbnASubB*|sg6Se`T}6KTYhZC1A12{o9Ntq_bd znw8a4v;#fP7sw!%e@oJtfI{TZ0cyQXa@m_lbImTn`mkh&s6#z>9`qb@BN6s6`-X)M zln{g`)z4ApI+VA9zU6Ccg)x?Lt65p|6RC%C;!4E%Za1@}+XW>uml$sscyrxlmLSWK zV-l8{i`GY?vp}A@6W|2Fys{^puN6kd~RZe>cSYiHHn{@@&u*oyh&>5WztKmK8W$SStSJ(BMlFOWP z!m4aDrTGo-lUKp@;M4F@OvAzOm1cieF#d)}eF~kQv#)ff#%5di@f5aEU@!e2rrtZg zt@3UkzQj4N=|G`-cnA4SK{nAiL)rgvMeV`6v>w5EZaZ`5XvSbg|fpg zl#u`}rR;`MMoS4$D3lRqp^PSM+QNI?3eWrb{b5;>Wl86pdwj<=oVOtJ#7+$~Q{3*t z`cYikfiW0YyAFtUp(tu+jE@)6m+Z=ec~0*Sd7aov`!05^02+8ozk-~;JT{Vm*tpj35eZtySI6CJPi8Ufj;JZdWLIppuUm*SEGzy_MIB^gYCM4!&=!c|V;c4gRfPh* z93=zE<4vz81;IbYYHMn8$deE|SO&+*A-J?qzy zd|LxRs-T>9`W&&XMtH$k-7AbZynn9?APHPstzgAm$0^hrkNGm9mN|eWL#R+@vaJDe zsZN;f2tA{WPv}r(UxCYz?GI$oBv_FHr^NeX!x3oBXWP7CWaY5;L#5<)AN`qhGfo;Z z4B@r&V;SuO#qyN?i{({He)0}t@4Knc7l}92+pA&Vf&T3nCPrQCa6p?~Rv(Kt6H=Lp z&gP1<{tFpiX372}oC_e$+XLNRsHl0*o`LA_9vVUAA2e|<>x^!m@-4~#0MYIQ!00O> zc>@8vs+pC(i50nCI30NG$qAvWFh#ZrdoBnNVv^##*Hz@^Oc0vd><4(_pXAa`5POCo zJ{sIxlx5b1Va=D}>$74WH<2oL`X<5yCP%VIvg>P?_85)W-Q+VTAcrAX%t`SPD<%0B z2|el*g(oTS*YPVTA0RYP+=X;2HbAOlQ*%gFMdSiNEe7q$SIQ2GI&XI@jf*<;JGpZ% z)SiDwm63+R_-&lM%K(CdpHY5jeX*2)JohE!bfD`*So&weJjv*sT4%a1-;*--Vfn+$ zE}F&_OMa?HI=4FxLQ5vPaX$&;x(ySnWJrZ^$AXn&GL5H9U)bW~|L2eDJy{U3Fx}(h zCtObP=#FWgm@$3LY0=GDmezjM2@7aWnn{h#@vty{nmRbmow|y8^!PRK%e*9SrEn~7 zdyq7yMOCB+Xz6q@Elc-Q&~8$jF`$C^SfM8s*+%!r1z#Bp^!RNa<+N7kMD-GS3*!e$ z!FxDR+6dNeg2{iMzaiX>usxV0ypJ}bPJFa@#adXQ=`TYR72VY4)?Bi8(h#^AK>yD! zyis`W;gc6f!gb_#g5Q7=wt&+gy_nvUzgBE8;R2#OPW#e0(9FYh6zj%3q++LlV}w9A z6fdg|1pY*1)D_Powlhc>=t(G_%qg^wJ;uKv`UwvkM1NViIyYGX8JhHt?VZv_whnJO zrT+mWq1-a4dO|cz@t}HQ{sc~lpJ1~fB~uqUD;t|9F*1C+OWYNuG@y5nzg|LLcJlY3 z_TXXC-trefeZW+k7snOK7&>b85SeKT;E<7!h?rGKryV=zCV_bqlgo(Ns$#NZWx$12^XtQV@HElm}iRgg$CQ`@UbS5s`Y*)*NW47m|B( z#Am)et7=`MnBNdymoQ+T6t)m#N!6AI6mLq(>O>KFH>m0@zoHSoy3Si8zu@B zem#O){navl9P%As!M+Kwz9i+gUis;1yu-8~WleF!W6@9Oj;0p0GWDH*?RQ6?mF4gv z#BjlmpB>O9(cvA#|#wiQmOJN7X7alVf zi#G3z^F~oH*$E~0+?M6y`6a(1-;lVtssOJ85x+C;%|vkubtk2V_%@+d3pJ+GsvP4T zgntatB=DzIc7!-1r!(nRkcFAKl>O2J!JY^PL!{NB=2`5?Q(1K+eF~`zd;ewX@Hguj z#ng!?b?7{3oSa8`@o6zj>IF&jzCl_QJOg2IZVHI47Fl{k#;gqKV1inFQo zk}MyU?nVw^?7-L5(+7zSBBb0*)qRxT#o5E8CSv?`y8JM~o{E+4hHP0J$Q0-ZTv0*& zP``r^m2QWA)SdE>A2@~9z@>|*QRS%E%Uu;W$h=fhwle7$aqM`AGH3!nP29r;!ks06 zNwtfu5`3S?FFBoBs9=a{7W(TJeip?RpJ#=2QlZyP3MQJb=?ePw^`DdI;;j1>)$HqE zRH?g=)oP{d+qDkN=JcT8qZtF&pK2e-=llqzN!dQJmPyUk9ipC9=NQi-RVR~oaCT13 z^t7>;vHy@BjN69JnsmsA@jIEOs&lf8()px7e=jYjR>AphVPLP`Ta__aTzE|_YnR(&6!8(l~(#-le~+_8s}Fc7tVVJGUOPf zz@mX|AM_p~+D%0I1sa5;R5wuyBK{i^pCQ}hAqTsQD$gSx9TT@Jf`@r#o^~IzDO0(#B2zjdSXKb0QCTFwXunWX zrdFmdXV`UOUVHinD=+5sT!3NY@5{ZPSw1b_lliIU*uk&B=k~8vC&7GaP?+cXhQOPjE1IJ{3d}8@&hV9SEW}<_Du@O z{f)#vCeDiA61r#`dpAef%>yG*@3knHIP6;FpXX21?*N3f#? z`2|`CZ_rZje@XttXR81ohr(s-f*RMdoVombc1(&71)+2Ee-bXi`SB_JPI;9WXu(4S zG)q5<8(&-5EAk$+XN+y=s=ks@RPEBF4%G~psT=!PwJ&%H+_WTz@4B2eG=J7sT7hrb z<>1RG)HWPxXJLKBxKAO{ItzQ7wgNlw#cE{(t&RKom2R=SL=<+dZFwjN|2W_r^Ec2i^R~LrhTY|0fdO=m2b=NLZA%=&3QtO4klnSVyLg|@UP$F%udoFfHYuG+Wjn|FeQj~>}X`)-+ac2u#M(mqkFSp%9Ow|$Jf6dNF zItLHT<&m2RE!d8m#azM=eWlQ`WXeqMmiS?{ZzIMTV4s%>F`t##tV1u;=_%u7T3J&g ztvyar*G1_O9X`RiM;-bsVhTzQ9Uucs=z9Z$vSLkPlkCAY2 zV?08XFl>q3c1j;prPtlXslVnv?;RB#rX+YwY2O+myL_{W?Np*_{RA@8kCJ0@F;dF*QoJo1_q&D>#f)Hz{mzr_Pk4bwQ zq2qvnz|*itF_qg*A2ECAYKQs;W9!*XQDdE`ERXp@CT@?;Ak!1ZD{Q7d{tw=rGB(Dq zqJ`r}xnq2j+8FVUTZ_tvUSnEyXuN?J4jm=7J*;G=K6ag1&|}B+@g8D(fl@6p%?1n$ z5=;3w!4W}KA8Z{Xg}r&r)#0YJo_Zv?T7*c$W0bY#@h(Y--sHc0HmyKl-@#rM2BFFs7HsrGr&SRgW0O69ipJslar{` z9bxiut{tbgNB9akS5_}3wg`9E`-0CA_!1GfdP^=H(UhR)oWdt5TDU{tBha@lq)tf+?YKeQ1Q&0zxGfCwx*Z`Z-b| z?tBhPmOx-dya|livFVc5qL z$*` z5(3Dz%KM4B3P!}0VGJfnx#fG+T(yPRzGrz_2q^hMTMt;RTZy)TLU=QE%ikpsd|gs| zP0g^I(8na~#MhHzh#=Qfh_}p@=~XbykV*N-8krX0W<9Q;G47-ffH=ZUjxJ=c`jfUM zJlVyh`<**=9Bw-7g^6X-XsNWdSQ)*>u02lKH0LfcXGM_QG?yRDEAVf3HsX~QTe0y{ zX}{P*<7eeEn3W!PMw3{5oRo=i{yhHdSf7ISmu`N1(K)olwAa+lF z+tP^lPc_b!j}iou`LQdCs~Uno*uUf1fUM>;luR)kVw1+&1gGwvZen}MEiDmRC)Mmg zN>o(@E!1~;j*30S5^4Zy>0gR2Er8&%!#;uO~2aW52Fx z$^cHmbzks$!gMF}FN>lXDs83(>CKeZPODAC5^%<%evFfG8qe|@qvMok1vN?ff%4x<(idFIDP#@#jTQ0X&9oCIFnS(s6Qra9 zEM+$Gb#4}Tiz*VzaX4L~NOjB!1^uj*JH6zy=*aArg`68`T3#wcK*2qU0x zH<7FF2EHOIW!Or0=u2iG5{6KT0i2X_JEDams0kc z`&402lsHmo8l|=)g(Va%kY||O#o6nHF5f;${zsvFiDV0K?oKQ#ZUTR5y)=dafHW#$ zDayTHQcfqd*YnZ;qxw2#B#u3x9DL9<{)STS-OjbUeZi>shjBg1W!biybET52JGd+Q zIx&AFRqN67;C|Ofutz9aO&*rBnw7Cd8?gF-lW;%u+zOB0F=b0t@JYN3sNVTP+$@@ z^Md5rR%SBs3(MV8|2EmXixl}GQ+ehi*<75dsNm)b?xWhlp5mpe$fa35(m+6uouk_I z4|`bkw893(EJM-TQetBYbHwi8=5Na7xuvGQ3hTcVbn)|J|3gRbRzl(P*jXJwCFD$V ze(b}U+T9DJP1S&1Ae4=G+iGbKQPvRWrZwoLH?x<`WvTa6`BiH9pL(34tufY#p_e7n zfT-}0RTy2rD+Bd^%#4a?I$QrlS|`9f7>QYPyCf(qi$#tD?Mvw!l1gvx5|K(U0;_;j z-Fni66O}5<0e4&lj5CT{9WF-N@V%HwDIiVb2_tDDyTm)7W|F!{mN%1HBeJU0xj2t9 zGc8Tu`RJ3AD5v9rFy}IO8I(n9O?Pmlbgbu;y4Zusu;0P75UG#`|-L+R(bL99IYTNY8St) zhPl$l&GKSG2UrW%!2{c0M0l4~7Az3NVR9+7uv8A5sW{63R&YK#9MsySIjNwAC_ zWcGnM)Rp`(*P*YhaD^_{=Y>G79rLR{O0;DsM@)Qm&RndO$q_k<9vs%#3epT~lL(QJ z7obKMZzPt@h-n}YB-Tr8A9xKaJZT?4m9F(y4-8Il`+z_24TBpamK=B>uK$TDRYBOt zNxDk1?LqrG6#p~jyJ(FOUo8RPEh*b(IpV({-C%>ppQmmtA%}p26W;*1&8a^VXTy8e zoWFv%yVC*Yv%`P3On(F6rD?D};`g`aR9m}6+WNAGZ=lL&5{C#t67`8=#8Xpqm!kY| zdx>$>O)u%^_S`NVxz}nOsA~6}v_wTraWXMQs^GQ2*k72v;x0nJ_hM90+e{g%Wl2)) zF^;H~mplP~bK_7<0djjaqwfxYh>d2?71vh9>VmN~*_Of?s`9z2?Bno|sSWvhI3$*B zD`8Z&fSB7g+Z9x?FWV|hud7b)n94poSk(}|xm`IxTXm1eLpLFq!knHSvd`@!^?H&n zXRVFF!Bc`AqHnXk;y)Zho3Zr#v&)Ry`Zlb@Y_PS*`rc=q!8U+pXyjp%Al@8WvQOLY~ zTT2(}^lX`LC)JI?PZiT1bCyKYr%6i*@NhQy?~?4~>orRu7HHhaEu+{; z5M4ks#0B)RT%r|*eo2^P$P%Pgdi)%^NQ52tV_^+wY=Oldzg=6NMgBcjYsV5NhcqN0 z>3UUqIR_{m_S8yO;H)NomDGiWVYrK0#>@`iKWSy4X@J@O&SdA*S(02MEA6s#{#u0I zRNAe`BRr8a~57_%CY3iHeC^Ke>Fuu?jw2`9lI{l-CUWCez7O8;6 zU?ey(-TJYKo+Yml`U4ryKBw<$4gv1pcud+!@A!lyIihHQfdGOU_p)dQkVMDWPKC7CJA#$(ZU-(B9G#VRPP%(u7boVKa%n)-m z>}TBXWewDF34}Y6dzlONv;cVe2o#U;RnrYv&-D4Vw8G72lZzY0apHtNC+A2blBHGM z(kRptLoii|wppI%7->cX0GkiW`6bo#8jZKF(Y`CEPv+82xzb3rE7B^(k-o;^h~xvI zCPWi(Zckner7`qq{!tCAcWRXMN6@zHK4q5Ow>KdFVGe@DG7IJ{%H?$+FkJSlT@Rym)%pj!chH$SH@Ven+ z4z7`NYi2c0ipou;iSFQ8Sr2as0KYBJ__qo1vrh20qhCuy0$Vwi@mZz4GCze^2U;4q zeJeDw-hlEXK%1L~y*-@nV`db77Fa?^WnW`w8Oh)#qo=du8@Gt6(^>su0kahP#W8)^5KcEbAwGSnFa+1%rFBO7r2^$Q#IjJaNRstJ z6Msr$7S+{Shyon67Bo;eH@Wh)-KbeZ+|@(X_aSM({;*nP8Cd=y4m-4t!aT^E7&hYj z>I&1HY|IB)SX_xeQZrDX`6lqQuv_?gr$H)-o@S-<*#1f51SqI!n6VYItN9J0G?GJ@ zoLH zmBltAvJC7;2#Ka7mff{fdCjDJ+aWtG>tB8*vqVK#O?pD*|rha0Y+Hz^;%99 z)h2SB=`A_ZUwL_YIedxQ|9~WjSJkb5vW}ixs3mPm5B%X89*>KH11=3xIq&a1T$SAZ zZ6x>~2(;{9+4xoGPz(*(R*H z{1hwt2V0SIM7)cASFk=@yq-wgVs?i_mpJ=`WbDR#^3MOOh-gISH&Ac>JivjVqosKX z-IIGEWNSs)H8NXr8P0Z3T7Sg`!pbiisELg)&SvM{^YiS<{AN@Ryg!;~9BU9nv`llw ze;t?T8WXMYODHt}<`OLSBhh!rHjfz?0>}z@%!`pK#2*H6(uYw(z|Wkds0{{A?!0U26@885xrw$4(}w5 z=*^x^@>`!H_MOi`iy4$OTY=)G7hGq85{O)!Iu3?Y=SY1&KFWdu4#d^5@v zt5?kbMLfen4`8N-szaEH&B6Ah-pKhvLZqM@46 z*FZ%f^C5Mv8YZG-?%CwK&SsCfPB@8PX*)D&t@tvx z_Ubfv!qLv?cUY*g?iXE>K2dj~pLLSJJO$qycb=Le$9{jGscZ|t_>Fp#_o#Z-VX zjzULg&>Njmtky8b9-XaxxkQTQt2u?9P?n zmRH8H6bsgIe0m{!ip|aL1y4yKr)`21LN@?!5h}}!Nga!7~qFu}ihU@2IhX)FzErEFL`_WJUTV%yNaoPE~RWRaQWtJ&s0V@Tef}VQ!1b zsBe$0+ssdij*CFFA-~wGV5NHX0nqim7x%a35mho%JIbm=1fAu>% zL0MY9T%;hcpF|%tK@X{rys%R|M&sNU1P*>TH~m2fHS!-!YLkKhX(KVsC9NnpbdtVo zxc(sx!1@F2y*k1#z~FVMz3c-Rd0^yq2^~LN%P(`?`j13Gy|fRI0G9;;bPc212z`bx z&&Gu-H>$GMH^r#C@s1y}hk|aU`=* zCbf$f%_q`1PX4qiom{?Dyz*4ywp~Z0^#fb*iXMQlZUi>#lkyatWe-t6*vBgCPuhJoHsn*QvLg1(A0BC!ul zn_>R5VIX`Tb$R{dc^wr25XR$UQ@&s|k>AI7s@nosR(vE83&XC=kDaYnJGc`HXkJJs zpxwT*w|Zx+uH^Kg6ZozDB)>d<5izZZLrpmHt@F~z3G;+dq{rh*{oX}?)=Xc@i)M)~ zshQ>`;4CcU2K4X*#RVnXWza&uqzou-S4A5DQ_H}{JQL$P08xh2QyPB@qeW!kG0mPj z=jS2?KcjM5>WoTvDq%9;P1u(*e@M#eb5vz~{Y6bEwG|Ne)c>D)pGWw6rG-=Zauc-G zoB1F@B0wNMzmNTB9{ovR->pS~UxR%D7MGtf>}o=h)}s>0JaR3#N7ZlyBsE+bjDmU6`n1kC&{!EMNL&YU9}x?@}kU&3M8h_$aI2S zUeO{(Gfw1p#I{f=kmhi=C&W$j0a>NCH43AT80wpd`V{(eX37-O)25`0RJKUvLDLWn z2atUaFn$|dku@url9jK@3)0t50w7g26)S;g@?)jDMC2==!Mq2V5)PC?>xKYgFl>p8vC%obK?Hp=eE!yMu_ zm?R&iaKA1Ay<&t8P%m(sM1md8Ryxy@zLf?V{cXRgERT&5X!;Uqgt>=)C=S7RU_Oa9 zH;beKUD#xiy<&5bIWkSJsj2nA;!fUX>cG|-a?nmhQ2x1Oe$rSMzo^fKLV{p?6p@m; z2bw^uTuovPqW0rC?ON&ICTjNcr3W?u)ezC~{T5NvaZNcLC@{P@y^a$0 zo-}iE@?OFppgHdWu1y74|1Z=T|2GhvZh>w~gVHIbAw)!Gdg6zPEHhh&hg2Eyw>dI~ z0#sYd_4$M>7sn9a#@VCPCNrNqF0*p{wAkaT30_wj_noS^D&7)*RHc{9_Vo`|NeyHW zUkZ%p%Ykc7wlmm~UPLW_Txe>@;Rl7@aceP~tMY$)>-BUuETUY*wg6FHB@oXGa$5na zC~vBdqvx+E^@Kj@rmxNw(A(`qHbGhzTIlbQ^mmVru3Eo{Q?W`U;(X8JhOh}DLoSL`M zN+Wgwwa4Pqv~vP}NQdv7!5^cp&^1b5$Tt}GUOz2AxoxKOuI+^U(WleCLp(T%ngbFF zNNytksXI_7-&|HORqFFhrT^rT&t#)Q?R!pgd@iF!l)X-1NaJaEn@E4+(hp_mP4Nbq z{YmRd0l9RAjV_dPmy+Cp>(wTz)=;*Z22c(G@ir!-_iQ7M#g{FRPIu2m4I<)}7US2Z zdBDn$Jr#i&;&Wz&n0!e+6%uAR{HlDLjh3LKsWjNbZ(3Il!p3F8YgAfMx=Uy=U^hyy zsFg>!Gh74ZI?zG?Wr0Rp4Vi94x>Sa+2dM3RcKT<|R+81Co~pk^P>SfF@+zX06Zr>J zT=A)vi}-zn{VkxDHvC9LEi@iZiqjIr?FiEegwLfBC^ zyNW8~MDCfUT&}nqFITd3fHzC*v3>v;Q6-pVYsD)47lt-p9^I$MVA=-MXLyLgu+S#6fHLE`jNn)<%@_?QB8Lj5Z+T!xn7M=rw z+5l+_W{sLdh{)9sOe$K?R32;JVgNYvyXn|VLx7482ivdubgIj zmbO3xqB5UeOyK8)1t)nK$-q=#9R^&eZ61cy)K2PNu#-BIx2^0HHNr-pV%6wlPApQ% zII&LG=s02RgI@vCibj^e=JzMJh^|TzbtKP$_zY1__Sn~+Mzoh1-3j@%HS+tgPi!cR z4ZCuVbLq4#vR&DX2FnXows9`1c zphEnheh~X0oq*(;aQ|9%?H#-f=X$@Vj1 zwpaI2=%~SsC|oQ-+O^R_Y6C4+vI3#zx}n#2zORtvH(kgFV+mAQC+;b(42350lrP)( zDT+O0HA#yl%-x>J$?E8WFtFJ0T~RfA>|@rK)r0#kPdc8%)na)Xp}%JQiClZ=IWmL+ z1aU%qT8jm_UK^6x*(JHJBiyj+HG!~4{Tj)~=|)#YBouqb!EPlUBX&M&U1X)uJoYrh z+AQ!IQJ!&zn5re*#az6Fgh= z3<5{~kiR_nke}{O`UFcJO^da3iIx32Dft4n)uJApIj5Axa}WQ-rCFXzWxB3+B052Q^{x|9^L$VnZ_%*6FX1h(Pn2wxZU1l&9y?t=}->ZsZ#x11vZCwSnR$7)8(xc zC{4MO@!h081os`Lds8F_jN{ZQGLMNEz0|M|a{wenZ0y7<^ ze1Lku>s(rj)sg0$FVkn$Zjp*FQ|Tf33kzm%T_lr9#}UEDFum&%vE+u#MpPZXFL!mI zdpA__cpN+`UkKqo!U3k!U4_z-uga4jNE#ijKA1qXsjm_@NO9b$8~V2k=Ru4obkD;s z5<$x*qxb$9tYFVf``8;A+Ib8?8@eQ$m&pL+M7Me786Qn1}=*Co9gz`&> zJMJWr6MpPZ=l5mj6GGkti#x^4!qEJYDwj&r#c{Lysbd0L%1I{VG~X^hmLoI?qd=2l zNyLqCtQ`^^8&pF^46YOnE?3?WHJ&XY0myVTw?%@r@?s;4}0&qrZCMu2MPm$tj zwi=`c(6XF~)M5@1u2o=Rc%ifAA}g|PU3apLKqhT5hTm+FtE96aw~7zi61o*D@UZvn zmHdT@(Lu3pj~K;{&Y3gEjgq1ce1vbsUFk?o&CHoYMzOaM9}p+nFQbrj0l;u~Xc(%Y zc&FO=$7FFbpl-Sa&UHe9YZd@W2@Rz)D<5O_=n$(+Os&pm=g<{XI1mFO4-FNh2tV|% zq+{ohG4VOw9~NPbrHx;>JrMcrEN{9+;shhoNE{HhDZZ!8{a0C!!4n2!YM{I)|vpBzroK#$}+o9?s z?}Z*Zw0+Wb>n~P4^OGkz%M&Xij6cW)PzZb5^PTiSu8Os*q#OY>QZ!Rur6nj=CXLfP zuZn&PYBX4Se6iu<$|`Z!f4EM1M&r*~x_Wy$x^J)Q5;Y488SGrzy>=joh~o;vJu(7b zVIDZ5#Ra5eU;tIa=G&-szl%frplKSZ^0)pG!7`zzRV@_9xY-p!*%ExcQ;S`h+s(>( zYW~Cr4E1aJsffX+PxZQiUEQvhM4e_wvcAk2dNG%#?JS>KZL_|Tal%$$eMf^rPuJjt z&5L@Q3q$NB2fc`?(@`ipNn3~JS>}2ReLN>WcEC0tV7JBB+H7SA#W#iNC8~NkE6%*m z=E4aJ8lv#*`1S$dlBe4_2ibF>by?q<|irxE!S6PvC|cR<*Z zKr%;co$>42ZC?-*1u1qOfNHJ57e$;`z`D~3Z<3VoFPYNLEIKJ)*Fv4u{=iPAJ%J{& zANq-xFvrL>OsgU@{Ebz_v89S=JE`gBI(d@PcVul_fmTf5N4Q`-t1fm%9+<l`~^hHRjw|EYBp7RTz2kMA!tttFbBs_(xjzt^#C2 zWJ}>~uaF>`{(mmn--Zf#7R4sR>Ma3q!73y21fAK~a2{+Inc5`PClZeanEZMpeatQ! zDvg?U>sgZDj{@qd$;mZ;`gSFew^I#L}=a6b=q1{#O>dt&@-uaFKHba_MS#=^XdIK-#Co8 zUn*?gM6h2vS`KqIg1*w5RMDu^1M7+YU-O7qxlrh%)4lyYR(dSY(cgHMs{VUsi_Jv=K5Pmk!q5lwGw=i9LC3wCxlOAzux!v8S+!a5qpX#Jto_; zT{+Ut1>Z5T2!mBLg7w1}>>Kj-7ZFoP_BI8_sPY}L_-(7j`EK|HlP;p#i`4o@4MM=9 z&B29HZ$N*QrE$J+w<$+xTxxU4kYAiZuLdWReK?%+d4BpYnwl!q&f{oXE||~5fWB-X zQaIobo=(`afk1E-sO!cmw9GeizMZKGZCqufe@~=S%<@X2n#{CbMdw_TyG=}%?Z#nA z$ogB#HLhx}HBt24%g`5u7As66&oDu8LYr%{Y%fu+AoM2Hb_EI3zYyCIm4VddyPKnE znIPj&Et4wW+Dp z?4tFf=nmB3?_TOl_Lr=*4UP9g1i2RSaI(F*D=dVDb?aX@DpsZiuIyE8h2E8p7C zOKKR!fLb@?jeedB`@@NTBf3LkOBJ@77gd~Os&58VNMnal&0xh%fE+&uQ)$@SMvdqT zIV(i4+kY9)Db0OJB+$bN6lq~=?7LQ>cVw+7;}3DEB(Do2E&_TKPLuSID;`@uE+;Hka;pI=0pr3Nsb$c$2F2<^|XUV*mqRz-X)_ z=d4`i5!tzZC+Ql*rm#16+=~I%sK=~k^z-J~34#+tBdJ!iY?0}b!s)TPlmA|&r_W^6Y}zXr-l!!H)ji?6yX*f<6=1?H zyFfPh4}r8_5-UJbNGzc#Im={k$w)}4x3=lF*GQ$KX(7*#q{+#m&>7sJu-;;2m4(fB zNH-IwZ@8HL*I|CVgRi9hQ0@y(yiib(TtMjcmIY!F&Nr>Z0v#GWui25#$X}3wOgxi_ z4WN6G6^Q(7!M$UhJ61+EEP)NM*F%o6ta6#x1Dti_@Tk~In*8v!%E ze>s>{#{PEGxOf_a>*T`!NZx^XCbne+E@lHhu_)pFw=TRAp0lz zRumjU96unQu9#EtJk+<)Udk5b1uz0Nw!;`-Kr4tq#E8^L^i(M_AbgJpzM{)h#;Rd& z9yy+)4{Q{ybBqu{sqm&}f{rbonVcLLc)rmAS?8>UY7}8qbarZyd^8GqS_~DmZQ$(* zPfPU+^#4egs8K8@>|I)k|0K)tXkY*c6FZrW!{|gwA(rSM0oh)m7aM#z29bZTrYm5y(SC+CPK?Mt1++)dOX0Any2Qgve^NTOoUaPrTRmR1Z- zO}zu@s@p05gle~xIwNxk`!(IWC6+9JU*lXQrCH^*AR6Kpr3#Xf=+0~;qw22|48+kyk2}58-?#90Nu>7Ri_PWhn9gIxN zb3vw|Jp37v%Y$zasnGX?qdw63xax5yAHoHyX_Bdn?LflF*_|iZ)~gl#Q*B!f5FbDf zT|%ihCj*bmI51N)b8OqID7~AgXghsULH8`Qf+F=Awa4v68&0544y)`HD3N0z7H{;z zl+@F3m&9DNt?!a>lW*dAm7DvmQ~s6!R;={jRQjDJZK+h>D5FJX^+x(zqN!+}loWe9 zho6&Pz;xW2(2){tK)&#yB=?mAWc)Dc#WV34X61h{_-f_3w{}Yfxz^P}0!yumB<9og zetmnw`*I$U?;z^q!i-2}_mJsZ6VKZ~2%O;$e8#kWY+B>9W@pnmX6=AkJ=bi4kkc~S zu>lL(vb(f(BFNiHq`$k_gO;r|qQYBKb2_7oD39>42f$jYpeZ5)G;b zDLIo{n0cFj#{jomzcoGKd6n#E;@FL^wG)iS@NB zv3G+bc@KnQVJL8VjVp6f0XkY1;t90e%^@Q->YD(p*vM?>^j%Ya;zEv+o-Z|+?%I)v z>Fe{Om~gSovX@ltSw~U07|sl$dx|+2*)gCGBvEw3FDSggkv5V92`-o_x~UU?M5Ij* zE8dAH>ROl5=3ygo5vSKl{6f-%yz+hNtYNX_E!~#fM_|a?hn9vhVHhR-Ec0i@@4E(${cZjk)0)jz2H=n=Td>90TP4b(QYW znmj&4q<=B~n>_htDxFM#W9|@%+_43y-eUjpp=jAg%BD()bbhe}KST(GH4|P+%BPme zxveNgSX%S-h*an;B&dzDxLlfi+&0tSzDdS6%(Wes-SWpJv{#uK@PAuk>a(kty6GOs z{J7ki+wFrGQJI=RO}(_&X-__5XX{~(w+Y+e0buj%@i(k3XoepVt>g4iz{t^8x(JRHjD2{Km?j z%F;Cf&cVah`EI&X<2S>_!l3^&b&6PIrqhW?VZNT^DQe}^q13Y%Uv=%P^tdj<;Xn1rQKZGnQyO9R0zP^ z{z}ZegVBd2TfZxJl?amcs|tqz)PM=-LzK31gurW-pTt1z^ZiZ%l%xP^c>{{=^Z?g- z)HX!Ri_q7EWl9H5-AD(Onk@W^QfYm4N%JKn_YetV>fa@!%~SIxX+w?K1=kFQQm=h# zS0S4&t56Bu_IEcj)*JH{LFLKl%%`7&nr}2LklA)9MOPr&M&x~r9zyW~F(ViPMr6HN zx~94ya!-Nvm6HF^=HRvjR^n`Ki6p$~e!eQ}f>S9FW{4Mz^eV{t1Lgi?iF8@Dc8F4k z)ds!GBJUE_o{r3s8Z>HhV{Syog=9{w#sD^Xg0r2xaO^@NpHUlz!eULnl*LQG0QYZh z^I`SetYn0%M0|Gi9p_tdgavSQi9%>bANxJgR!-qyQg2reBK%kz^}e_^h*-U%Yzij{ zS#{(Zri{@hyySD+#dw`4AFNwRtNwQMQM$8H)5YzzgV2XKLqqg&ith)QkU# zT}bTR7YaF9J-yjdmg!Z?n;j);{?gfm9+M=rz#mY+I-{#h0AvoT(ub1m4#ia49K4#0 z>hT(ZR65_Mx9hhls*jlFa9bx~UP<^h#9*FrQO)}bf`@0%qLy5uUkAo977Hs#V* zh|Q(*py+fqUieas~hZ9*K;N zK@#Ugn+g$nL@4g*vl>p4mDw7dnM+3-)hCJR=hSv1j3@m!QLTwo1sdm3J?;~uJPLChJfAno|l7eJn@V-|@b7Kbe3LTR4(UKf}wzaqY-U1$~mXQu-*fr%o=`6? z@^YL;0JNt*CZ4JI6VwvV;~takQAX|aZ?@ZDkO*j^?YE5PewIVuA@UQ9sit16_w654 znWq?qPK}e)`C(1Zr)0W=N@<%l^)7E?*zfNt@YDqM*;#20zg|Z4WG8(>_}}yD#Eh`F z6^ib&8udjl6qqg|(jcQZz>bpAnS}mZvb{!^+H{8^t+dZHVim3QdBx>deOt4InKs>J znvIsnGJ88t^Bg;nKt~h^W|$q&fTd-b3phADwwv=L`|?JanKX4p8|}rSY4q;2Xu~cL z)}{R9nHv3+I0Ju^*r(`D4N5F`xYYVc4$H}%nKn)Wioq(Ml2aW@ugob(KV>yp%GWmq zu?o#4jQ>2(ncON#`Lf-2MLs3oKGfOMp+jc&NHH8!VmkGwjowckw9U>`2l^a;a??}K zZNynVoCK2W``M#|z;$f~7=IgY=LqBDv4WD36?Z0#3FMZq&p2yt zDlMwp!J~?Ds*EVnyZEXle(X5I)j*#5LTB>)imAt0i4D5Aa1#t5@Z3>=g>p(ij}EVh zZ$R^OP|(PF2puGJzp^V0+-nSd%^76=)Rf-I=d>@5j7%%xE^f!%5EbAQ)}XJ&&q8r$RNO3Zk{iYEHR5L7Y~t+G;7Qm< zj!QAo%z@=S2n^$fzaFUA@iF3zYh=Y5JQuMNXG$aYj)@mU#ZAINoZ}i991;(PbBHT+ z25qSr8TZg{DAcM^(?+D(5FV|WH5$C~=r4u=8x9KL*6;KDkXasAM%M}x(+1yjozq8a z_HJxM0dB^d$I4>Hx>#skZ1!xh^*r8nv6{wq_+7#49+BC)m=S0R_?w!r^Fl>%>JV$j z?|B5y9dH>OPt~m$k%v|AiD|ic+oXYvPG4I*w|e7veb+ID8YHx@@Gin>6pFSBRs@eT z@MP+uXpO}Z%YZWk=4M^4^a?v}#miPex=RP7XNUMbiVs8-jXFeRlpP~FB&~3af@Xt* zocHxKzL7xL^*;>%HwsKNsuXMSDcJjaA!j~xTb_HvLGWvYjCV_CQ6GfNe7^hmivSKH zP(VNnqYU40;_@1Z)NLP$r(+eT_aP?Fc*hF*5aWb9*2BR&xKty%v%+SeKR&X+9{&R! zo9KsRfG2%BS((v6WNfIVwPou?^*Q;^d)P~8f_b)%V=otpEymUGn@x}*v-Z$dsY9HD zcgPoJ5q9BIh43Ae|Ab`Aj_&vvXn=npftrYKUy-BXDG7=i&ondr64$J1_M1k8X({}B zLpXIhbA_j&BoqiX1hb5EA$@{=W2UE$&Qf+o5zm5ClvqWZ;G;OeEKt$?beI;)tdw;} z!NZ(ly9AIJZ@3gtf$vFm%JUJqZnZe31W1sd679$&!aQ+CMX=ZO3Yb@ssff|%k{S^e zUY-?K?n_*v9m@;%B>;=+OJupWKPjWb4HDqusko7va#VOc{;TrcflKDPrp~7Ae&58~ zjDAL?Ky}kpB=|d%J^ffg2=AFHDh^gwcQrOO`x^4UVojpbf(s<5DFR9Dc__kS>aLa` zMg^#^eY~nS@x98?lBfs-feTb%B}MqGm(!;+{<-q4C4^Zg5NnX=6~cojM1zfm zL*Dhl;1hzqgCk9k>(z-j$T7=k0+UtT78I<#e1`yfkADqUHk`&J1`f;y8 z%=AMyZNZi@br~#ubt?>%Lfw*b8A_dBN>cRUqry)cQW+4dfMf=|eM(TdUx%jekl}K} zmxot>MNw%V0zsg)t+@$uA%v;`ox%R;`$@7#fw zJRXmYj{)907TYm@3*3yd{vz8VahUCGz^ zhJp#KpG%O;;7ErY^877^H}WOzh=gO~pCEJaMt;sjCKU;13L%P|#fEM%M&bFcZ}CBF z58aIkoY%--f0t9D^ujxP(Qzv0+=T>TGY#ON@1kEa95<$Xb_Ec{W>4Z7Eig5;=q3>e zsjJlLt>dOLo@{c5UNgHg$L7C_UOgrNO27E!_iLoK+4p8yN0vZgs?5}V>B@Pc!k+9d z?}5AA*sB$wsQG>@w_VzpTNt0+xfhcqo^`Q##x3gPu%hvHW-2j)O_d&hewBN|hFt7! zxy@!ilR@r9zLH4422>JyXhzbm&@~RlQ)wHhtmS)2cLN%arfKe}j|&`iXbl#ll<5+k zhN-DCQsZejlhZFKZ5sQ6W46LEZb1TS6UJ#6JF&XQm?t*$m|~wKYS##^g`Gt3Cyc)r zwQi8Yw@e4-{X7qSLp_jfK^@ z>VtWxf;vLmE$Unw{gdLzY|R1O@TfEz65z{O{?*j{xO@O#ZxpRGT=AE*+<$Yqh*N{u z>=LCfl1`W-__kRNZHHu}J`XX(LgU*#QlI9~4^@Xp4TNo6y3NEItY50wTCpoFNEp~~ zvGI!i7mHK1m6ckLWNMICdoy6opw<9e7oir4mNtL`iRFyG+$cyp4X^ToSOs}kNPa>* zp$5@o!|e1N=8Vr_UudY9N5me&X0x?`8`Kz`~CkvpGa0T?>P58?|I(meI8ulwQ|vLhFY=ZQzW%P+JaVZ zO?%PnTC%E@in?2AZMT1atorcFLcw!xhrbo^?!bZjNia*GSGdY3JtFKwom<@vn5-Sn z4YAthmR3(Q%E=w|=a=j?6R@u}^Av$iRo1!2YE<1!(&u_he1IKH`A2*sSigu6p^2NV7sFz^j0OkF*=c-OzML3NgV zOssO!7^@-ww%EVS*7e`l-Q4NTkD-LWJ+9Rb=%nXItCo4M)2S9S$R&1*(Xp=ap!gid zkneA@>hGrREl5E=SDRxtM`kM~MsRRqRF?CcvkY(fN>cj?L21uzWZAKtc)hw8wY<3< zlKLa-O%s}7(6{bimHguZS9G=#$ji6wcK#5=k%L_3+qPXt{7eunGxkGA3M)|ZP z=H*{9&z`LF(i)p@f=&2bn^5{vt4h1Giu!#tT;djepK1QW>|O7Il33obs;;f6={PI$ zY82BynI3eO4W&Gd3V+Nu=6%E_A7}N=-Wyq;h}gd@fTq9lDZ32^YjY5_9M@bswQkNj z&o2MSn1k0=HXai39_M#G4m=&YBEcj#r)+5+jOR#73y z<<3BVADJ$d3RQ1unsHt-QnnlCW(0ku(!#F2(l+-Fm$xY`EjlAD<&Y(9LGrL)GH!yMRx{86-<|4{&-^(fpKc^CIwO zT*sJsA$9UJq%o zjKPEm8qxEr*Y3p_xK>jVzZOwdD@lJ+igL5O591@cS>hiTCN|(uWPIz%E?r%1u8lwW zFR9Cko@c>tY)0(B^szN|kLEGa54;x313ckgHNrtwMh~5(MNN)&*LN1FzhRdzEa9(A zUO6E?Cn#r+mpcylkDVzR(cJ5@XRL%E!s_g?KS&|PTyZ65OJrdIa)V9#FgZ}=r=qn3 zrlLrk@#71Zt__7NAjoehwH@Cqb4>8PFx^C$?BimUk!e8W=x%O+h z>Q-jn_%5j@Ezr+G1gSQv&k-oaT7XyQA6Ow+&d~tSmb#0fkx~BykkgqLK+2{n@l8T~2it-Ous9EP8 z_vc)7Ug?`#MGzLng&Z#B%~Z5i)Z36MUkWV(;a0`xRaiE_ zZO1JfcRa{NM%)PPtv`kGS!|I;9cUf_T@Z@AE1-Cp%H_6W3L6x^wO`!zWStLJSO*&l zE!)#Y*6mfwBj`QJVw5Mc?)U=)*@ny7G~jAIh?XQ29v&pNSFxww`I{Uwu*-G)F^IeB zT3}Cp5B$Z#_-(-h)htx+hIulu%BQv+#;kyLIf#3Ip&ib12m4C)LyJcId;jaZ;FFw! z<#1I)coAJ8ScXUka6pT!p6^1?lWa+-Ffq3u*e9sZcx3NI2+P0>f3nQWMf#-X{CzQZb6EBVe9>@FDu z%AX5eyhpL_9QTK7Yr_pwYVGjOL->htQB?nTPgOMWrEzJ}Uqq8QVpIO~o~_a$$dpfh zn`DXgAkm&sF@>?Vu1TWF;GvS|r>HVa866eDwCcjbt z7U3H+>2go&Kw+tqFG3#^lw+OAcXqA;Q8W40&TB9qjOK+Lpq%^XooL-RC!i^Y*E%3^ zLH6lpC`NLniLBF21fk0#v;J^H2*^$*vWp+Yd_F4*fIkBbCX4YKA{2U(1O3h2u~;L$ z1;_?VFbqMh_re(T(Lc)O)F0)xcrMprnZVV$-S3&T74Lst zF891H8|0r4nO>8rU(oOVAXl1BZzI!_r0&@6Z+VD{dmf^LtzfBPkhMGp_370Sr%%${ ztX!9dfSEs=^<}0sCa#vA+wR9IOn^rS2th!i?rj1}i7YTcyIOjBaZ&U_Ju4>mEx)u& zYr!>$)Ha+!{ykSLAK2379*Gp#44p%31>1ZJAI&!#)DSyCV9bF+`Jd^@nF(t6ON;IW zh9Xv=;{I#;s=3@2JFnE4Ghb6_ylS~$&2Yb=G=wDsss!&W?iNBvKh~U!%d*LrSe|-? z+Y+zvE1~eYE3w-bbl#@G0bpduNrvkN769o}YHT@I?r&^Bm_=rO$RCg5;AU$vfgp-r zRk#pB6Ysd=|1lP;t&P^U-e@~F+ZE2%)p?yYmCcxaVe=uE6y?6uvfr@nNYb2<@~^X> zvRE`!<$!$Ij=hn|>1% z(JbLlXAv^~#F*Gm!T`8N{Gl_M z@$Y<%sJXbdndZ3~#WUI}&Q9=o&fhPpaWrk>>esaV0jVVOC9{rvu0JGLpjaTppIzom zg+8sjA14wh9YDXJ6V}_uHD>3EDy!!PUgX-w%{$$c#&E!fM7~(*XmKT|Taa>BT1F~R zJaztHGm{o7ZW^2$ff;K(c)50VU!}QXgHF1nfO}OXsqtoZnIi2eVRsf@DFRlXut8X4 zRq9{SSZmSg{!>*=s^Qzx|GW&G#E;3a0Y468!vU`XL5xFH8Tvb2>{AtZis)Yg?@&xX z*kB>#>Uk#zXc6k}D0*RE@D{<*DyLTTmwak9=Wb}=PcrrG%)aJ!CNC^IC>GScpx-^E zv!>>(X4ellDUiO-%hUG!jhmxJdS@t-@d5e;3Iu+(YnX9g08RlY&K%LADhyA&BKRW$ ztcjkac~cx1q;o{@PQac*q{8JR7Qm-ixlWbLqXuMiy>?f`4a2Mr&yYs5ukdIXlTIez zGXF9u@kNHS7j@Le-O)v2DH+G|c6MJ)VW6soN?!(NSkGTwOid45%fym`ldb z$h!SA;_D&3wJCBYV~;Mi?BuE{H(>B*tFwZ(9}ynj5TK`F%#2OG-p+vyYD5iyy|w%KE+{-l#YnnJ0tnpqYJaTm}^QTCH-n_F!H! z5-7)h1$7uSvZwlB7K*SnE`3t%3;LZv22MdQt)2rBS6#CUpa$R@2(~*DwP&WqS~iGM zYjcaI9d?La2AXFFAXU&6L=$LJE{J_>YUX>f$siS{Uan<}mVTuU`uE~vc~ny5+H2rh zi0Z)4%bAo_d69CRsGf%sHeeKFqt0NhK=Ly%pIHhVjJlx~pSlX3R@P?t;r)%N!%8%W zqcNAT*BwlV2RPnU{NDVeAHngCho2$Ony}Nkv@#yX>g*TAH4!IUYq+2BKRaNY0qJFb zS~PEgoErS6(*hca{5=his`iQ#5%q%Nw4%dVtm9Pe+- z&HiZaJJ!Q!wG`15I<&kG;J*6_8gYt~emS?MFNZw8Zc%)XvJ9u>QiWZq>k-)ezGZWH zz{?%f8memRpI9=~40I-_W3U=H4TS^X0cioxB#_)i#awc#v!u)EOh2sL18C5hu1z6j zMXoc?#T)!_gl+5$mYK`_R=c*f?yKShesQ93Ev5x`74BQOJoe&-Q-Jvsryv9E{-y=B zlEo2bT_{5A;aub0HYB*z?ni6bHZoo3UMSrt%P&iy66k1PvuNNyLMpT%EaawxHpWUX zsCLF>DIZYJ)cy<*Vr@OZID9#D;X?ZAip=hXhBJxXX_U?^w|IsY4tILv{=qvGW}!(j zqxkn&=mk%ECy2&CwekNRH_0z4eRv`F33d8I;TFx9h?tuqa%aK!sotL{fPZxcockX# zQxDTBciR_v)(Jj+%IO0u6&aL0hT`~{>;ti5Vm=b<6{Nn@ zhXWWNC&f=KDgWM=ifb!UkMgcHUAV8ES1^XV@>E#ogh)8VP;q}Qtraor0OMLKPszGF zXcu-h-t`C@$-D1tl8a(%*gEk8zlVG#bwVpKC!Z@W+_@cL=K(dMylW2So32d}=SIwM zVVCziiaLk^6?WdjFjhZm;5R9~vuM&gsD*yQ_K17?_nS`Ww?{-u(An833Rv4wz(TJ#$K%B5>YPOU3c6S74zgSemXMLm z;vH!CN=*e8QU_M-R@-x@I{q@Jw8qT^=7`s@7x4R3Uoi9iKbORt>1YZjgwqkxXrMBv zRxuX@(6RW!gLr42C!~|5 zw@v2sXVlY;19CRi1l;gOeA@u!G|{T$v45n9Mk#L^aA+Fqj7SQ_g}LaKcUZ9mEZpYR zasCjur7kQ!3^zOR)=1dRa5`SnJ{9pNx{^W_49L$V>?po(j}SYbOP=%`SKyuHMp+mA zNNx~uClg_Bv_VT2So0u==DuF0;@#3wz99I1fhXONyo~PdJEeev{QZ11;^VH|95ffq zOIQ`dv@xC;hIMZw#Wj7?FnU4OgfX{<*EO5EKY_!MJFmI!v0ir*JOCNFB)}D zgpu5DOzexKdV^_EKnf({fjT^mhDN-yHJJpT(O8GKrSp_x(R+}?=XRvTJG6dI^IDGG7E|S zlaA{)Wt{kGh}x$kt8fld0r#!|TAb67!vvel9jHbIcDreGVG+ewxr z#QRT!unIvFQ}U1!AQ@|ea^QL5zveR@rX#(Rq8|?%AJUS!;v8nJfQ6riy0N7dK6C?D z?1{{xi!v_$W|I}?E#N*n7Ra-(u~`)X*XeY42#FFL5Zr+QI<65P1PfmXzhE(*tq7-3 zwTg$w0U(T!dX$)-F%VF6WZFwseo*Soj3K55Jj24JeI=;`>~5 zZUBs=OeC=T0V?Y4AoHpYwAp~tO=Ss)M~y%hfw6n2Uu-a}lf(J{tB1f@9#YR-nu+xUCvGnn6_%c4rmg>eWViKiJZ0q{P78~ zLlt0BMWXa99zzJIA{~kBiG4r z?Oaf(hUUPT?bSfCAatI+U#xUi;pAgRe1Zzg-H%bUHkUi8wG8x^@x8X0%&zIoZeM&` z5oq`F*E#>z!jBmC41SrTW7#oWvjJaxM=|mrXKz?lV)i`fF~|25m880?=CK&Jx^JyG z4zdyIg$S;YIs><&7pn;O>jw6jwPyyyN8V@Nc#C!bCooWVLB&TZlw!k)DJLk)i6O9- z;}g!7c9&CA?0J=ULn0<}6+7sN?3xMhcFn25AVS34lhLgfJx4z^Ok>rX!J$DacfBfz z`NBCJeus`z207!H=))EXxXX=IRVgdZFP>8{5 zuo1hbl_17v)CQq|RBB}i9P*AWzg#JnkMdu`Fq7gBF{D*mnnD6p##TjjeWdbCJ zRf=TaHv}o=7Y&Y-Z@8-&w2(`iSA*^Bx~tIQdb!);d)W@h^r-@iYl)fNrD{d?-`r_Q z{LqD_7m!WPklL{jSZ~xBYWN$n2Qx7A=vTs%zDw!FTN}b)@T@dje1p`A#;gQY;z*Dg zHz*t>_K?CJH_H7`HA?KH={%O@OCzQoqPcy@P=V^cPc0bWvowa0kE}UMmUcDSpi%dU zrbD2vXq@qh@EDC}BOn>zO1Qe9EtgBZO6|Fc_>~fW9RTks+HN>5)~N|#WdT()xQx1G z%^3C_P>}gS4d#bz$lo0Ypt-!>5~-9_muUA28pbfpy2>m~N#>{sF0Qu*tU7a`&uR|L z==KI68NWX6+pOZ41a|AjqVa$iiviu~0c`93s{HI!O`FFsJ5>>XQ_V2{#Azrvj>)8u z^!+#t*BL%%;#VMwkj-S^B8QsOL(~$?&McwTLXZ1R1K&-mDxKgMJ|T(wzmw7czdC}o zISR$C7#nwxdaE9N%OU^9FQ`|{51l0K)oTmLo>@&G19r?rGv#EC-EQFL(i-uG4E2aV z7eMoqX3-pl&kkZyi!Kt>Kp=ZB? zda7tL6UBp_Rec!HQJSRcx|AVdP9lite>hw*}oI2)#BN0ZfcoG zWFkhXRgsnv1b@;{K*6r59Snps^(P#1D3Cb9y~@JTO{=nrXIL+H`FGq((qSFj#I!Hz z(3TYPSI1AP?cwuu13-)&VPAbU3=cG5#k|Fx$F3_<$rn*m(mNV%_){L`#iL9<6cNPVn zK{3mxwJY{!1)nSHABpu~77Sma|F{roZ^Jk|H~)j+i&X%wr!3-KcGoDfV*O$4rZaTl zKpFzKpUcsPQ`!y~e$lR|7xPK9$BrhF^n{gvxKwUXnMXRw;_ad?cc|-1lBzO^!z3yR zXz2B4yRISriq)F^H+vSpgVq%M5coRa!yFQQALxAxf5gUw;E1OK-u3OSf73yclw+A3 z!YUD8k#e1Buw)-D1un5Mj>G;5s6e7px6-}B#VPDeiVwc6z)xob*}@yhH|fUx zad{lui~fdj)Dl9qwOQAgcTTQ5Y3mCEkPo3ak;N7?;XO&;gb(j@PPkl?b#-{y^m}QZ zjbg-&`bP2msLM4`S9e@GAz{j&tViJ+!`6vWHCbO@OfO8-Lq%i?&xqcmgz!KRZEG|{Q9~^Xn36<1pT2TExUAUg5a^ZZ3OickKN^5+fy}Y|W>`dI_HHRyV zs*8Bpcohwd3%=E!Qd{n6DVyrTaZ9;A2e^`s8pbzcO(*(x^xN$kzbf?} zX)d8N+zt#txEI+S(}Xt1X?g-fBoLa6g!YUjW-g`^lK6iL^0A2k8t7_+kO?{+0EXba ziUA;g%;lZ-_urtOmr$kgPm6UoE}N+o3>K&NaOoTDP%t-JTRZEFT=WK;l-S)`bK*-X zfvVme@$3{DXE^GKG2! z$Q%FEAHgDORX8e~3$CZ`Oe!1sKzejPEJxgMVoJHUrPg!l8B*`nJ%;o>;N1pY?390h zH9^^c{Zrw`)V*s#!z}Ql%L&rQuuOouD7#b5>U6xs&b>6z?B0r<7-GDS3^{v9%n^gB z=T&mOo}W!}0j^kTX z6PwT)TB!>wFb{mpueBDSL+Y0{xs$4LGtXK0XZ+1rsw<6pp*t=PZ<3h9<$Z(6n+#up zY7=k3>Y*2qh?t{1%Q}eo4$oIMmv08|jpFpgA@v&y#sp#+*pm=hRt&2BGY-EM(qvsT z{!*GRz;EM)7~bt1^Y0VvCa-e_S&(}u?hm}puMoAunRwSse4c}`62IyKMBx~Yl+duo zz!3=Cj0^tlvu~BMN9k^HWv-1oXWWq=XjH{BlM7DjW?{85)-{v&yYY{C@iQS!))2I`*4p?|^YAjKd2%^8%F%7N8Bolm8*B!;| z@(izbM?aB|q#EFsF&PD^`FDgOIH&`lb(dzK=kUHxsu88c$mey|mJ%~eGJTQZWtQA* zVePKn=bLSP-?v&LR}jwi`2r6SRrnQj#J3OV3WWOs3KJS#AU)LsO0e&AjF`w3i&bGX z3lA~*1GD^IQI9CZpJD3};UHXSk6*@z;yBT%AUn=ZwS}Y`{wp|lW06u+ZuZUU&1tl7 zexmLZnOl`&N#8}*^m%YyBTGlo*H7rgmLQvjd6F=4iiv47E_7aEm#~t=CG2-`@IXVD zKfTcd2kJ?Z4>Pp&0WgjKTp!8{TP3O_-cV(U;KkDo_RM#9PuiQv-KTt9xF_syJS}xv zysL4--7Owxp?uqP;I!=O)vNm&CpMRu&-ex7%|_fo767T6gXV zy92sFA&qq7L7=75yy;#h57Oa^jQC{>d`e^UzDPwWp9X2mFr70d z8n)5@S!)Nbir~J2)?xEf&%!Dv1Ph$BI1d5upnzu0v^3zZrE?LC*1>3O27Op~7)6HZ zr0Q_=ImzHGU4H$XexkNxhHB2f6CaD)tuJnFkutxc5<&X`FOeU7W01k1}lJk}AVeHVhB> zXrkY$da9B^bL&QwT3mAj^jP4G;B@em>acednK~~0J6lhfolCka1|#^4D9%k)>APbPr4E?q0YP=HyWtm8D2<;80owfv2d1zn=Y$21t2O<@pEEqwpHj&IM zfQwUAMeu6k)zI7o6ck27#eV7z#?pyfVY3^bsG(-qIu4NXv&z1PC9c3cW3z5)Ju`e- zxmKkAS!U_l!ud<;%Gz41d?+9=RmchKt#L%%w^UnEXg?zMU{ySXX|M`rpl?pZhkm>m;BfVT^FwQ?`_?AZLdBc{ zCD`RaL_9RF2@41eXiqve3qWPve?_5hkfd*kZo1nbFZ?_CxP)q_6L`U7W-2D)K4$Lg zB!0EjlXkW@hZx+dQw>X?{}=xUu>q=vx0VWEgMI~Ngc1k9<-_p10+*oIcsVoB?+~mAMy4Wg%F@6|ey?)?=||C_mDOShr08G=0IyKUCi=ep~2srP3DE0B3%p zJIP{$k-Se^Bt0k;6jC|HeozPpqmM}JZzMGx>5e5Qv2JRUv{`PtnCljI{arPR=;HFe zrB<7LEt)>zA@p&{pKxs?_N_*k9s+|UvPaz)OwYzgG+FVhTVlcHQY=|B;F6>(8K32< z0vL-G4VZVu{C*$pW!Z!#r$VNs}&x26*Ns=;25?w?`(kQ|A_D)%b_A2Nz)ycsET zZEf@Nzhl+>Z7S@>dxy>@wvtI@R?{15rd%SKekTj@>x&^0ahAjudEujWfuT8xeJWe( z9k{CY0yK!C>)RQ?s&(Vj2DzE^Ytf$~cX#6uE|tvt>&R52SggTTGHoGVnF^gW;6bUB z|35Pi_83i@2O(yoOr?UQZ{x2UL778mHsLY)4kZB`Iy|c*;w2(RU`d_ip zDJ&h+Ye{qU_Dvfb^MddTF8V%D;+d(KcPyCx1|_CqD3DLQg@Ch}RQ)kLzJaKJ2+Awp zB8NHMN&0ry6HN!aFLQS^9fQ1+rWG>Y_o&oecKJb#^noVrMgNj#c|2#{izGUZqQPoY zE2a(u?d=y+$1e0Jv-TnhlA4>VO6Do2;Id z)S^EsC-kN_&79@XlxQwP-ywTWtJ$cR4;s`OmFatiPE&`mRNkXEU9NYrA8~FnGPoWs z*Q*Ehtb$B0aqDp%@-GK0>`y5u2;@{~lHhk;msD&O8|wcvH#ZxI-`M?xKQ8!g>5*t= z()lBY!{xr}PWDN2p9d*Sqx5clhxZgi2vev(vbS{re}evo1^1mQ4F(1BJ7?237s{Wi z3>P{3&|u_hA+6gWrp)zJ>=;$6Dv!Q}^f-Akq2@FyPc>GX)M~nk4;Skyclo!J}j%ogR9^w(>#gX6e%~lCd6Iv!t#fMVq4-urY7#?*5Dl* zf)L9^YlY%6J9aUvs|h+WPZHZ{GyG-Y4ag1<^dS~Qixddb{DVv%$SU1krfhRdhX6<) zgQSM}&!VvfUOWN@$mODESfn}fxZ`3LswOAWYFt|+FCyvtCGuyuNV1vrOnRdcdj0&g z%Ljn@`J)>2g2&XvZQW2ZEKl{rE*^ISSnck@-?DH&bkzHqIeu{w+{D5k%jQg-rYvCx zbXL9-tx4Wk2}!jK!h!k!Wy?wWRzVs3qNFyG%L;XiNcE~>`VrHsUn0|^3|^D66eH7qZJng z>Z%?Bp#b(Y8Nb4>pqtR_FE$0w9I01Kv?ya?inEm2Sb4(6XcVGH{*;YrZTR0}+{(l) z^$AI@m?S)kVplu3VLf7^O(S2$bltphS@Q}m7bi{wbsFD1SEhYNs!SxyOGXi{ow1uF z_2Xze26B(Ri0yVCFHp+3HHMk(EZ&S`dxy~+6T~iSh17u~Cz)f9E#^(lD1$VUubzi=4pxjSZa=rr*_LwGm=`%O1xDnwVQ^f zk)tK*XfT)sDA2_+qJ)rIKI(S;T8M$U2Arc|NP&H7ABc+Hu2qWZGL@u`(cUm>NYPlj zo>cFUW)5W@to_4ZC*Ee2jMOb=?M{OIuf#rpH2OmRiJ6@+h-~Sgr3Gd%o5(h4rBzt} zPC$bi^{6gF;7bLk>Zvx9nzK~z8}rg3QJoVLO#>^>PI0vjcZ+ee;-*>&16wlHtCud$ zhPiwm7i3nz85!55<(BaMWGvL!!uOCFH;Z4b1N-tX26H$-+zt`m`xL%ms5}IjH5=~C zxN%Q6x2epb%>Zg8^@WSAtJiDQ`&vvJwQIPn;moPqi@h zoEF5|0|!P?iEq~qVzo2wx``nrPgNuPA4xw`CVyQAZB$%|Veb@X=<%QP765anv(S2R1tgx;42N+^Mi)u^Parv{c%%Gi zK6*{z0t|s1%lYFPbL^0Qk$XAs71Bc2Rm!)jtic&36*GPmkh(0#HKPIVls{I@)ppn( z+i*i?=;f_!O8Z>ulvr0vT)eS9t+9-=pw<#JbIj~VNIpvXkhU}F4=&)+CZ_y^X>oBq zPHnjQAE#kKbc)R|57vqI!i(M?n$QlRwBk;lroA5*uLIY#Kynj+;I&U6I(F^ajYTQK zN=$2s?^y-(zdU(BYhdrorBe^14dzYR_%L&XT>q}&C*(zrK4uTCm#m@sf`aFm>X)3q z#{_IVtg(fV8$};stVG8iVTTY6Kml?GKhzrMXh@iyJFgc`@V{irWhne!MqNoi-mNa1 zT118)5Q7u^3n~?#4>Bkt>vRQ{=$Xs;Yx3t@#Jm*Nytx5!@NpiJ(9qZAB`YUyQ!c|8 zPhu!c-pOci62FqXm`ur=gv!pJgP)Y=r zsmpbSvlUD18XEFXVKcu?tg(@KkmRXMKho*!$y8Zca>Oi6nQM}c^A?#NBEBD%yV$6y z`*ba6{QSNKzE^M#F&)$vY2MSCF4dOtb&I3~nZBtt=QniX3pOgYa$-~=%fuKneae*T z30I02O&nwF3pM*#s~lsN>@i%`dnB@C$H;Vy=}ku6B8jgfWqc}kqh2bv^SGwN#9RgSuskjlqcPM1mn;Z5xdcd`}X-R^RwH;l`x z(o>uCPmz5J%a{WPuGR5dxKvo6`iG`y`j=FW8(DB?`jRR3=`Yy=+$`g(wS(e|#vh|{ z+*m2@I0DraQtn%{w(+aji~ntFs#u3g>1vxb5|{0v&5QZZY9&Q#9Z$sdrNWT(DkHmE zlKyOANOkU#_*?=vCly1~k(>W>#?M33!wBsGM( zJ!*3(UB}UJ8_b-NHUO`!%4Ms$uBU^C;&dyu9MpZlgU1dd4=e`INa)_*34}pqLswMS~0|!3vRK*h- zSvjksI@2&+d<`I=biOA4^*Y2m>4G+smn3Oxy&AFmr&?Kc*bToQc)=)tXoQE1N_t@l zKTl!X`NEMcT(B1wu~gj9#5H~00W6KGo90!3=CEZWr%dKYKw%Sd z1UgEI&d>Op56!ElZ|YOqB$ZHOz&wW-2!U!t`C=}`?@xSy(5zPj)8mD zhXD+w6S3)tbEWQM`IJs6P{UAAc(ra1yK+xRmhI=O01()n0|e;oPJKz_oEG!WM$+8N zw+VU4z>C~E@FJIJ4f{|*C)Uf%8lC^m{X@~b|B4u`*EA?A2G&NwpH^%^T_+T(@NyYuLWw9UE5}=~o-qZQg)Y zUJ%DE*0IIxACjN?Z=1eY_1(x)fP9hB=hiUR$Ii+lq+pLSnwQ067rNA<|Fz$6`7UFs zrM$FyDQ}PXB1`$=)KYGazeoHc&AcA{c*1C)L9mn1cN&JIgun{0^*<$f+mQw6BDl+c zN@EX@SScAd(07F{C)OPZWTT7Cf!$OUa7{MDOsc>k8aTg4>%c#E-c5s8%0ft*Ch`*T zZUeOf_3R>4^wrnXU&M>zi@?c1ISHmXHBwosMq&#jrryTA8kli&iIsXPs{>zaYYS9& z>Bx>V@00Id1Fa;~ihuA;_JqNBAq)w1eZsly*x6rno`8HkH+=PvQkNS^!543T8 z-P8+#L7zdRuO?(ItvClY8uczmUd;SM-AR5*#vET32dze|pNrSMb!L278(*p~^)1p`n?DC+>5of_{4VJ(kwhzO1||4s z8IqMR8|7Ad49^7CZQinV7<;l<%}%y(XPw3M86Y>Q8{^pVWI;{F-?ai*W-GDNegLR~ zz05##daaosGjOHS15n#edD2rfCIl}5b+;PS1(FB2XM-{r7TinAoxK0 z^`MsDQMy5V`@bDm{ZHTZEwY1Sq9!BzgUxUi&0)0Rd<6d@*VWbrD*sg&5Nxt~AYh?L z7=$y_D^Xv;=10Z2gt8;?hZ>IJ|4t~w0-9?VP)z}OaGHA=H`8KQ&;L(Lb=`lpRQVQ? zdxe}TKo@|iPAD=80oE1bbyFu+KWKXLUaRf^w(x6agm2jnu1B(Yht34cuR~ZYV=JA( z>&T0T*AmKap{I$}&L)2Y=try3vi=OJosddp70k4eRcuBc6$Fpe-q?jl=kpRdX_l|$ zw+qU4#4oIYWyS8k*0)NC3E56xErR48#Twrz^KC6`BbWOn<=bWIfP5wPwIy#7P%R?{ zdeGfgNTycV1Y#`QC=dLA^x8wDyi2lI=cq_wjq)$KAD`*Gff~v)H;~&Ld`KnVPt2pX z_!Tv(7A7h_^Lbr)0-d9*S`8cBja|6zf3N*SB|y4a^fYDiwLt;40c_ocVi4%W4SFED ze6dP7N++c=1FpQnG~oO}Av5jIbSu0Tp$oZBe1_2XjD`-3p+70rDgQkT|Cz(h{hUVk z>m`GMzWzb zohmc_hxw2wEBhW*S(-+*xLG6n@M>gsYoBKZT-eOI=J-xAWf zk?ANf_CFhC%St#yE>eBls9a<;-Dbp~E6_5wATm$(#=q)H8eb&+9c21FR#xLjq#Aep z9*ZaA?|E-jy2sA+1*}!Q$p4kG2cCGYK9l@s$f~q?`W^D!8rE%+dq6EMFrnJJsHFpj zC`L@6{UR>MiIuc=kbJGma3yts>U)i3PX3yy9$@c)lJJvN24A__S|TxwEJ?EsE)h31JErT?Qve#FdnYxj#@JdpAIW^EZQgP>Er zWGdSxNMfJ3M#s@<8}{MmpBwj1kHeL8cHG_P9U6D{dV9y+8@>C+-BaG-arbs_-?)3o zyK3CM$=g3(Wv-bX|Ayt*=y=08MiBbYv(BA`+bSTmkhw=~o_zt77HQwG*i2t$v6VmC z+Y1Iz=2;D&@*fLyogt*lz4&lyIjbHUzuzKnD{NU2$gb1L;45ul)|rff&k>Y1#>T~j zj(K5(nKOSdu?Cw5-&2cPc+|fa%~t5B^5D^Y^13smmj^_1=^JdTRTfy$zi z_!^pUM(g~6T}kbLcvrj$^CP$+dD&M`RZvI8<6!to^WlhJ1V3=a+d(?h9oKu~ZV}PE znHmnwgVPZyBOsP-K+OJRk~$PAW&mZjd)&FmDX%tM5%g~n6LfBB zEq4T6pJ^SF&XwF6C}-Aymou1om=>5Uuy%ab{uT~@Ff9%_rr@b@Db*jup-gkKIMQ7D ztD4_I?%LYxdH4*hYGC+`997t<))HA)*OKXHyvsbio3mX!I3=6Og*%f$6ql{^yr>32{98O9N!@0Sp3;N2lGX_AP`*mcu}e9dFO!#Y?XRGs$Ja-4 zXi|e^(k!MK*XL@}i!{P6vgou5U(2O;wEa0if_g;_Y;uh(>y^zY>*D{q7d=(0#=j*G z&0(wz8HjD+n%iZ$O?9MCaE%%!6VY^{KQojUaB}}zT7`7V?^po`(c?_42}Knkh${u# zlUPeFY5!cIu|-Hbakgdntekiu;ZI6Zi!0sGPL=+)UmLLV><)=PSj3BEU&LMcV4<}V zW-VyUni{_v%-`DDL7dCu1TEG?RHY2~Fw8>x{9?%`%Y*D&}gWHGH1;e@RH2Q~6_qc99?=K+u1cbna?!Nk9w}YT2Qb2UfCc&3jfd{>VlAx%v>uEYp#OPy6?}x*#eZb#0W{ zUU^~kJY!!g+K>)wtdToa>XIf)rf7w9t$~eOdj!=rAS;06x9f35FDcS2*Ft@h*|f?X z<*-rDh>Nh8E)<Q;Zv=^F(mM&ZlPh8ZmzM@m{gT`?s{7G4FaxgOOQz=3brj1<>^g?8E{^(iqNeP!Vh~zlZjv&SoviHRB zL=WKmMAe`Q))4{CL!!wyBEoju|6?87ujRjG>`Mc;a9y#nxS_`QqFXebObA&D*f1Et zwgmAe8pLA_v5!@~q8hiEK39vsZV_GgBcvNq4MMpEyVc#mG5mb%fS*YK)x z5C=Z*d-pxraIirAst)G2fey9mTwatpx%4t|h7cR$8HvGA*p!ttqPEs46`H0cw{&L5 zuvfHyd~fq?+S%IL>W-|0--*#O=VIo-sI2~)zB-UoGjqi~9DD`UTPF6Td@wKMhPeL^ zQ}OOLf>f&sOz3Y{OgiUs*qOOCTGnMf|3AsR`+bRJ6-`rR@#edf1(5-X-KsPFqkus> zlzofN%hUyS2V7mLliCEJ9W1Uig>qG@VXdwoA_SS!wPxu(QbvhKv?i>K3nJQr<R=oOKOuCkvchyQAy90G+ zms;0l8E-8!H$0_X4SXo`0~wl4lC0*JSR}2Qr{VzDD_46vC)TL)^23QMVeEa+jK|0=Jf$xrDeU6LU&DdX!RpQD<^ZWh|wi&0vU&kBV(Fs}8YA~Z0-4<;H z9$seLYb%X5qLB8Omz}Sb-v$FlZTehMs#@IFOBR&#L4`41tgUl6_p-{`)(=4FW2P(7 z8=tz8*<3oV@)3ly>>Ia(5MQz|b&Ju{9gB@|{tKIGfmB(!yIoefRgT^6#bhj|RmhK` z&m~*CQoEr1QQy4#gmg2mdrS#J)nNhk_!4z>1_KfnfwQ;o{92xjh?L}r9xOT+dT_jj z-GUEswHK z$D*VZ5SuJr*mGi`eOo;r#)kBa|Fyz0w?ictm0`i(Q&QH0jNBJ2Y(d5SLmt9YNUT>_ zY;nC&!4>j-y$3IBQrH*mtr$+jia8Zn#1EOIUlp^Bx)#jVD@#6>|33~cNDlMT=HF~x2S!4RF zLCv4zlG>;`jUE-PJAnXN(Ia(0TP)Ze&fC5ww`-lXExQ%# znmC66YmzI8McP$lb(**()os%J4myr`hRl?JJvWJo$yr4XSAz{Cr1TT11>pTvwYuVD zI72VpZZUV?CSQyYhoFIhYSfhKvgnNyGvnJ#ReZYkh=wc_*f$|AU8B{c?5c}(3xhE| z>y`{NI@4ZVtLCR*qr4=$&|g2JVkJydOwwu{B=)eJ#0^o@_(JRusE+4OS#*-=(`w#{G;js%M^2=PGCh#BGy%9m{&5M$T`)P@= z?ko0z1mnNn`aIALC^5H3TI;Y-{03c-*D$TE-7x+%w9lN_oOTby@0M;9?R_7q%A>cC zR{Co0`PGTkDREQ5;P@9w$VZ=|g{EIoiP^c6vB&6%6&uF8+M2wbO--)43p}M297}}& z6Jz&fyLw+M!mq24x}*5@0==qT@vI+r&YL`w(X=GD*VjX0P5J^DBFSGezVjlp!$yX7 z#(#zB3m~3U>Dq1sJDh;GE5mxM{PZfmGy=J%G9MZ_4ZH`OKe5F9O%4lja7N1~HwD1L& z|F7ja_=xT>RvQQzK4;?lhLpgz0cjs^@S)~4r~2dV!vYBknKYS*uswaw8wM>NTqLHa9FHl5{O-?n;Lb)KvzLbhk(@1 zLFE;`3aLVBS~)9&ql|d@FRQ9Ng=&iQhE+_XN`ZD!R4HH!UD{8)>;tP0E6kMzNXa8g zf$1_F*3bDE%KCHF)z@eq^}&?p{>LvQq^pNY0mCl!vgr<7g=~dn~jU zi5cLz-d`^&ZhE2CT>U1OpV@X0>>TLuy5_EthS31!Zw@l`)*CDR5j&mpUoDvzBnh6u zZ%M}qYPtf@6RrA!@qAR`+i35pO3d1a!`1%!AZh+^DRV+_E;0k=SvUcY#L|$FLZ>va z(=?KID%?wOb~*ZgVr-9){`SjI0L(Zc^0APfJEd3}2_xl2%P)-~Vu6|Xyb3Drm5~&N zZX3Vye{#}S>eb5#lbzN=isnAvP3kkQ0FEmyQ)^psK`_3ZWVou|N;l;dPsOh^{haO< z^utIz-_`sov2$hq8XcE#7|~!j9z}wUC>EXZqucyQBEB^q3ZkPq^MJnEA1$WhUa&QS zV0r$NdW6tZBM2B};6Nl2=sqrn)RD}dX6ho+K1%muU{B=%QGck5PAbv3Xw??#IR7U( ze6v>dj;cD6+|j1`5hQ{0lE}qcwntIk+!J&Mzfk67p=W{hYsNn*Bv|u;iblQ`qb~9uvHc~e*4cwRjTU(bhCRSK zy(#yF4}B~HJ__L)j0iAey^zdwM?OZBO1|!UDIzQuCou$F-pOb+mB!214~6P?CMlA* zREuQxFO-V9%+wn7rcUS9-Vtdmii>1>aD}Tm8JvBfQn{BZxY^l)f#!N4jzdY(Wg+G$SVl7cQiM_&#zVO9>LaR+zZI;K1Y~nsBfV1#2I8g zn1qstFXPYtwvJz?flN?4!uEcqnq@3O)=b2(MhK&g8_b~s#yd^?{(`;G_447?1p=e~ zV;&T`fqB#SQ`&*q7Bq03vNur3E7fjjc(k9LnL)VutJ!xa%Uv38GXD z;`6GOUhxhu8toy_4At5C=H)|4ROZoL(2t4k(!MHR^a!{3E~jQ}@t>+&@ot|af8IG5 z6b+MKm@6w<)^w=Pl3S3=&Z~99a!JEFnTIKy@K;Bi!8ar}r?9;=n3rxxr!1<84!dql zwOUx11XNtD9>ODE;-~a(NC?}UQWrvkhbs3)s*@$Vns-A7(w%@>O(=)1J`5J2jo*&b z=!B7g5M2?fu<9F2TsK8E7hHc|TAab4Q2k0TXY6O(k#^gt8KCduycth_>aqY%v$^-B zQ7(Fu4Fz#cMI+WeI2Gp(GgPnt09bE-k*j!OHw&m6u|lJ0N1IpRlCF{Y+u@<)x8 z<4Kn!ziKpBETCR-Hp=KctXI%U7;*hp=9e=Fdd5Dev}L}_`Fb_GhO-9gTom%e5%YD~ zEiLEDYy22in@RpAS$ti@pCPG*ppDi-<>3}82Dh~+a{)I2;FG{yxSR6&SY0bgvsJev zuP1XS4TwJX$YXWvryAwKBF}iz|75Z1Ld86FG1EU)6FMbrFuRR!a&zGCJKdJh`YE{h ztrE<;8sf6LPte#Fld1U-Ga1j-cMH0C{VW?r=6a6o0ZjA8xwh#3=s9^U4%FJwYW)3S z0Ci}am=+}N0_p%yuK&>a|J9mUL{-_OfkyHQ8+D?c7(VoULU!U)YJ;MK%z8zYwwq>? zr*t-a`TpM#LbtE7gRTU|-!N1!y?0^6)bL3K^IsqrzBbq=ZRWX{g% zrHfEa=5XE2rp%GZ+{T05x zqW@WXnk1NOdHkil;^+Z|ew0^dBKL`Xx6j34t&(%uGl^m|H8Y z)ml+1w6s=R3U+TTU97d%+ErVtwN|TFZ7bDZ{ay8{Ko^%*@AHP<&*%OT$V|?hIm>(A z_xpXm&+~lkC<+&X+Dg(gR#3l;T?M1`49Jk?naAsTf}#6u72Vb=WxSUy(R;nbfpqBm zuE&c*B~)0qoQhg;*;n4;da!hhURRq#xz|50y=Shq_*e#(WidOPE$!r=NxAcOh`?&d zMb}TKpldUrv(5(rSD2ePsRrgIJ&Yqnb5kt>1P8Ht?$bY#TXYz_Y)}gIYKC&Kgj}?b z!1)&2x1%krP~>e8Du-lBUnAAylZb6#Tl^>4r@te6^;OqOPajtvlukjilbX?o;g0e% zxj?^>_&ljl|Ipn#XOeQQ{Eh!(sdC!JUT`eRZdBPmNjsyElwv~-qCmQD0Y`H>=Nd_B z=h6+#j{A^g?llrj_@yV99jwF+=*&u}>0D$&$2pV2A|sY*xCNC^eb-26-JFWOl$^Ai1=yXzFPfFFYjqCHeRBKY*4GZW5SV(fUdF{@@vf7di?xLy$~DP||}% z$$-7Z+#f~QiA5Kpjzo6*3}i?9JB9r&mg81B6N$rtUdWZ(R@o22UTAzUXY{RY0Zh<0 zx=bykU&lcML282~IfyzOWe!7*3dHNJNp%-2{ED4L+2>ub7sC8jDabxM%hqG5wjS^l z_i0RN=5osx{3|VCSB<5jQ>oxs>J#pE0rw0pwyon*n7pwtmEnhf{1Rig3_o1@7vnAb zpa{eT7Qzb~TE!swaQ*?TTB+bn>gjncaRk1|1jxn03bMtSagGfTmn~&*IwQC0!T+lvv2hfo4 zt^7*>^-`F0!>N@%pHVKu#&kBB0t+G+s%AyGU9NfAT`qq%x}ZtDnY00_ffGd{=UU)+ zmDsgsD$6`TaG09)6a2hN17T zZp@x4kso7gvHq@Mx>FfuMi#b+TW(fwjsum>;yt#m4`qPXQJ~Kb36wTUW|UFGEv>1xjI$ zTZTt;kfB1ExLL8)%6b<<4y{V{WQH6Xb zAHs3sRl5hAOm*LaELejL*+%PX-VVB5-Df%6@&~f$HUQI{?1~!L=D<~{c4JWb&+?MA zR?h6wkrcum@Q6aYi)Z?BmTI%CX&uIsr5*JA6n?$vI*U{S%;2F=xw%ZaAkz&W-h}ET0W;b z_Ts+-sw0m7cB+nDyfnKLpm>a7ZHKX7<8;czP`EL4V>&vWf|{ctJ)!W^%npp`yf%tN zAlOh1myY^-XYl9hL9l*7a-}O8LLFkvE+B&C<8D^^7UPBLkS_SnaF8jg;r~0cXaaA+)z>^5za{*jDUy|ze}DmTqU<07BJ3}oq8*`3Gzop4ayhv0 zBHr(km9KLCEp?lCKi*G;4Zq>-poE!CJYY8e9G*N703}ws&Y{_GTHK2??uBqaEY6iW z*KAD%5A(CoB8z7jd0|&aK5jiKqHw+(PmBty8S?;JQknfjX@Q9$yPV2{v*$kPiyWn* z;EP-vYmiVcIsi>I>~f7mjtkU_SRorRFG)O2V~t12$5$O!It6RwBgaq>Eh|t!z_Z z`j+y$sZA(v{z1|^c0?T7cCg_W5y(mc{>T$bPuJHolf%PU(M=;;Lq6%l6yOu%8n&wj zNRF|X*WZ9P(bID#Wc{?9fz!n|PgkxO# zcRsp-37!QmU{?wX)nqm)rYEqY5cQ{{ojyVy&7|`!QgfN32Y2^wC#zFj=IY++wUA@} z2yyKjMYlI3FsKZX1%2d_7cW|idvPNjnoJ=0OJq-qPPEERP)G6t>;EmzJ4xl1|0L{U zbcast8VRME71`)x&JMAcyB7tF+B1;3%>x_xY0OI4r;!!rCF^k;|E-rHdh_M|+1?`L zn(uJ+O}O+T_dL6tf8e}KwEb#$*TXs-{9EoeASJ1Jj#bF}DqXI6o8VvEztEp;Tx0Wv zSJG7t2`EPzrcG0#{GCD|pdd>d0Ru@S*?%DZJE{Sb={beH32#cMe@)I8EJn~O9gvkLxjm-7 zyP!j~zY;$8aA_2)b@US9R@f|v(qMQm!eIzOQEXUFSy*0T41})CYXLNzdcMRL&E24!uBCInz5l9*Jxx4qNX$Jo5J zYvp%s05{Vh=%KcN9+dHDMFvxoHQcHCvgg#X;2ba#a-R~^^%XW ze;1d3y3(bipSqEKAz-hg%5fNBb2ww13)riP{l%~^A|58`59P`~#NWNiVR8p(8-lBB z^x;4Yke8YhvZO$voOaMz)W!jtZN*7gcgo5q6?A|RwqyV?z;Eiritz^*q+(Gk82l1j z8Eu-qLRLO)*FM1uSA#aB)ZjPQO<^pg!~5FXI{xMcRZm+BpDRq!CVyH?i$b9eBq_Yu z_9|ZHasWfH0$bB}^ADsbjN{=P1XDQiEOaN|T!{5?-m;vtTimUV16JqPa^f>1kL0xD9&7^r*TY2Q~<3s;K< zdo70azm}{In(Nk$(U87>GLzL^1?_rVPhCM*=}1B+t+2VqyBaaEqxSFAe90~@vSrTi zB?LpOJ8V3ob^ymP8f%pcYz+eDXOR&`P$qu4iIr@sFcM{MSTNP8!=X75W=aKT6wkL6 z&us&KX3Rc7X%V5rLgu{msBl-q%^4jZo6M)cR623;9lQmbH4(WWTC?Kbd3(O$er9YU zITWAh!-RYI@Gpnh!u)rgV^Qeerw$IqkUS^%S+V`d24XSOh?RifH-ScuSb67Dwn50Q zhxL2-4Z5(n$wp^$zl957j4r6*SgfxF^|4sjmcEYOEiHH^V_|qh27m0Z?dC-@cTtny zAH0W-h43i$#V6C_57Pw>Qxst$p<6oAU`+6tJJ+#W-Or5=>q!)qQ0h0$3<|rt7`qml zj^o)ug3=0#N&?Gk9jzDMQupLnWc5hhGs#itRI`z3T*l^3!g3TTqwRB(+d}fq4p4a( zvd)TJs7JE>b8B80ZG%FQK>}swJToT~`w}8w97>Omj9L1F@_H!e~6nXv`*X!Zk%y-bj<^y^Ii zDt{&nj^aKTOG3-8KT&#IpR$I8nTGypJ2LZv%Cw^442E*>?6lH(8Uz7~=iS zW;w$opsqyJ_>NINKC*G`37~jp(5dVCwA2Dyidio`rDM#e_2D5ss-u<55#1lv1p%g- znsvQ-1&rJV0u{4~TwrhowMJl$ahqH|(ojT@AnHN!seV#F1IHH~9f#k!{eA7hF}r-HF4y zGqbfuE(H4(6P~6yp}G({rW_5<-76fR$2Z?>J%5imJQRo1YRu97D6aO|hqgiOp zs>7>SB1)+*q5}NHSJxdzYlxpxzj&e=(!^e5$3h9Tt`#np_44}?jwXcK{|JIbc+~^? ztiIPaFEQV?1QibTA_Lnc)EveH(IKu&IyqaU8niHNkbh`=NpV5O&(#nu91qcb*rp`` z&}4jM1ucrNpb=|m-*~q92g;QjE}4*I+NrV&hKs?Z)y4e-vCAC=7DoRHeneE_6BzPd zh##zTG-`@BW=a4his_<=mZq(F!kRS!@pLg>7sOvd)tZXqFz_8uX3VY1fefGwT+tGj zI?SF+it3($m-={Sj5!z6YAhAEkqqqbJ(~xvBo7ML+%4_cVFj`8kPNZ+)y%V$Wb``@ zQB#*dBH6FW_P94VY#ni_he5y#4#X=DRadSZ3TDnBv!IeDz2Mz5$fQEggCg)1tk zM{E%y`L%(|@e8&QTd#Os-WA6BzyGh&wYnG)F*9_BwIKI3M?u@}KpUd@*)iqHxbT71 zCoEI|{CWHt#Xl4-a4fSzgT)dK`a|`~1V(LWU~JIj&MXLJ+YtWdhYB+FQGR=c`W7pU zTut@ZVp_5-b{2$Q3-JGf6&Fn&433D52G+*EWe~I1V3V=NV$Xo4ugqz#JVX z+Ehue0~EoOgxVi6v~Y|4G{OwbpC}21wtB4z@#U_3z2%oP|CXx5&|ZkCs_PS+Zg z`+nmQk)HAAN9r&G_vUSgU>f`p^G{mMPJRcIZ)3{4#GWL+6694@JUd0(Ku$3dA1+vf zBY#_Ve3^PTZtvLw&eQq)@zI&!teC8^YRn0xGgvM^PF#Rv{mMBjd`J<*Ofc7`qiAi`NMR3I`Z{#(HD`p+Ckuwug@l=_7t_|YC zIYW_dBv-A6oF5YA+gX7ZCjW)Na{K|yI#1_j3%+gP$zH-ae$W5MT@gt zR51F6l(&(-(EhO<$R;W&gF%K{8uht*A-XK479a+Auk--X~StkNi zZ5vX%Q{*T+I6yXU?`s?+lci-dATqQGj>$LZb8rWW3!MQ#@dvTp&KWxy8)+96 zMY~nDON|a&lPY!7Y!?ik5P5!{9Cz{U;^12fxb$bVT4=HnD(9-xrg7Zo?Q7UA%LVC5kx#=K7Pbtw z0@IDC!BdE8LBZA|c6kBu>+P5#u7`O!@PWxhr9nzDU1?<|Cjerk?c`bnUvQuclbMK_ zu?ugE)96EMMPV>M61hb!4*y+!1WGmxX&{KhRv85lMlK0OOiZ}9V&Mlqf0*K%u&PJQ zr=1tt4xSfT+$y^_C=Du6gUmzEud3~?Dd=Z_PLe*8FD{B{EJ-L zE@KQjUCRlGguT#sfut)a?^#-drE8k?&v~ui2CpJL4vK{i-=kp^-iP&{@d|6?WpaNP z-=b*GkTVa??9Y*1JIIQkP9`b=obJ=bQ?kihvB&x(L;ss5#4yuLy9;;vY2Y8LfP=h* z4;*EAE}K-i6|JSNX0j$puaNX}=DwC`m#eb&D&@iH!`phE%h#*gkEvT;>RM>MDQUkX zxoo1>!L*$0(2yT$x3e{;H^&p>qc926_rK+65e8%}61&%_-O1dZ*KFF$rL4r=uP1OBA0o$nB)_W`WgaD7Mw;pX z*nlj zG^3Bv!VuUNkK{W5oVSwt6e$&?4Kcg?9!D2*?G-BR>lB)i-#CT^V5SH6%2yjYW|=WV zzbF2rp=U(DdSNB?63NXHE~ryoLb$|j@-1$5EgM0$gd6co@(w|#tt4$0 z8yCQ&O#2DtOS_o%vXrO1K(?Hn7jbEd%LS71UE(L4-gE&jqFS1Hl3+fg_VMD0MvuI) zNGo-FIV98>lS%#5j;KcO4#ceyVg9yfN!~4_Jd~&1OfH0;S}$&Cp5pqa+9oo29rZap zI;e<__Q*-XL@#Bc$bUDqtZM&=7wnG;{6}cG2l=Ly7As^ zjLoNWcsrYeln%2JHaw;A8hDauuqENQXuO2@bs8c;4x-_DI&S6~&>3{I74FXugrK#R zTl3om`~)f(&u_qD3&eX}zH=!xV3PB~3!j|$Q!syCsD>4H)v!YSiADS?(D4Z`#@ot^ zawaS8`R=?{tGU_iVcWZLyn!ESwn+X=AQHqO_W5WP(2nKMl`FM@h!rB#zj}i`sQesO z7%{b;xQ9#43L30Bipbqa?FRlRhizTt`msmeM6P0TZx(@p*5grRSTO6PV6oS3#d_-X z$TrCO9#Whi=7DG7(~jqnd>Lr~-1R&l7vb#-ZzMruqA)r0pOu zSmE#3ZL3KtcgirI)5twaWkwUp^7mA^nPhN2*U=*T%aUz9o@vV4LarZ4{GXEJIXgcj z7hodd(|lPbxPlbJ&b~$77BrwjbDRBSgTc|81obZV;jRP5#pKIuwic4YHhG=+ro3iy zyhR6cMlZNA!3WFcmcwjIWju@-E@&REwe4B+SbQRkN~qvYnZJX+^n3TR#!dXyHT@!a zNZTquVo0x{Y!j|>&{qS~1V3vYtW)QjPzbxfL_vhLw)6^zr1YT`EkcVMpr7DVb4|O2 zy;SJP+ErkI-=$9hgaz@+c(eaK*$}HJqBiU?0xl8o%EXsL{D-yYW#G=3NKq#h(Ba<9 zG@utdeFo;F0vX%qtyVWy`FpKRRlv%OLs&TF;&Ri-E2{&<+Q5s=R(oly7l`oDyiBtx zj06*_cc~{;G{8Zxj7%1OK{`nvu@~^VCR~j*AJ#1GHvbN5&@LZb#ID)JoMZf@D|yCt zZ?hQ`B9o9Ac|kA8*3=Z&-n@MNhKJ$ZQ}rJsA~$cgWTQ7?4phu8p#I#TUNKt=Q2Qoo z?FZU&kNtU^pZLzM5rmv&ivw185uFs!b8;W(v3PV^{RQUz49$Vi|$2qxdhUvSg~B4Ca4&rwle8{k zGVwh+1KUi|t_FqrnUQ6Y^s8LHg{j}BYfa30IKja$(Bf1JE04uCEU^6@6%cTyS7GOa z{Kg3@F_zuaRNed*L7Tt>vDg>5V;k{8VzPV8ciZ(njF ztRliEe!oyTD%~)U2HOC=n9>^+c}mPMjx|`eJ7^omLoVds=Gw@3?G}3+mRy7*nNQBB zT-eDOdj&Oun?d9@UR+on$=*Y8;2HqftDC0wBqvY@iabF+^LcvLBSyc0Qx(JQLn8x%xPkWWuMO;DLVY%I0=Nyy<}I>)fI60hD&R`rj(z`QS!Ha2Y?8rR#0> z8{my+3_z2xoN}m_3;>LWlB-$CizP9Z;>`;G(i{MYazWyi<#M6JZ<@bc0}jB9Z3_7* zni-|*726glmJ}x6)jOn%OjbVtr-Zc47VUCBr|^mu#SJ5*J*6lZXWBIg$DUG_%U&

Vdewv|EW4h#OVh=`i}%D+md^{I3`l?gkMFWXe@FA6#CXWDO+ zQtc+i;d)E)!l>AGinJ3-nQ|*@!(wZFUMaP9E6cONE`_aE)N?lNDRm=fzWR)A-l=FG zsjBNS6+-6ZzispdL5GT?+R|*}?3-$d{=ACW0#(Bws0w)9E}vA-N)wN(o#w0_xX0ZV zi0FZP-H!)!5gR0@GLhPGSn!QAZbw4ToQifvtx;Yh{xzTLj4IzDsra{m@UG1_enwRW z$lFV9_EW<9g`3W(b*O3eSDnF7Lkx;(3?>r=(UTjrd{=%mQmOkHGTEil>m)~)uEs+D zDml(dd&To=!L73vTbN3fl|BK_m6gO_yh7VcDANSZJvFXy<`O44^1#}1z3*op+pnau zOo_}HDsdx~_7il#JZK`18{Eh2jZ)rfm9-F?{GTSD*Xt^sj4g&@7LD>ArozmtZ z%(1H_*`PIuY-X9)x=YY(gGP3@%XOctYY4DIjy0HDl7Ct&{c7zweGecO9p-aD@2n9R zzu&f&T~&WU&FE9utv-r~Sx0qlF9C6NyxnX~N{0SF7!dNjW4!&~WkoA(saTEtSUg|u_ zgNe*q0y4E*T%Fnwd9LGSmd@RwvGkjH=eAfVVosn2Bp&7Lpqo7x+zsf?3Oz)9q2Nx- z_=)B{8bK{`ovQsD7)j?PSlfC?_t2(QqMbP`8 zqJjPJvz@T8?{qSgLEmwJTRPN)(LfbSGFCQIHOI59OsDp=C5L8uu)YVH8mrofJxG6* zg<@5Qk|-L;%!#GZ66h@1dZ=4S2)ELATX$6sjjSBq5gyKDN?1WvDZ7~8DEu05HLwcm bA_WkaR_qe5@ov>(N>yFDUf=lD*dPA~cl>=D diff --git a/testapp/src/main/kotlin/dev/wvb/testapp/MainActivity.kt b/testapp/src/main/kotlin/dev/wvb/testapp/MainActivity.kt index 4e56642..f09fad0 100644 --- a/testapp/src/main/kotlin/dev/wvb/testapp/MainActivity.kt +++ b/testapp/src/main/kotlin/dev/wvb/testapp/MainActivity.kt @@ -33,7 +33,9 @@ class MainActivity : Activity() { WebViewBundleConfig( protocols = listOf(WebViewBundleProtocol.bundle()), updater = WebViewBundleUpdaterConfig( - remote = WebViewBundleRemoteConfig(endpoint = REMOTE_ENDPOINT), + remote = WebViewBundleRemoteConfig( + baseUrl = intent.getStringExtra("wvbRemoteUrl") ?: REMOTE_ENDPOINT, + ), ), onError = { error -> Log.e(TAG, "handler error", error) }, ), @@ -101,7 +103,7 @@ class MainActivity : Activity() { const val TAG = "WvbTestApp" const val START_URL = "https://testbed.wvb/" - /** The playground remote the e2e testbed spec downloads the `testbed` bundle from. */ + /** Default remote for manual use; E2E supplies wvbRemoteUrl at launch. */ const val REMOTE_ENDPOINT = "https://playground-remote.wvb.dev" } }