diff --git a/.gitignore b/.gitignore
index 585c72d..69167ca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,7 @@ target
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
+
+# Python related files
+__pycache__/
+.venv/
\ No newline at end of file
diff --git a/.idea/caches/deviceStreaming.xml b/.idea/caches/deviceStreaming.xml
new file mode 100644
index 0000000..33049c4
--- /dev/null
+++ b/.idea/caches/deviceStreaming.xml
@@ -0,0 +1,1313 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml
new file mode 100644
index 0000000..91f9558
--- /dev/null
+++ b/.idea/deviceManager.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..6e86672
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..f8ab653
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ "associatedIndex": 0
+}
+
+
+
+
+
+ {
+ "keyToString": {
+ "ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
+ "ModuleVcsDetector.initialDetectionPerformed": "true",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
+ "RunOnceActivity.cidr.known.project.marker": "true",
+ "RunOnceActivity.git.unshallow": "true",
+ "RunOnceActivity.readMode.enableVisualFormatting": "true",
+ "cf.first.check.clang-format": "false",
+ "cidr.known.project.marker": "true",
+ "dart.analysis.tool.window.visible": "false",
+ "git-widget-placeholder": "start-flutter-2",
+ "kotlin-language-version-configured": "true",
+ "last_opened_file_path": "/Users/emmanuel/Code/pp/acho/mobile_app/lib",
+ "project.structure.last.edited": "Project",
+ "project.structure.proportion": "0.0",
+ "project.structure.side.proportion": "0.2",
+ "settings.editor.selected.configurable": "project.propDebugger",
+ "show.migrate.to.gradle.popup": "false"
+ },
+ "keyToStringList": {
+ "com.intellij.ide.scratch.ScratchImplUtil$2/New Scratch File": [
+ "Dart"
+ ]
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1768432606666
+
+
+ 1768432606666
+
+
+
+ 1768652888194
+
+
+
+ 1768652888194
+
+
+
+
+
+
+
+
+
+
+
+
+ file://$PROJECT_DIR$/mobile_app/lib/utils.dart
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Justfile b/Justfile
index 0dc1f7e..cd5adbe 100644
--- a/Justfile
+++ b/Justfile
@@ -1,6 +1,7 @@
import 'scripts/watch.justfile'
import 'scripts/clean.justfile'
import 'scripts/android.justfile'
+import 'scripts/onnx.justfile'
# Alias
diff --git a/inference/Cargo.lock b/inference/Cargo.lock
new file mode 100644
index 0000000..13f4cb8
--- /dev/null
+++ b/inference/Cargo.lock
@@ -0,0 +1,1992 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
+[[package]]
+name = "ahash"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
+dependencies = [
+ "cfg-if",
+ "getrandom 0.3.4",
+ "once_cell",
+ "serde",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
+
+[[package]]
+name = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "base64ct"
+version = "1.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
+
+[[package]]
+name = "bitflags"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
+
+[[package]]
+name = "bumpalo"
+version = "3.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
+
+[[package]]
+name = "castaway"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
+dependencies = [
+ "rustversion",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583"
+dependencies = [
+ "find-msvc-tools",
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "compact_str"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a"
+dependencies = [
+ "castaway",
+ "cfg-if",
+ "itoa",
+ "rustversion",
+ "ryu",
+ "serde",
+ "static_assertions",
+]
+
+[[package]]
+name = "console"
+version = "0.15.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
+dependencies = [
+ "encode_unicode",
+ "libc",
+ "once_cell",
+ "unicode-width",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "console"
+version = "0.16.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4"
+dependencies = [
+ "encode_unicode",
+ "libc",
+ "once_cell",
+ "unicode-width",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "crc32fast"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "darling"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "dary_heap"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06d2e3287df1c007e74221c49ca10a95d557349e54b3a75dc2fb14712c751f04"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "der"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
+dependencies = [
+ "pem-rfc7468",
+ "zeroize",
+]
+
+[[package]]
+name = "derive_builder"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
+dependencies = [
+ "derive_builder_macro",
+]
+
+[[package]]
+name = "derive_builder_core"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "derive_builder_macro"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
+dependencies = [
+ "derive_builder_core",
+ "syn",
+]
+
+[[package]]
+name = "dirs"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "encode_unicode"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
+
+[[package]]
+name = "errno"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "esaxx-rs"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d817e038c30374a4bcb22f94d0a8a0e216958d4c3dcde369b1439fec4bdda6e6"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
+
+[[package]]
+name = "flate2"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasip2",
+]
+
+[[package]]
+name = "hf-hub"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "629d8f3bbeda9d148036d6b0de0a3ab947abd08ce90626327fc3547a49d59d97"
+dependencies = [
+ "dirs",
+ "http",
+ "indicatif 0.17.11",
+ "libc",
+ "log",
+ "rand",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "ureq 2.12.1",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "hmac-sha256"
+version = "1.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0f0ae375a85536cac3a243e3a9cda80a47910348abdea7e2c22f8ec556d586d"
+
+[[package]]
+name = "http"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
+dependencies = [
+ "bytes",
+ "itoa",
+]
+
+[[package]]
+name = "httparse"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+
+[[package]]
+name = "icu_collections"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
+dependencies = [
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
+
+[[package]]
+name = "icu_properties"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec"
+dependencies = [
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af"
+
+[[package]]
+name = "icu_provider"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "idna"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "indicatif"
+version = "0.17.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235"
+dependencies = [
+ "console 0.15.11",
+ "number_prefix",
+ "portable-atomic",
+ "unicode-width",
+ "web-time",
+]
+
+[[package]]
+name = "indicatif"
+version = "0.18.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88"
+dependencies = [
+ "console 0.16.2",
+ "portable-atomic",
+ "unicode-width",
+ "unit-prefix",
+ "web-time",
+]
+
+[[package]]
+name = "inference"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "ndarray",
+ "ort",
+ "tokenizers",
+]
+
+[[package]]
+name = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
+
+[[package]]
+name = "js-sys"
+version = "0.3.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.180"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
+
+[[package]]
+name = "libredox"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
+dependencies = [
+ "bitflags",
+ "libc",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
+
+[[package]]
+name = "litemap"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
+
+[[package]]
+name = "log"
+version = "0.4.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
+[[package]]
+name = "lzma-rust2"
+version = "0.15.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1670343e58806300d87950e3401e820b519b9384281bbabfb15e3636689ffd69"
+
+[[package]]
+name = "macro_rules_attribute"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520"
+dependencies = [
+ "macro_rules_attribute-proc_macro",
+ "paste",
+]
+
+[[package]]
+name = "macro_rules_attribute-proc_macro"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30"
+
+[[package]]
+name = "matrixmultiply"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08"
+dependencies = [
+ "autocfg",
+ "rawpointer",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
+[[package]]
+name = "monostate"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3341a273f6c9d5bef1908f17b7267bbab0e95c9bf69a0d4dcf8e9e1b2c76ef67"
+dependencies = [
+ "monostate-impl",
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "monostate-impl"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4db6d5580af57bf992f59068d4ea26fd518574ff48d7639b255a36f9de6e7e9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "native-tls"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
+dependencies = [
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
+]
+
+[[package]]
+name = "ndarray"
+version = "0.17.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "520080814a7a6b4a6e9070823bb24b4531daac8c4627e08ba5de8c5ef2f2752d"
+dependencies = [
+ "matrixmultiply",
+ "num-complex",
+ "num-integer",
+ "num-traits",
+ "portable-atomic",
+ "portable-atomic-util",
+ "rawpointer",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "number_prefix"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "onig"
+version = "6.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0"
+dependencies = [
+ "bitflags",
+ "libc",
+ "once_cell",
+ "onig_sys",
+]
+
+[[package]]
+name = "onig_sys"
+version = "69.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7f86c6eef3d6df15f23bcfb6af487cbd2fed4e5581d58d5bf1f5f8b7f6727dc"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
+
+[[package]]
+name = "openssl"
+version = "0.10.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.111"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
+name = "ort"
+version = "2.0.0-rc.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5df903c0d2c07b56950f1058104ab0c8557159f2741782223704de9be73c3c"
+dependencies = [
+ "ndarray",
+ "ort-sys",
+ "smallvec",
+ "tracing",
+ "ureq 3.1.4",
+]
+
+[[package]]
+name = "ort-sys"
+version = "2.0.0-rc.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06503bb33f294c5f1ba484011e053bfa6ae227074bdb841e9863492dc5960d4b"
+dependencies = [
+ "hmac-sha256",
+ "lzma-rust2",
+ "ureq 3.1.4",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "pem-rfc7468"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
+dependencies = [
+ "base64ct",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
+name = "portable-atomic"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
+dependencies = [
+ "portable-atomic",
+]
+
+[[package]]
+name = "potential_utf"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "rand"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
+dependencies = [
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
+dependencies = [
+ "getrandom 0.3.4",
+]
+
+[[package]]
+name = "rawpointer"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
+
+[[package]]
+name = "rayon"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-cond"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2964d0cf57a3e7a06e8183d14a8b527195c706b7983549cd5462d5aa3747438f"
+dependencies = [
+ "either",
+ "itertools",
+ "rayon",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
+dependencies = [
+ "getrandom 0.2.17",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
+
+[[package]]
+name = "ring"
+version = "0.17.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.17",
+ "libc",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustix"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustls"
+version = "0.23.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b"
+dependencies = [
+ "log",
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
+dependencies = [
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.103.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "ryu"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
+
+[[package]]
+name = "schannel"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "security-framework"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.149"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "socks"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b"
+dependencies = [
+ "byteorder",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "spm_precompiled"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5851699c4033c63636f7ea4cf7b7c1f1bf06d0cc03cfb42e711de5a5c46cf326"
+dependencies = [
+ "base64 0.13.1",
+ "nom",
+ "serde",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "syn"
+version = "2.0.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
+dependencies = [
+ "fastrand",
+ "getrandom 0.3.4",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tokenizers"
+version = "0.22.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b238e22d44a15349529690fb07bd645cf58149a1b1e44d6cb5bd1641ff1a6223"
+dependencies = [
+ "ahash",
+ "aho-corasick",
+ "compact_str",
+ "dary_heap",
+ "derive_builder",
+ "esaxx-rs",
+ "getrandom 0.3.4",
+ "hf-hub",
+ "indicatif 0.18.3",
+ "itertools",
+ "log",
+ "macro_rules_attribute",
+ "monostate",
+ "onig",
+ "paste",
+ "rand",
+ "rayon",
+ "rayon-cond",
+ "regex",
+ "regex-syntax",
+ "serde",
+ "serde_json",
+ "spm_precompiled",
+ "thiserror",
+ "unicode-normalization-alignments",
+ "unicode-segmentation",
+ "unicode_categories",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
+dependencies = [
+ "pin-project-lite",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
+
+[[package]]
+name = "unicode-normalization-alignments"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43f613e4fa046e69818dd287fdc4bc78175ff20331479dab6e1b0f98d57062de"
+dependencies = [
+ "smallvec",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
+[[package]]
+name = "unicode-width"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
+
+[[package]]
+name = "unicode_categories"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
+
+[[package]]
+name = "unit-prefix"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "ureq"
+version = "2.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
+dependencies = [
+ "base64 0.22.1",
+ "flate2",
+ "log",
+ "once_cell",
+ "rustls",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "socks",
+ "url",
+ "webpki-roots 0.26.11",
+]
+
+[[package]]
+name = "ureq"
+version = "3.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a"
+dependencies = [
+ "base64 0.22.1",
+ "der",
+ "log",
+ "native-tls",
+ "percent-encoding",
+ "rustls-pki-types",
+ "socks",
+ "ureq-proto",
+ "utf-8",
+ "webpki-root-certs",
+]
+
+[[package]]
+name = "ureq-proto"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f"
+dependencies = [
+ "base64 0.22.1",
+ "http",
+ "httparse",
+ "log",
+]
+
+[[package]]
+name = "url"
+version = "2.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasip2"
+version = "1.0.2+wasi-0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki-root-certs"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.26.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
+dependencies = [
+ "webpki-roots 1.0.5",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
+dependencies = [
+ "windows-link",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm 0.53.1",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+
+[[package]]
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+
+[[package]]
+name = "writeable"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
+
+[[package]]
+name = "yoke"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
+dependencies = [
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+
+[[package]]
+name = "zerotrie"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zmij"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439"
diff --git a/inference/Cargo.toml b/inference/Cargo.toml
new file mode 100644
index 0000000..e1b0768
--- /dev/null
+++ b/inference/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "inference"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ndarray = "0.17.2"
+tokenizers = { version = "0.22.2", default-features = false, features = ["esaxx_fast", "indicatif", "onig", "progressbar", "hf-hub", "http"] }
+ort = { version = "2.0.0-rc.11" }
+anyhow = "1.0.100"
+
+[target.'cfg(target_os = "android")'.dependencies]
+ort = { version = "2.0.0-rc.11", default-features = false, features = ["ndarray", "copy-dylibs", "std", "tls-native"] }
diff --git a/inference/src/lib.rs b/inference/src/lib.rs
new file mode 100644
index 0000000..56d32e5
--- /dev/null
+++ b/inference/src/lib.rs
@@ -0,0 +1,79 @@
+use std::env;
+use anyhow::Result;
+use tokenizers::{Encoding, Tokenizer};
+
+use ort::session::Session;
+use ort::session::builder::GraphOptimizationLevel;
+
+type EncodingArray = ndarray::Array2;
+type InputIds = ndarray::Array2;
+type AttentionMask = ndarray::Array2;
+pub type Embeddings = ndarray::Array2;
+
+
+enum EncodingType {
+ Ids,
+ AttentionMask
+}
+
+pub fn load_artifacts() -> Result<(Tokenizer, Session)> {
+ let mut tokenizer = Tokenizer::from_pretrained("abdulmatinomotoso/bge-finetuned", None)
+ .expect("Failed to load tokenizer.");
+ let padding = Some(tokenizers::PaddingParams {
+ strategy: tokenizers::PaddingStrategy::BatchLongest,
+ direction: tokenizers::PaddingDirection::Right,
+ pad_to_multiple_of: None,
+ pad_id: 1,
+ pad_type_id: 0,
+ pad_token: "".to_string(),
+ });
+ tokenizer.with_padding(padding);
+
+ let model_path = env::var("ACHO_MODEL_PATH")
+ .unwrap_or("weights/model.onnx".to_string());
+ let session = Session::builder()?
+ .with_optimization_level(GraphOptimizationLevel::Level1)?
+ .with_intra_threads(4)?
+ .commit_from_file(&model_path).expect("Failed to load the ONNX model.");
+ Ok((tokenizer, session))
+}
+
+fn get_encoding_array(encodings: &Vec, encoding_type: EncodingType) -> Result {
+ let extract: fn(&Encoding) -> &[u32] = match encoding_type {
+ EncodingType::Ids => |e: &Encoding| e.get_ids(),
+ EncodingType::AttentionMask => |e: &Encoding| e.get_attention_mask(),
+ };
+
+ let nrows = encodings.len();
+ let ncols = extract(&encodings[0]).len();
+ let vec_matrix: Vec = encodings.iter()
+ .flat_map(|e| extract(e).iter().map(|&x| x as i64).collect::>())
+ .collect();
+ Ok(
+ ndarray::Array2::from_shape_vec((nrows, ncols), vec_matrix)
+ .expect("Failed to create Array2D from encodings.")
+ )
+}
+
+fn tokenize(texts: &[&str], tokenizer: &Tokenizer) -> Result<(InputIds, AttentionMask)> {
+ let encodings = tokenizer.encode_batch(texts.to_vec(), true)
+ .expect("Tokenization failed.");
+
+ let input_ids = get_encoding_array(&encodings, EncodingType::Ids)?;
+ let attention_mask = get_encoding_array(&encodings, EncodingType::AttentionMask)?;
+ Ok((input_ids, attention_mask))
+}
+
+pub fn run_inference<'a>(text: &[&str], model: &'a mut Session, tokenizer: &Tokenizer) -> Result{
+ let (tokens, attn_mask) = tokenize(text, tokenizer)?;
+ let token_input_value = ort::value::Tensor::from_array(tokens)?;
+ let attn_mask_input_value = ort::value::Tensor::from_array(attn_mask)?;
+ let dense: ort::session::SessionOutputs<'_>= model.run(
+ ort::inputs!["input_ids" => token_input_value, "attention_mask" => attn_mask_input_value]
+ ).expect("Embedding model inference failed.");
+
+ let dense_embeddings = (&dense["dense_embeddings"]).try_extract_array::()?
+ .into_dimensionality::()
+ .expect("Failed to convert embeddings to 2D array.");
+ Ok(dense_embeddings.to_owned())
+}
\ No newline at end of file
diff --git a/inference/src/main.rs b/inference/src/main.rs
new file mode 100644
index 0000000..d971edb
--- /dev/null
+++ b/inference/src/main.rs
@@ -0,0 +1,17 @@
+use anyhow::Result;
+
+use inference::{load_artifacts, Embeddings, run_inference};
+
+fn main() -> Result<()> {
+ let (tokenizer, mut model) = load_artifacts()?;
+ let texts = vec![
+ "What is your name",
+ "Ki lo ruko e?",
+ "Ki lo je losan?"
+ ];
+ let all_embeddings: Embeddings = run_inference(&texts, &mut model, &tokenizer)?;
+ let similarity_matrix = all_embeddings.dot(&all_embeddings.t());
+ println!("Similarity matrix:\n{:?}", similarity_matrix);
+
+ Ok(())
+}
\ No newline at end of file
diff --git a/mobile_app/README.md b/mobile_app/README.md
index 1dfe888..d2865ca 100644
--- a/mobile_app/README.md
+++ b/mobile_app/README.md
@@ -1,16 +1,17 @@
# mobile_app
-A new Flutter project.
+To run acho on your machine:
-## Getting Started
-
-This project is a starting point for a Flutter application.
-
-A few resources to get you started if this is your first Flutter project:
-
-- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
-- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
-
-For help getting started with Flutter development, view the
-[online documentation](https://docs.flutter.dev/), which offers tutorials,
-samples, guidance on mobile development, and a full API reference.
+- Install Android Studio
+- Install Java 17
+ - Configure flutter to use Java 17 because bundled Java 21 has bugs with the latest mac version (Tahoe) with
+ For Mac:
+ ```
+ flutter config --jdk-dir="/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home"
+ ```
+
+ Path may differ for windows
+- Configure a device (Android 16.0) in Tools > Device Manager. and start with the play button ( ▶)
+
+- `cd mobile_app`
+- `flutter run`
\ No newline at end of file
diff --git a/mobile_app/android/app/build.gradle b/mobile_app/android/app/build.gradle
index 031a0ed..ba9fb7b 100644
--- a/mobile_app/android/app/build.gradle
+++ b/mobile_app/android/app/build.gradle
@@ -29,8 +29,8 @@ android {
ndkVersion = flutter.ndkVersion
compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
}
defaultConfig {
@@ -44,6 +44,11 @@ android {
versionName = flutterVersionName
}
+ kotlinOptions {
+ // Change this from "1.8" to "17"
+ jvmTarget = "17"
+ }
+
buildTypes {
release {
// TODO: Add your own signing config for the release build.
diff --git a/mobile_app/android/app/src/main/AndroidManifest.xml b/mobile_app/android/app/src/main/AndroidManifest.xml
index ff78bf3..bb3e8cc 100644
--- a/mobile_app/android/app/src/main/AndroidManifest.xml
+++ b/mobile_app/android/app/src/main/AndroidManifest.xml
@@ -1,4 +1,5 @@
+
files = [];
+ FileApp({super.key, required this.files});
+
+ @override
+ Widget build(BuildContext context) {
+ //TODO:switch to class, so we can access more attributes outside of title
+
+ return SizedBox.shrink(
+ child: Column(
+ children: [
+ const ListTile(
+ leading: Text(
+ "files",
+ style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
+ )),
+ Column(
+ children: List.generate(files.length, (int index) {
+ String fileName = files[index].path.split("/").last;
+ String fileType = files[index].path.split("/").last.split(".").last;
+
+ Map fileIcon = {"pdf": Icon(Icons.picture_as_pdf)};
+
+ return ListTile(
+ leading: fileIcon[fileType] ?? Icon(Icons.book),
+ trailing: Icon(Icons.chevron_right),
+ //TODO: style to make borders visible
+ onTap: () {
+ PdfScanner().openFile(files[index]);
+ //TODO: Handle click, popular search bar with text controller
+ },
+ title: Text(fileName), // Display results from search
+ );
+ })),
+ ],
+ ));
+ }
+}
diff --git a/mobile_app/lib/home.dart b/mobile_app/lib/home.dart
new file mode 100644
index 0000000..7509d9f
--- /dev/null
+++ b/mobile_app/lib/home.dart
@@ -0,0 +1,181 @@
+import 'dart:math';
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/foundation.dart';
+import 'package:gap/gap.dart';
+import 'package:mobile_app/utils.dart';
+import 'package:flutter_tantivy/flutter_tantivy.dart';
+
+class HomeApp extends StatefulWidget {
+ List files = [];
+ HomeApp({super.key, required this.files});
+
+ @override
+ State createState() => _HomeAppState();
+}
+
+class _HomeAppState extends State {
+ List matchedDocuments = [];
+ List searchedItems = [];
+
+ @override
+ Widget build(BuildContext context) {
+ double width = MediaQuery.sizeOf(context).width;
+
+ void _showDocumentDetails(SearchResult result) {
+ showDialog(
+ context: context,
+ builder: (context) => AlertDialog(
+ title: const Text("Document Preview"),
+ content: SingleChildScrollView(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text(
+ "Matching Chunk:",
+ style: TextStyle(
+ fontWeight: FontWeight.bold, color: Colors.blue),
+ ),
+ const Gap(10),
+ Text(
+ result.doc.text, // The full text chunk
+ style: const TextStyle(fontSize: 15, height: 1.5),
+ ),
+ ],
+ ),
+ ),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.pop(context),
+ child: const Text("Close"),
+ ),
+ ElevatedButton(
+ onPressed: () {
+ ///TODO: Integrate with PDF viewer to jump to specific page
+ Navigator.pop(context);
+ },
+ child: const Text("Open Full PDF"),
+ ),
+ ],
+ ),
+ );
+ }
+
+ return SizedBox.shrink(
+ child: Column(children: [
+ SizedBox(
+ height: 70,
+ width: width - 40,
+ child: SearchBar(
+ leading: const Icon(Icons.search),
+ hintText: "Search...",
+ shape: WidgetStateProperty.all(
+ const RoundedRectangleBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(3)), // This makes the corners square
+ ),
+ ),
+
+ backgroundColor: WidgetStateProperty.all(Colors.grey[200]),
+ elevation: WidgetStateProperty.all(0), // Flat style
+
+ onChanged: (text) {},
+ onSubmitted: (text) async {
+ final List docs = await compute(findMatch, text);
+ //TODO: Handle enter key press,
+ //TODO: similar to above depending on latency we may just use this
+ saveSearchHistory(text);
+ setState(() {
+ matchedDocuments = docs;
+ });
+
+ List _searchedItems = await getSearchHistory();
+ setState(() {
+ searchedItems = _searchedItems;
+ });
+ },
+ ),
+ ),
+ const ListTile(
+ leading: Text(
+ "Recent Searches",
+ style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
+ )),
+ Row(
+ children: List.generate(min(searchedItems.length, 3), (int index) {
+ return Expanded(
+ child: OutlinedButton(
+ //TODO: style to make borders visible
+ style: OutlinedButton.styleFrom(
+ shape: const StadiumBorder(), // Makes it look like a pill/chip
+ side: BorderSide(color: Colors.grey[300]!),
+ ),
+ onPressed: () async {
+ final List docs =
+ await compute(findMatch, searchedItems[index]);
+
+ setState(() {
+ matchedDocuments = docs;
+ });
+ },
+ child: Text(searchedItems[index]), // Display results from search
+ ));
+ })),
+ matchedDocuments.length >= 1
+ ? const ListTile(
+ leading: Text(
+ "Matching Documents",
+ style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
+ ))
+ : Expanded(
+ child: Column(
+ children: [
+ const ListTile(
+ leading: Text(
+ "Files",
+ style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
+ )),
+ Column(
+ children: List.generate(widget.files.length, (int index) {
+ String fileName = widget.files[index].path.split("/").last;
+ String fileType =
+ widget.files[index].path.split("/").last.split(".").last;
+
+ Map fileIcon = {
+ "pdf": Icon(Icons.picture_as_pdf)
+ };
+
+ return ListTile(
+ leading: fileIcon[fileType] ?? Icon(Icons.book),
+ trailing: Icon(Icons.chevron_right),
+ //TODO: style to make borders visible
+ onTap: () {
+ PdfScanner().openFile(widget.files[index]);
+ //TODO: Handle click, popular search bar with text controller
+ },
+ title: Text(fileName), // Display results from search
+ );
+ })),
+ ],
+ )),
+ Expanded(
+ child: ListView.builder(
+ itemCount: matchedDocuments.length,
+ itemBuilder: (context, index) {
+ final result = matchedDocuments[index];
+
+ return ListTile(
+ leading: const Icon(Icons.picture_as_pdf),
+ onTap: () {
+ _showDocumentDetails(result);
+ },
+ trailing: const Icon(Icons.chevron_right),
+ title: Text(result.doc.text
+ .substring(0, 50)), // Display results from search
+ );
+ })),
+ ]));
+ }
+}
diff --git a/mobile_app/lib/main.dart b/mobile_app/lib/main.dart
index 53581cf..abcdf67 100644
--- a/mobile_app/lib/main.dart
+++ b/mobile_app/lib/main.dart
@@ -1,25 +1,112 @@
+import 'dart:io';
+
import 'package:flutter/material.dart';
-import 'package:mobile_app/src/rust/api/simple.dart';
-import 'package:mobile_app/src/rust/frb_generated.dart';
+import 'package:flutter/services.dart';
+import 'package:mobile_app/settings.dart';
+import 'package:flutter/foundation.dart';
+import 'package:mobile_app/home.dart';
+import 'package:mobile_app/file.dart';
+import 'package:flutter_tantivy/flutter_tantivy.dart';
+import 'package:path_provider/path_provider.dart';
+import 'package:mobile_app/storage.dart';
+import 'package:permission_handler/permission_handler.dart';
+import 'package:mobile_app/utils.dart';
Future main() async {
await RustLib.init();
+ WidgetsFlutterBinding
+ .ensureInitialized(); // Ensure plugin services are initialized
+
+ RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
+ BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
+ await Log.init();
+
+ var status = await Permission.manageExternalStorage.status;
+
+ if (!status.isGranted) {
+ status = await Permission.manageExternalStorage.request();
+ }
+
+ status = await Permission.manageExternalStorage.status;
+ Log.logger.i("Permission for external storage: $status");
+
+ final directory = await getApplicationDocumentsDirectory();
+ final indexPath = '${directory.path}/tantivy_index';
+ initTantivy(dirPath: indexPath);
+ Log.logger.i("Index Path $indexPath");
+
runApp(const MyApp());
}
-class MyApp extends StatelessWidget {
+class MyApp extends StatefulWidget {
const MyApp({super.key});
+ @override
+ MyAppState createState() => MyAppState();
+}
+
+class MyAppState extends State {
+ PageController pageController = PageController();
+ List folders = [];
+
+ int selectIndex = 0;
+ void onPageChanged(int index) {
+ setState(() {
+ selectIndex = index;
+ });
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ _loadPdfs();
+ _indexDocuments();
+ }
+
+ void _loadPdfs() async {
+ PdfScanner scanner = PdfScanner();
+ List files =
+ await scanner.getAllPdfs(); // This might block UI if not careful
+ setState(() {
+ folders = files;
+ });
+ }
+
+ void _indexDocuments() async {
+ PdfScanner scanner = PdfScanner();
+ scanner.indexPdfFiles();
+ }
+
+ void onItemTap(int selectedItems) {
+ pageController.jumpToPage(selectedItems);
+ }
+
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
- appBar: AppBar(title: const Text('flutter_rust_bridge quickstart')),
- body: Center(
- child: Text(
- 'Action: Call Rust `greet("Tom")`\nResult: `${greet(name: "Tom")}`'),
- ),
- ),
+ appBar: AppBar(title: const Text('Acho')),
+ body: PageView(
+ children: [HomeApp(files: folders), SettingsApp()],
+ controller: pageController,
+ onPageChanged: onPageChanged,
+ ),
+ bottomNavigationBar: BottomNavigationBar(
+ onTap: onItemTap,
+ selectedItemColor: Colors.brown,
+ items: [
+ BottomNavigationBarItem(
+ backgroundColor: Colors.red,
+ label: 'Home',
+ icon: Icon(Icons.home_filled),
+ activeIcon: HomeApp(files: []),
+ ),
+ const BottomNavigationBarItem(
+ label: 'Settings',
+ icon: Icon(Icons.settings),
+ activeIcon: SettingsApp(),
+ )
+ ])),
);
}
}
diff --git a/mobile_app/lib/settings.dart b/mobile_app/lib/settings.dart
new file mode 100644
index 0000000..2feab11
--- /dev/null
+++ b/mobile_app/lib/settings.dart
@@ -0,0 +1,84 @@
+import 'package:flutter/material.dart';
+
+class SettingsApp extends StatelessWidget {
+ const SettingsApp({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ // TODO: implement build
+ return const SizedBox.shrink(
+ child: Column(
+ children: [
+ ListTile(
+ leading: Text(
+ "Search Settings",
+ style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
+ ),
+ // onTap:,
+ ),
+ ListTile(
+ leading: Icon(Icons.folder),
+ title: Text("Folder"),
+ trailing: Icon(Icons.chevron_right_sharp),
+ // onTap:,
+ ),
+ ListTile(
+ leading: Icon(Icons.change_circle_sharp),
+ title: Text("Re-index PDFs"),
+ trailing: Icon(Icons.chevron_right_sharp),
+ // onTap:,
+ ),
+ Divider(),
+ ListTile(
+ leading: Text(
+ "Display",
+ style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
+ ),
+ ),
+ Divider(),
+ ListTile(
+ leading: Text(
+ "Storage",
+ style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
+ ),
+ // onTap:,
+ ),
+ ListTile(
+ leading: Text(
+ "Index Size",
+ style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
+ ),
+ // onTap:,
+ ),
+ ListTile(
+ leading: Icon(Icons.delete_forever_rounded),
+ title: Text("Clear search history"),
+ trailing: Icon(Icons.chevron_right),
+ // onTap:,
+ ),
+ Divider(),
+ ListTile(
+ leading: Text(
+ "About",
+ style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
+ ),
+ // onTap:,
+ ),
+ ListTile(
+ leading: Text(
+ "Version 1.0.0",
+ style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
+ ),
+ // onTap:,
+ ),
+ ListTile(
+ leading: Text(
+ "Help & Feedback",
+ style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
+ ),
+ // onTap:,
+ )
+ ],
+ ));
+ }
+}
diff --git a/mobile_app/lib/src/rust/api/keyword_search.dart b/mobile_app/lib/src/rust/api/keyword_search.dart
new file mode 100644
index 0000000..441be26
--- /dev/null
+++ b/mobile_app/lib/src/rust/api/keyword_search.dart
@@ -0,0 +1,26 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.11.1.
+
+// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
+
+import '../frb_generated.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
+
+// Rust type: RustOpaqueMoi>
+abstract class PathBuf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class SearchFn implements RustOpaqueInterface {
+ PathBuf get pathToIndex;
+
+ set pathToIndex(PathBuf pathToIndex);
+
+ Future deleteIndex();
+
+ Future ingestPdfDir({required PathBuf filePath});
+
+ // HINT: Make it `#[frb(sync)]` to let it become the default constructor of Dart class.
+ /// Creates a new SearchFn instance and initializes the index on disk
+ static Future newInstance({required PathBuf path}) =>
+ RustLib.instance.api.crateApiKeywordSearchSearchFnNew(path: path);
+}
diff --git a/mobile_app/lib/src/rust/lib.dart b/mobile_app/lib/src/rust/lib.dart
new file mode 100644
index 0000000..5890449
--- /dev/null
+++ b/mobile_app/lib/src/rust/lib.dart
@@ -0,0 +1,25 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.11.1.
+
+// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
+
+import 'frb_generated.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
+
+// Rust type: RustOpaqueMoi>>
+abstract class AHashMapStringFacet implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi >>>
+abstract class ArcIndex implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi , usize) >>>
+abstract class IndexMapStringVecStringUsize implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class Path implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class Result implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class Value implements RustOpaqueInterface {}
diff --git a/mobile_app/lib/src/rust/third_party/seekstorm/commit.dart b/mobile_app/lib/src/rust/third_party/seekstorm/commit.dart
new file mode 100644
index 0000000..59093da
--- /dev/null
+++ b/mobile_app/lib/src/rust/third_party/seekstorm/commit.dart
@@ -0,0 +1,31 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.11.1.
+
+// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
+
+import '../../frb_generated.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
+
+// These functions are ignored (category: IgnoreBecauseNotAllowedOwner): `commit`
+
+abstract class Commit {
+ /// Commit moves indexed documents from the intermediate uncompressed data structure (array lists/HashMap, queryable by realtime search) in RAM
+ /// to the final compressed data structure (roaring bitmap) on Mmap or disk -
+ /// which is persistent, more compact, with lower query latency and allows search with realtime=false.
+ /// Commit is invoked automatically each time 64K documents are newly indexed as well as on close_index (e.g. server quit).
+ /// There is no way to prevent this automatic commit by not manually invoking it.
+ /// But commit can also be invoked manually at any time at any number of newly indexed documents.
+ /// commit is a **hard commit** for persistence on disk. A **soft commit** for searchability
+ /// is invoked implicitly with every index_doc,
+ /// i.e. the document can immediately searched and included in the search results
+ /// if it matches the query AND the query paramter realtime=true is enabled.
+ /// **Use commit with caution, as it is an expensive operation**.
+ /// **Usually, there is no need to invoke it manually**, as it is invoked automatically every 64k documents and when the index is closed with close_index.
+ /// Before terminating the program, always call close_index (commit), otherwise all documents indexed since last (manual or automatic) commit are lost.
+ /// There are only 2 reasons that justify a manual commit:
+ /// 1. if you want to search newly indexed documents without using realtime=true for search performance reasons or
+ /// 2. if after indexing new documents there won't be more documents indexed (for some time),
+ /// so there won't be (soon) a commit invoked automatically at the next 64k threshold or close_index,
+ /// but you still need immediate persistence guarantees on disk to protect against data loss in the event of a crash.
+ Future commit();
+}
diff --git a/mobile_app/lib/src/rust/third_party/seekstorm/geo_search.dart b/mobile_app/lib/src/rust/third_party/seekstorm/geo_search.dart
new file mode 100644
index 0000000..9447d2b
--- /dev/null
+++ b/mobile_app/lib/src/rust/third_party/seekstorm/geo_search.dart
@@ -0,0 +1,52 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.11.1.
+
+// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
+
+import '../../frb_generated.dart';
+import 'index.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
+import 'search.dart';
+
+/// encode 2D-coordinate (lat/lon) into 64-bit Morton code
+/// This method is lossy/quantized as two f64 coordinate values are mapped to a single u64 Morton code!
+/// The z-value of a point in multidimensions is simply calculated by interleaving the binary representations of its coordinate values.
+Future encodeMorton2D({required List point}) =>
+ RustLib.instance.api.seekstormGeoSearchEncodeMorton2D(point: point);
+
+/// decode 64-bit Morton code into 2D-coordinate (lat/lon)
+/// This method is lossy/quantized as a single u64 Morton code is converted to two f64 coordinate values!
+Future decodeMorton2D({required BigInt code}) =>
+ RustLib.instance.api.seekstormGeoSearchDecodeMorton2D(code: code);
+
+/// Comparison of the distances between two morton encoded positions and a base position
+Future mortonOrdering(
+ {required BigInt morton1,
+ required BigInt morton2,
+ required List basePoint,
+ required SortOrder order}) =>
+ RustLib.instance.api.seekstormGeoSearchMortonOrdering(
+ morton1: morton1, morton2: morton2, basePoint: basePoint, order: order);
+
+/// calculates distance in kilometers or miles between two 2D-coordinates using Euclidian distance (Pythagoras theorem) with Equirectangular approximation.
+Future euclidianDistance(
+ {required List point1,
+ required List point2,
+ required DistanceUnit unit}) =>
+ RustLib.instance.api.seekstormGeoSearchEuclidianDistance(
+ point1: point1, point2: point2, unit: unit);
+
+/// Converts a Point and a distance radius into a range of morton_codes for geo search range filtering.
+/// The conversion is lossy due to coordinate to Morton code rounding errors and Equirectangular approximation of Euclidian distance.
+Future pointDistanceToMortonRange(
+ {required List point,
+ required double distance,
+ required DistanceUnit unit}) =>
+ RustLib.instance.api.seekstormGeoSearchPointDistanceToMortonRange(
+ point: point, distance: distance, unit: unit);
+
+// Rust type: RustOpaqueMoi>
+abstract class Ordering implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class RangeU64 implements RustOpaqueInterface {}
diff --git a/mobile_app/lib/src/rust/third_party/seekstorm/highlighter.dart b/mobile_app/lib/src/rust/third_party/seekstorm/highlighter.dart
new file mode 100644
index 0000000..cb10c4a
--- /dev/null
+++ b/mobile_app/lib/src/rust/third_party/seekstorm/highlighter.dart
@@ -0,0 +1,89 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.11.1.
+
+// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
+
+import '../../frb_generated.dart';
+import '../../lib.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
+
+// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `Fragment`
+// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `compose`, `fmt`, `fmt`, `name`, `schemas`
+
+/// Returns the Highlighter object used as get_document parameter for highlighting fields in documents
+Future highlighter(
+ {required ArcIndex indexArc,
+ required List highlights,
+ required List queryTermsVec}) =>
+ RustLib.instance.api.seekstormHighlighterHighlighter(
+ indexArc: indexArc,
+ highlights: highlights,
+ queryTermsVec: queryTermsVec);
+
+// Rust type: RustOpaqueMoi>
+abstract class Highlighter implements RustOpaqueInterface {}
+
+/// Specifies the number and size of fragments (snippets, summaries) to generate from each specified field to provide a "keyword in context" (KWIC) functionality.
+/// With highlight_markup the matching query terms within the fragments can be highlighted with HTML markup.
+class Highlight {
+ /// Specifies the field from which the fragments (snippets, summaries) are created.
+ final String field;
+
+ /// Allows to specifiy multiple highlight result fields from the same source field, leaving the original field intact,
+ /// Default: if name is empty then field is used instead, i.e the original field is overwritten with the highlight.
+ final String name;
+
+ /// If 0/default then return the full original text without fragmenting.
+ final BigInt fragmentNumber;
+
+ /// Specifies the length of a highlight fragment.
+ /// The default 0 returns the full original text without truncating, but still with highlighting if highlight_markup is enabled.
+ final BigInt fragmentSize;
+
+ /// if true, the matching query terms within the fragments are highlighted with HTML markup **\term\<\/b\>**.
+ final bool highlightMarkup;
+
+ /// Specifies the markup tags to insert **before** each highlighted term (e.g. \"\\" or \"\\"). This can be any string, but is most often an HTML or XML tag.
+ /// Only used when **highlight_markup** is set to true.
+ final String preTags;
+
+ /// Specifies the markup tags to insert **after** each highlighted term. (e.g. \"\<\/b\>\" or \"\<\/em\>\"). This can be any string, but is most often an HTML or XML tag.
+ /// Only used when **highlight_markup** is set to true.
+ final String postTags;
+
+ const Highlight({
+ required this.field,
+ required this.name,
+ required this.fragmentNumber,
+ required this.fragmentSize,
+ required this.highlightMarkup,
+ required this.preTags,
+ required this.postTags,
+ });
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormHighlighterHighlightDefault();
+
+ @override
+ int get hashCode =>
+ field.hashCode ^
+ name.hashCode ^
+ fragmentNumber.hashCode ^
+ fragmentSize.hashCode ^
+ highlightMarkup.hashCode ^
+ preTags.hashCode ^
+ postTags.hashCode;
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is Highlight &&
+ runtimeType == other.runtimeType &&
+ field == other.field &&
+ name == other.name &&
+ fragmentNumber == other.fragmentNumber &&
+ fragmentSize == other.fragmentSize &&
+ highlightMarkup == other.highlightMarkup &&
+ preTags == other.preTags &&
+ postTags == other.postTags;
+}
diff --git a/mobile_app/lib/src/rust/third_party/seekstorm/index.dart b/mobile_app/lib/src/rust/third_party/seekstorm/index.dart
new file mode 100644
index 0000000..91550b6
--- /dev/null
+++ b/mobile_app/lib/src/rust/third_party/seekstorm/index.dart
@@ -0,0 +1,857 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.11.1.
+
+// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
+
+import '../../frb_generated.dart';
+import '../../lib.dart';
+import 'highlighter.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
+import 'search.dart';
+
+// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `BlockObjectIndex`, `IndexedField`, `LevelIndex`, `NgramSet`, `NonUniquePostingListObjectQuery`, `NonUniqueTermObject`, `PostingListObject0`, `PostingListObjectIndex`, `PostingListObjectQuery`, `QueueObject`, `ResultFacet`, `SegmentIndex`, `SegmentLevel0`, `Shard`, `TermObject`
+// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`
+// These functions are ignored (category: IgnoreBecauseNotAllowedOwner): `close`, `delete_document`, `delete_documents_by_query`, `delete_documents`, `index_document_2`, `index_document_shard`, `index_document`, `index_documents`, `update_document`, `update_documents`
+// These functions are ignored (category: IgnoreBecauseOwnerTyShouldIgnore): `default`, `default`, `default`, `default`, `default`, `default`, `default`, `default`
+
+/// Get the version of the SeekStorm search library
+Future version() => RustLib.instance.api.seekstormIndexVersion();
+
+/// Create index in RAM.
+/// Inner data structures for create index and open_index
+/// * `index_path` - index path.
+/// * `meta` - index meta object.
+/// * `schema` - schema.
+/// * `synonyms` - vector of synonyms.
+/// * `segment_number_bits1` - number of index segments: e.g. 11 bits for 2048 segments.
+/// * `mute` - prevent emitting status messages (e.g. when using pipes for data interprocess communication).
+/// * `force_shard_number` - set number of shards manually or automatically.
+/// - none: number of shards is set automatically = number of physical processor cores (default)
+/// - small: slower indexing, higher latency, slightly higher throughput, faster realtime search, lower RAM consumption
+/// - large: faster indexing, lower latency, slightly lower throughput, slower realtime search, higher RAM consumption
+Future createIndex(
+ {required Path indexPath,
+ required IndexMetaObject meta,
+ required List schema,
+ required List synonyms,
+ required BigInt segmentNumberBits1,
+ required bool mute,
+ BigInt? forceShardNumber}) =>
+ RustLib.instance.api.seekstormIndexCreateIndex(
+ indexPath: indexPath,
+ meta: meta,
+ schema: schema,
+ synonyms: synonyms,
+ segmentNumberBits1: segmentNumberBits1,
+ mute: mute,
+ forceShardNumber: forceShardNumber);
+
+/// Loads the index from disk into RAM or MMAP.
+/// * `index_path` - index path.
+/// * `mute` - prevent emitting status messages (e.g. when using pipes for data interprocess communication).
+Future openIndex({required Path indexPath, required bool mute}) =>
+ RustLib.instance.api
+ .seekstormIndexOpenIndex(indexPath: indexPath, mute: mute);
+
+// Rust type: RustOpaqueMoi>
+abstract class DocumentItem implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class FacetField implements RustOpaqueInterface {
+ ValueType get max;
+
+ ValueType get min;
+
+ String get name;
+
+ IndexMapStringVecStringUsize get values;
+
+ set max(ValueType max);
+
+ set min(ValueType min);
+
+ set name(String name);
+
+ set values(IndexMapStringVecStringUsize values);
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormIndexFacetFieldDefault();
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class FileType implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class FrequentwordType implements RustOpaqueInterface {
+ static Future default_() =>
+ RustLib.instance.api.seekstormIndexFrequentwordTypeDefault();
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class Index implements RustOpaqueInterface {
+ /// Add/append/update/merge synonyms in index
+ /// Affects only subsequently indexed documents
+ Future addSynonyms({required List synonyms});
+
+ int get indexFormatVersionMajor;
+
+ int get indexFormatVersionMinor;
+
+ IndexMetaObject get meta;
+
+ Map get schemaMap;
+
+ List get storedFieldNames;
+
+ set indexFormatVersionMajor(int indexFormatVersionMajor);
+
+ set indexFormatVersionMinor(int indexFormatVersionMinor);
+
+ set meta(IndexMetaObject meta);
+
+ set schemaMap(Map schemaMap);
+
+ set storedFieldNames(List storedFieldNames);
+
+ /// Reset index to empty, while maintaining schema
+ Future clearIndex();
+
+ /// Get number of indexed documents.
+ Future committedDocCount();
+
+ /// Current document count: indexed document count - deleted document count
+ Future currentDocCount();
+
+ /// Delete index from disc and ram
+ Future deleteIndex();
+
+ /// Get number of facets defined in the index schema.
+ Future facetsCount();
+
+ /// Get document for document id
+ /// Arguments:
+ /// * `doc_id`: Specifies which document to load from the document store of the index.
+ /// * `include_uncommited`: Return also documents which have not yet been committed.
+ /// * `highlighter_option`: Specifies the extraction of keyword-in-context (KWIC) fragments from fields in documents, and the highlighting of the query terms within.
+ /// * `fields`: Specifies which of the stored fields to return with each document. Default: If empty return all stored fields
+ /// * `distance_fields`: insert distance fields into result documents, calculating the distance between a specified facet field of type Point and a base Point, in kilometers or miles.
+ /// using Euclidian distance (Pythagoras theorem) with Equirectangular approximation.
+ Future> getDocument(
+ {required BigInt docId,
+ required bool includeUncommited,
+ Highlighter? highlighterOption,
+ required Set fields,
+ required List distanceFields});
+
+ /// get_facet_value: Returns value from facet field for a doc_id even if schema stored=false (field not stored in document JSON).
+ /// Facet fields are more compact than fields stored in document JSON.
+ /// Strings are stored more compact as indices to a unique term dictionary. Numbers are stored binary, not as strings.
+ /// Facet fields are faster because no document loading, decompression and JSON decoding is required.
+ /// Facet fields are always memory mapped, internally always stored with fixed byte length layout, regardless of string size.
+ Future getFacetValue(
+ {required String field, required BigInt docId});
+
+ /// Get file for document id
+ /// Arguments:
+ /// * `doc_id`: Specifies which document to load from the document store of the index.
+ ///
+ /// Returns:
+ /// * `Vec`: The file content as a byte vector.
+ ///
+ Future getFile({required BigInt docId});
+
+ /// get_index_string_facets: list of string facet fields, each with field name and a map of unique values and their count (number of times the specific value appears in the whole index).
+ /// values are sorted by their occurrence count within all indexed documents in descending order
+ /// * `query_facets`: Must be set if facet fields should be returned in get_index_facets. If set to Vec::new() then no facet fields are returned.
+ /// The prefix property of a QueryFacet allows to filter the returned facet values to those matching a given prefix, if there are too many distinct values per facet field.
+ /// The length property of a QueryFacet allows limiting the number of returned distinct values per facet field, if there are too many distinct values. The QueryFacet can be used to improve the usability in an UI.
+ /// If the length property of a QueryFacet is set to 0 then no facet values for that facet are returned.
+ /// The facet values are sorted by the frequency of the appearance of the value within the indexed documents matching the query in descending order.
+ /// Example: query_facets = vec![QueryFacet::String16 {field: "language".to_string(),prefix: "ger".to_string(),length: 5},QueryFacet::String16 {field: "brand".to_string(),prefix: "a".to_string(),length: 5}];
+ Future getIndexStringFacets(
+ {required List queryFacets});
+
+ /// Get synonyms from index
+ Future> getSynonyms();
+
+ /// get_index_facets_minmax: return map of numeric facet fields, each with field name and min/max values.
+ Future> indexFacetsMinmax();
+
+ /// Get number of indexed documents.
+ Future indexedDocCount();
+
+ /// Get number of index levels. One index level comprises 64K documents.
+ Future levelCount();
+
+ /// Set/replace/overwrite synonyms in index
+ /// Affects only subsequently indexed documents
+ Future setSynonyms({required List synonyms});
+
+ /// Get number of index shards.
+ Future shardCount();
+
+ /// are there uncommited documents?
+ Future uncommittedDocCount();
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class IndexMetaObject implements RustOpaqueInterface {
+ AccessType get accessType;
+
+ FrequentwordType get frequentWords;
+
+ BigInt get id;
+
+ String get name;
+
+ int get ngramIndexing;
+
+ QueryCompletion? get queryCompletion;
+
+ SimilarityType get similarity;
+
+ SpellingCorrection? get spellingCorrection;
+
+ StemmerType get stemmer;
+
+ StopwordType get stopWords;
+
+ TokenizerType get tokenizer;
+
+ set accessType(AccessType accessType);
+
+ set frequentWords(FrequentwordType frequentWords);
+
+ set id(BigInt id);
+
+ set name(String name);
+
+ set ngramIndexing(int ngramIndexing);
+
+ set queryCompletion(QueryCompletion? queryCompletion);
+
+ set similarity(SimilarityType similarity);
+
+ set spellingCorrection(SpellingCorrection? spellingCorrection);
+
+ set stemmer(StemmerType stemmer);
+
+ set stopWords(StopwordType stopWords);
+
+ set tokenizer(TokenizerType tokenizer);
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class MinMaxField implements RustOpaqueInterface {
+ ValueType get max;
+
+ ValueType get min;
+
+ set max(ValueType max);
+
+ set min(ValueType min);
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormIndexMinMaxFieldDefault();
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class MinMaxFieldJson implements RustOpaqueInterface {
+ Value get max;
+
+ Value get min;
+
+ set max(Value max);
+
+ set min(Value min);
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormIndexMinMaxFieldJsonDefault();
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class SchemaField implements RustOpaqueInterface {
+ double get boost;
+
+ bool get completionSource;
+
+ bool get dictionarySource;
+
+ bool get facet;
+
+ String get field;
+
+ FieldType get fieldType;
+
+ bool get indexed;
+
+ bool get longest;
+
+ bool get stored;
+
+ set boost(double boost);
+
+ set completionSource(bool completionSource);
+
+ set dictionarySource(bool dictionarySource);
+
+ set facet(bool facet);
+
+ set field(String field);
+
+ set fieldType(FieldType fieldType);
+
+ set indexed(bool indexed);
+
+ set longest(bool longest);
+
+ set stored(bool stored);
+
+ // HINT: Make it `#[frb(sync)]` to let it become the default constructor of Dart class.
+ /// Creates a new SchemaField.
+ /// Defines a field in index schema: field, stored, indexed , field_type, facet, boost.
+ /// # Parameters
+ /// - field: unique name of a field
+ /// - stored: only stored fields are returned in the search results
+ /// - indexed: only indexed fields can be searched
+ /// - field_type: type of a field: u8, u16, u32, u64, i8, i16, i32, i64, f32, f64, point
+ /// - facet: enable faceting for a field: for sorting results by field values, for range filtering, for result count per field value or range
+ /// - `longest`: This allows to annotate (manually set) the longest field in schema.
+ /// Otherwise the longest field will be automatically detected in first index_document.
+ /// Setting/detecting the longest field ensures efficient index encoding.
+ /// - boost: optional custom weight factor for Bm25 ranking
+ /// # Returns
+ /// - SchemaField
+ /// # Example
+ /// ```rust
+ /// use seekstorm::index::{SchemaField, FieldType};
+ /// let schema_field = SchemaField::new("title".to_string(), true, true, FieldType::String16, false, false, 1.0, false, false);
+ /// ```
+ static Future newInstance(
+ {required String field,
+ required bool stored,
+ required bool indexed,
+ required FieldType fieldType,
+ required bool facet,
+ required bool longest,
+ required double boost,
+ required bool dictionarySource,
+ required bool completionSource}) =>
+ RustLib.instance.api.seekstormIndexSchemaFieldNew(
+ field: field,
+ stored: stored,
+ indexed: indexed,
+ fieldType: fieldType,
+ facet: facet,
+ longest: longest,
+ boost: boost,
+ dictionarySource: dictionarySource,
+ completionSource: completionSource);
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class StopwordType implements RustOpaqueInterface {
+ static Future default_() =>
+ RustLib.instance.api.seekstormIndexStopwordTypeDefault();
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class ValueType implements RustOpaqueInterface {
+ static Future default_() =>
+ RustLib.instance.api.seekstormIndexValueTypeDefault();
+}
+
+abstract class Close {
+ /// Remove index from RAM (Reverse of open_index)
+ Future close();
+}
+
+abstract class DeleteDocument {
+ /// Delete document from index by document id
+ Future deleteDocument({required BigInt docid});
+}
+
+abstract class DeleteDocuments {
+ /// Delete documents from index by document id
+ Future deleteDocuments({required Uint64List docidVec});
+}
+
+abstract class DeleteDocumentsByQuery {
+ /// Delete documents from index by query
+ /// Delete and search have identical parameters.
+ /// It is recommended to test with search prior to delete to verify that only those documents are returned that you really want to delete.
+ Future deleteDocumentsByQuery(
+ {required String queryString,
+ required QueryType queryTypeDefault,
+ required BigInt offset,
+ required BigInt length,
+ required bool includeUncommited,
+ required List fieldFilter,
+ required List facetFilter,
+ required List resultSort});
+}
+
+abstract class IndexDocument {
+ /// Indexes a single document
+ /// May block, if the threshold of documents indexed in parallel is exceeded.
+ Future indexDocument(
+ {required Map document, required FileType file});
+}
+
+abstract class IndexDocument2 {
+ Future indexDocument2(
+ {required DocumentItem documentItem, required FileType file});
+}
+
+abstract class IndexDocumentShard {
+ /// Indexes a single document
+ /// May block, if the threshold of documents indexed in parallel is exceeded.
+ Future indexDocumentShard(
+ {required Map document, required FileType file});
+}
+
+abstract class IndexDocuments {
+ /// Indexes a list of documents
+ /// May block, if the threshold of documents indexed in parallel is exceeded.
+ Future indexDocuments({required List> documentVec});
+}
+
+abstract class UpdateDocument {
+ /// Update document in index
+ /// Update_document is a combination of delete_document and index_document.
+ /// All current limitations of delete_document apply.
+ Future updateDocument(
+ {required (BigInt, Map) idDocument});
+}
+
+abstract class UpdateDocuments {
+ /// Update documents in index
+ /// Update_document is a combination of delete_document and index_document.
+ /// All current limitations of delete_document apply.
+ Future updateDocuments(
+ {required List<(BigInt, Map)> idDocumentVec});
+}
+
+/// Defines where the index resides during search:
+/// - Ram (the complete index is preloaded to Ram when opening the index)
+/// - Mmap (the index is accessed via memory-mapped files). See architecture.md for details.
+/// - At commit the data is serialized to disk for persistence both in Ram and Mmap mode.
+/// - The serialization format is identical for Ram and Mmap mode, allowing to change it retrospectively.
+enum AccessType {
+ /// Ram (the complete index is preloaded to Ram when opening the index).
+ /// - Index size is limited by available RAM size.
+ /// - Slightly fastesr search speed.
+ /// - Higher index loading time.
+ /// - Higher RAM usage.
+ ram,
+
+ /// Mmap (the index is accessed via memory-mapped files). See architecture.md for details.
+ /// - Enables index size scaling beyond RAM size.
+ /// - Slightly slower search speed compared to Ram.
+ /// - Faster index loading time compared to Ram.
+ /// - Lower RAM usage.
+ mmap,
+ ;
+}
+
+/// Type of posting list compression.
+enum CompressionType {
+ delta,
+ array,
+ bitmap,
+ rle,
+ error,
+ ;
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormIndexCompressionTypeDefault();
+}
+
+/// DistanceField defines a field for proximity search.
+class DistanceField {
+ /// field name of a numeric facet field (currently onyl Point field type supported)
+ final String field;
+
+ /// field name of the distance field we are deriving from the numeric facet field (Point type) and the base (Point type)
+ final String distance;
+
+ /// base point (lat,lon) for distance calculation
+ final Float64List base;
+
+ /// distance unit for the distance field: kilometers or miles
+ final DistanceUnit unit;
+
+ const DistanceField({
+ required this.field,
+ required this.distance,
+ required this.base,
+ required this.unit,
+ });
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormIndexDistanceFieldDefault();
+
+ @override
+ int get hashCode =>
+ field.hashCode ^ distance.hashCode ^ base.hashCode ^ unit.hashCode;
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is DistanceField &&
+ runtimeType == other.runtimeType &&
+ field == other.field &&
+ distance == other.distance &&
+ base == other.base &&
+ unit == other.unit;
+}
+
+/// DistanceUnit defines the unit for distance calculation: kilometers or miles.
+enum DistanceUnit {
+ /// Kilometers
+ kilometers,
+
+ /// Miles
+ miles,
+ ;
+}
+
+/// FieldType defines the type of a field in the document: u8, u16, u32, u64, i8, i16, i32, i64, f32, f64, point, string, stringset, text.
+enum FieldType {
+ /// Unsigned 8-bit integer
+ u8,
+
+ /// Unsigned 16-bit integer
+ u16,
+
+ /// Unsigned 32-bit integer
+ u32,
+
+ /// Unsigned 64-bit integer
+ u64,
+
+ /// Signed 8-bit integer
+ i8,
+
+ /// Signed 16-bit integer
+ i16,
+
+ /// Signed 32-bit integer
+ i32,
+
+ /// Signed 64-bit integer
+ i64,
+
+ /// Timestamp is identical to I64, but to be used for Unix timestamps .
+ /// The reason for a separate FieldType is to enable the UI to interpret I64 as timestamp without using the field name as indicator.
+ /// For date facets and filtering.
+ timestamp,
+
+ /// Floating point 32-bit
+ f32,
+
+ /// Floating point 64-bit
+ f64,
+
+ /// Boolean
+ bool,
+
+ /// String16
+ /// allows a maximum cardinality of 65_535 (16 bit) distinct values, is space-saving.
+ string16,
+
+ /// String32
+ /// allows a maximum cardinality of 4_294_967_295 (32 bit) distinct values
+ string32,
+
+ /// StringSet16 is a set of strings, e.g. tags, categories, keywords, authors, genres, etc.
+ /// allows a maximum cardinality of 65_535 (16 bit) distinct values, is space-saving.
+ stringSet16,
+
+ /// StringSet32 is a set of strings, e.g. tags, categories, keywords, authors, genres, etc.
+ /// allows a maximum cardinality of 4_294_967_295 (32 bit) distinct values
+ stringSet32,
+
+ /// Point is a geographic field type: A `Vec` with two coordinate values (latitude and longitude) are internally encoded into a single u64 value (Morton code).
+ /// Morton codes enable efficient range queries.
+ /// Latitude and longitude are a pair of numbers (coordinates) used to describe a position on the plane of a geographic coordinate system.
+ /// The numbers are in decimal degrees format and range from -90 to 90 for latitude and -180 to 180 for longitude.
+ /// Coordinates are internally stored as u64 morton code: both f64 values are multiplied by 10_000_000, converted to i32 and bitwise interleaved into a single u64 morton code
+ /// The conversion between longitude/latitude coordinates and Morton code is lossy due to rounding errors.
+ point,
+
+ /// Text is a text field, that will be tokenized by the selected Tokenizer into string tokens.
+ text,
+ ;
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormIndexFieldTypeDefault();
+}
+
+enum NgramType {
+ /// no n-grams, only single terms are indexed
+ singleTerm,
+
+ /// Ngram frequent frequent
+ ngramFf,
+
+ /// Ngram frequent rare
+ ngramFr,
+
+ /// Ngram rare frequent
+ ngramRf,
+
+ /// Ngram frequent frequent frequent
+ ngramFff,
+
+ /// Ngram rare frequent frequent
+ ngramRff,
+
+ /// Ngram frequent frequent rare
+ ngramFfr,
+
+ /// Ngram frequent rare frequent
+ ngramFrf,
+ ;
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormIndexNgramTypeDefault();
+}
+
+/// Defines spelling correction (fuzzy search) settings for an index.
+class QueryCompletion {
+ /// Maximum number of completions to generate during indexing
+ /// disabled if == 0
+ final BigInt maxCompletionEntries;
+
+ const QueryCompletion({
+ required this.maxCompletionEntries,
+ });
+
+ @override
+ int get hashCode => maxCompletionEntries.hashCode;
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is QueryCompletion &&
+ runtimeType == other.runtimeType &&
+ maxCompletionEntries == other.maxCompletionEntries;
+}
+
+/// Similarity type defines the scoring and ranking of the search results:
+/// - Bm25f: considers documents composed from several fields, with different field lengths and importance
+/// - Bm25fProximity: considers term proximity, e.g. for implicit phrase search with improved relevancy
+enum SimilarityType {
+ /// Bm25f considers documents composed from several fields, with different field lengths and importance
+ bm25F,
+
+ /// Bm25fProximity considers term proximity, e.g. for implicit phrase search with improved relevancy
+ bm25FProximity,
+ ;
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormIndexSimilarityTypeDefault();
+}
+
+/// Defines spelling correction (fuzzy search) settings for an index.
+class SpellingCorrection {
+ /// The edit distance thresholds for suggestions: 1..2 recommended; higher values increase latency and memory consumption.
+ final BigInt maxDictionaryEditDistance;
+
+ /// Term length thresholds for each edit distance.
+ /// None: max_dictionary_edit_distance for all terms lengths
+ /// Some(\[4\]): max_dictionary_edit_distance for all terms lengths >= 4,
+ /// Some(\[2,8\]): max_dictionary_edit_distance for all terms lengths >=2, max_dictionary_edit_distance +1 for all terms for lengths>=8
+ final Uint64List? termLengthThreshold;
+
+ /// The minimum frequency count for dictionary words to be considered eligible for spelling correction.
+ /// Depends on the corpus size, 1..20 recommended.
+ /// If count_threshold is too high, some correct words might be missed from the dictionary and deemed misspelled,
+ /// if count_threshold is too low, some misspelled words from the corpus might be considered correct and added to the dictionary.
+ /// Dictionary terms eligible for spelling correction (frequency count >= count_threshold) consume much more RAM, than the candidates (frequency count < count_threshold),
+ /// but the terms below count_threshold will be included in dictionary.csv too.
+ final BigInt countThreshold;
+
+ /// Limits the maximum number of dictionary entries (terms >= count_threshold) to generate during indexing, preventing excessive RAM consumption.
+ /// The number of terms in dictionary.csv will be higher, because it contains also the terms < count_threshold, to become eligible in the future during incremental dictionary updates.
+ /// Dictionary terms eligible for spelling correction (frequency count >= count_threshold) consume much more RAM, than the candidates (frequency count < count_threshold).
+ /// ⚠️ Above this threshold no new terms are added to the dictionary, causing them to be deemed incorrect during spelling correction and possibly changed to similar terms that are in the dictionary.
+ final BigInt maxDictionaryEntries;
+
+ const SpellingCorrection({
+ required this.maxDictionaryEditDistance,
+ this.termLengthThreshold,
+ required this.countThreshold,
+ required this.maxDictionaryEntries,
+ });
+
+ @override
+ int get hashCode =>
+ maxDictionaryEditDistance.hashCode ^
+ termLengthThreshold.hashCode ^
+ countThreshold.hashCode ^
+ maxDictionaryEntries.hashCode;
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is SpellingCorrection &&
+ runtimeType == other.runtimeType &&
+ maxDictionaryEditDistance == other.maxDictionaryEditDistance &&
+ termLengthThreshold == other.termLengthThreshold &&
+ countThreshold == other.countThreshold &&
+ maxDictionaryEntries == other.maxDictionaryEntries;
+}
+
+/// Defines stemming behavior, reducing inflected words to their word stem, base or root form.
+/// Stemming increases recall, but decreases precision. It can introduce false positive results.
+enum StemmerType {
+ /// No stemming
+ none,
+
+ /// Arabic stemmer
+ arabic,
+
+ /// Danish stemmer
+ danish,
+
+ /// Dutch stemmer
+ dutch,
+
+ /// English stemmer
+ english,
+
+ /// Finnish stemmer
+ finnish,
+
+ /// French stemmer
+ french,
+
+ /// German stemmer
+ german,
+
+ /// Hungarian stemmer
+ greek,
+
+ /// Hungarian stemmer
+ hungarian,
+
+ /// Italian stemmer
+ italian,
+
+ /// Norwegian stemmer
+ norwegian,
+
+ /// Portuguese stemmer
+ portuguese,
+
+ /// Romanian stemmer
+ romanian,
+
+ /// Russian stemmer
+ russian,
+
+ /// Spanish stemmer
+ spanish,
+
+ /// Swedish stemmer
+ swedish,
+
+ /// Tamil stemmer
+ tamil,
+
+ /// Turkish stemmer
+ turkish,
+ ;
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormIndexStemmerTypeDefault();
+}
+
+/// Defines synonyms for terms per index.
+class Synonym {
+ /// List of terms that are synonyms.
+ final List terms;
+
+ /// Creates alternative versions of documents where in each copy a term is replaced with one of its synonyms.
+ /// Doesn't impact the query latency, but does increase the index size.
+ /// Multi-way synonyms (default): all terms are synonyms of each other.
+ /// One-way synonyms: only the first term is a synonym of the following terms, but not vice versa.
+ /// E.g. [street, avenue, road] will result in searches for street to return documents containing any of the terms street, avenue or road,
+ /// but searches for avenue will only return documents containing avenue, but not documents containing street or road.
+ /// Currently only single terms without spaces are supported.
+ /// Synonyms are supported in result highlighting.
+ /// The synonyms that were created with the synonyms parameter in create_index are stored in synonyms.json in the index directory contains
+ /// Can be manually modified, but becomes effective only after restart and only for newly indexed documents.
+ final bool multiway;
+
+ const Synonym({
+ required this.terms,
+ required this.multiway,
+ });
+
+ @override
+ int get hashCode => terms.hashCode ^ multiway.hashCode;
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is Synonym &&
+ runtimeType == other.runtimeType &&
+ terms == other.terms &&
+ multiway == other.multiway;
+}
+
+/// Defines tokenizer behavior:
+/// AsciiAlphabetic
+/// - Mainly for for benchmark compatibility
+/// - Only ASCII alphabetic chars are recognized as token.
+///
+/// UnicodeAlphanumeric
+/// - All Unicode alphanumeric chars are recognized as token.
+/// - Allows '+' '-' '#' in middle or end of a token: c++, c#, block-max.
+///
+/// UnicodeAlphanumericFolded
+/// - All Unicode alphanumeric chars are recognized as token.
+/// - Allows '+' '-' '#' in middle or end of a token: c++, c#, block-max.
+/// - Diacritics, accents, zalgo text, umlaut, bold, italic, full-width UTF-8 characters are converted into its basic representation.
+/// - Apostroph handling prevents that short term parts preceding or following the apostroph get indexed (e.g. "s" in "someone's").
+/// - Tokenizing might be slower due to folding and apostroph processing.
+///
+/// UnicodeAlphanumericZH
+/// - Implements Chinese word segmentation to segment continuous Chinese text into tokens for indexing and search.
+/// - Supports mixed Latin and Chinese texts
+/// - Supports Chinese sentence boundary chars for KWIC snippets ahd highlighting.
+/// - Requires feature #[cfg(feature = "zh")]
+enum TokenizerType {
+ /// Only ASCII alphabetic chars are recognized as token. Mainly for benchmark compatibility.
+ asciiAlphabetic,
+
+ /// All Unicode alphanumeric chars are recognized as token.
+ /// Allow '+' '-' '#' in middle or end of a token: c++, c#, block-max.
+ unicodeAlphanumeric,
+
+ /// All Unicode alphanumeric chars are recognized as token.
+ /// Allows '+' '-' '#' in middle or end of a token: c++, c#, block-max.
+ /// Diacritics, accents, zalgo text, umlaut, bold, italic, full-width UTF-8 characters are converted into its basic representation.
+ /// Apostroph handling prevents that short term parts preceding or following the apostroph get indexed (e.g. "s" in "someone's").
+ /// Tokenizing might be slower due to folding and apostroph processing.
+ unicodeAlphanumericFolded,
+
+ /// Tokens are separated by whitespace. Mainly for benchmark compatibility.
+ whitespace,
+
+ /// Tokens are separated by whitespace. Token are converted to lowercase. Mainly for benchmark compatibility.
+ whitespaceLowercase,
+
+ /// Implements Chinese word segmentation to segment continuous Chinese text into tokens for indexing and search.
+ /// Supports mixed Latin and Chinese texts
+ /// Supports Chinese sentence boundary chars for KWIC snippets ahd highlighting.
+ /// Requires feature #[cfg(feature = "zh")]
+ unicodeAlphanumericZh,
+ ;
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormIndexTokenizerTypeDefault();
+}
diff --git a/mobile_app/lib/src/rust/third_party/seekstorm/ingest.dart b/mobile_app/lib/src/rust/third_party/seekstorm/ingest.dart
new file mode 100644
index 0000000..ddfead1
--- /dev/null
+++ b/mobile_app/lib/src/rust/third_party/seekstorm/ingest.dart
@@ -0,0 +1,83 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.11.1.
+
+// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
+
+import '../../frb_generated.dart';
+import '../../lib.dart';
+import 'index.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
+
+// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `pdfium_option`
+// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `deref`, `initialize`
+// These functions are ignored (category: IgnoreBecauseNotAllowedOwner): `index_pdf_bytes`, `index_pdf_file`, `index_pdf`, `ingest_csv`, `ingest_json`, `ingest_pdf`
+
+// Rust type: RustOpaqueMoi>>
+abstract class PdfDocument implements RustOpaqueInterface {}
+
+abstract class IndexPdf {
+ Future indexPdf(
+ {required Path filePath,
+ required BigInt fileSize,
+ required PlatformInt64 fileDate,
+ required FileType file,
+ required PdfDocument pdf});
+}
+
+abstract class IndexPdfBytes {
+ /// Index PDF file from byte array.
+ /// - converts pdf to text and indexes it
+ /// - extracts title from metatag, or first line of text, or from filename
+ /// - extracts creation date from metatag, or from file creation date (Unix timestamp: the number of seconds since 1 January 1970)
+ /// - copies all ingested pdf files to "files" subdirectory in index
+ /// # Arguments
+ /// * `file_path` - Path to the file (fallback, if title and date can't be extracted)
+ /// * `file_date` - File creation date (Unix timestamp: the number of seconds since 1 January 1970) (fallback, if date can't be extracted)
+ /// * `file_bytes` - Byte array of the file
+ Future indexPdfBytes(
+ {required Path filePath,
+ required PlatformInt64 fileDate,
+ required List fileBytes});
+}
+
+abstract class IndexPdfFile {
+ /// Index PDF file from local disk.
+ /// - converts pdf to text and indexes it
+ /// - extracts title from metatag, or first line of text, or from filename
+ /// - extracts creation date from metatag, or from file creation date (Unix timestamp: the number of seconds since 1 January 1970)
+ /// - copies all ingested pdf files to "files" subdirectory in index
+ /// # Arguments
+ /// * `file_path` - Path to the file
+ /// # Returns
+ /// * `Result<(), String>` - Ok(()) or Err(String)
+ Future indexPdfFile({required Path filePath});
+}
+
+abstract class IngestCsv {
+ /// Ingest local data files in [CSV](https://en.wikipedia.org/wiki/Comma-separated_values).
+ /// The document ingestion is streamed without loading the whole document vector into memory to allwow for unlimited file size while keeping RAM consumption low.
+ Future ingestCsv(
+ {required Path dataPath,
+ required bool hasHeader,
+ required bool quoting,
+ required int delimiter,
+ BigInt? skipDocs,
+ BigInt? numDocs});
+}
+
+abstract class IngestJson {
+ /// Ingest local data files in [JSON](https://en.wikipedia.org/wiki/JSON), [Newline-delimited JSON](https://github.com/ndjson/ndjson-spec) (ndjson), and [Concatenated JSON](https://en.wikipedia.org/wiki/JSON_streaming) formats via console command.
+ /// The document ingestion is streamed without loading the whole document vector into memory to allwow for unlimited file size while keeping RAM consumption low.
+ Future ingestJson({required Path dataPath});
+}
+
+abstract class IngestPdf {
+ /// Index PDF files from local disk directory and sub-directories or from file.
+ /// - converts pdf to text and indexes it
+ /// - extracts title from metatag, or first line of text, or from filename
+ /// - extracts creation date from metatag, or from file creation date (Unix timestamp: the number of seconds since 1 January 1970)
+ /// - copies all ingested pdf files to "files" subdirectory in index
+ /// # Arguments
+ /// * `file_path` - Path to the file
+ Future ingestPdf({required Path filePath});
+}
diff --git a/mobile_app/lib/src/rust/third_party/seekstorm/search.dart b/mobile_app/lib/src/rust/third_party/seekstorm/search.dart
new file mode 100644
index 0000000..5374173
--- /dev/null
+++ b/mobile_app/lib/src/rust/third_party/seekstorm/search.dart
@@ -0,0 +1,291 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.11.1.
+
+// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
+
+import '../../frb_generated.dart';
+import '../../lib.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
+
+// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `FilterSparse`, `RangeF32`, `RangeF64`, `RangeI16`, `RangeI32`, `RangeI64`, `RangeI8`, `RangeU16`, `RangeU32`, `RangeU64`, `RangeU8`, `ResultSortIndex`, `SearchResult`
+// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `compose`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `name`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`, `schemas`
+// These functions are ignored (category: IgnoreBecauseNotAllowedOwner): `search_shard`, `search`
+// These functions are ignored (category: IgnoreBecauseOwnerTyShouldIgnore): `default`
+
+// Rust type: RustOpaqueMoi>
+abstract class FacetFilter implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class FacetValue implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class QueryFacet implements RustOpaqueInterface {
+ static Future default_() =>
+ RustLib.instance.api.seekstormSearchQueryFacetDefault();
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class QueryRewriting implements RustOpaqueInterface {
+ static Future default_() =>
+ RustLib.instance.api.seekstormSearchQueryRewritingDefault();
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class Ranges implements RustOpaqueInterface {
+ static Future default_() =>
+ RustLib.instance.api.seekstormSearchRangesDefault();
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class ResultObject implements RustOpaqueInterface {
+ AHashMapStringFacet get facets;
+
+ String get originalQuery;
+
+ String get query;
+
+ List get queryTerms;
+
+ BigInt get resultCount;
+
+ BigInt get resultCountTotal;
+
+ List get results;
+
+ set facets(AHashMapStringFacet facets);
+
+ set originalQuery(String originalQuery);
+
+ set query(String query);
+
+ set queryTerms(List queryTerms);
+
+ set resultCount(BigInt resultCount);
+
+ set resultCountTotal(BigInt resultCountTotal);
+
+ set results(List results);
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormSearchResultObjectDefault();
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class ResultSort implements RustOpaqueInterface {
+ FacetValue get base;
+
+ String get field;
+
+ SortOrder get order;
+
+ set base(FacetValue base);
+
+ set field(String field);
+
+ set order(SortOrder order);
+}
+
+abstract class Search {
+ /// Search the index for all indexed documents, both for committed and uncommitted documents.
+ /// The latter enables true realtime search: documents are available for search in exact the same millisecond they are indexed.
+ /// Arguments:
+ /// * `query_string`: query string `+` `-` `""` search operators are recognized.
+ /// * `query_type_default`: Specifiy default QueryType:
+ /// * **Union**, disjunction, OR,
+ /// * **Intersection**, conjunction, AND, `+`,
+ /// * **Phrase** `""`,
+ /// * **Not**, except, minus `-`.
+ ///
+ /// The default QueryType is superseded if the query parser detects that a different query type is specified within the query string (`+` `-` `""`).
+ ///
+ /// Boolean queries are specified in the search method either via the query_type parameter or via operator chars within the query parameter.
+ /// The interpretation of operator chars within the query string (set `query_type=QueryType::Union`) allows to specify advanced search operations via a simple search box.
+ ///
+ /// Intersection, AND `+`
+ /// ```rust ,no_run
+ /// use seekstorm::search::QueryType;
+ /// let query_type=QueryType::Union;
+ /// let query_string="+red +apple".to_string();
+ /// ```
+ /// ```rust ,no_run
+ /// use seekstorm::search::QueryType;
+ /// let query_type=QueryType::Intersection;
+ /// let query_string="red apple".to_string();
+ /// ```
+ /// Union, OR
+ /// ```rust ,no_run
+ /// use seekstorm::search::QueryType;
+ /// let query_type=QueryType::Union;
+ /// let query_string="red apple".to_string();
+ /// ```
+ /// Phrase `""`
+ /// ```rust ,no_run
+ /// use seekstorm::search::QueryType;
+ /// let query_type=QueryType::Union;
+ /// let query_string="\"red apple\"".to_string();
+ /// ```
+ /// ```rust ,no_run
+ /// use seekstorm::search::QueryType;
+ /// let query_type=QueryType::Phrase;
+ /// let query_string="red apple".to_string();
+ /// ```
+ /// Except, minus, NOT `-`
+ /// ```rust ,no_run
+ /// use seekstorm::search::QueryType;
+ /// let query_type=QueryType::Union;
+ /// let query_string="apple -red".to_string();
+ /// ```
+ /// Mixed phrase and intersection
+ /// ```rust ,no_run
+ /// use seekstorm::search::QueryType;
+ /// let query_type=QueryType::Union;
+ /// let query_string="+\"the who\" +uk".to_string();
+ /// ```
+ /// * `offset`: offset of search results to return.
+ /// * `length`: number of search results to return.
+ /// With length=0, resultType::TopkCount will be automatically downgraded to resultType::Count, returning the number of results only, without returning the results itself.
+ /// * `result_type`: type of search results to return: Count, Topk, TopkCount.
+ /// * `include_uncommited`: true realtime search: include indexed documents which where not yet committed into search results.
+ /// * `field_filter`: Specify field names where to search at querytime, whereas SchemaField.indexed is set at indextime. If set to Vec::new() then all indexed fields are searched.
+ /// * `query_facets`: Must be set if facets should be returned in ResultObject. If set to Vec::new() then no facet fields are returned.
+ /// Facet fields are only collected, counted and returned for ResultType::Count and ResultType::TopkCount, but not for ResultType::Topk.
+ /// The prefix property of a QueryFacet allows at query time to filter the returned facet values to those matching a given prefix, if there are too many distinct values per facet field.
+ /// The length property of a QueryFacet allows at query time limiting the number of returned distinct values per facet field, if there are too many distinct values. The QueryFacet can be used to improve the usability in an UI.
+ /// If the length property of a QueryFacet is set to 0 then no facet values for that facet are collected, counted and returned at query time. That decreases the query latency significantly.
+ /// The facet values are sorted by the frequency of the appearance of the value within the indexed documents matching the query in descending order.
+ /// Examples:
+ /// query_facets = vec![QueryFacet::String16 {field: "language".into(),prefix: "ger".into(),length: 5},QueryFacet::String16 {field: "brand".into(),prefix: "a".into(),length: 5}];
+ /// query_facets = vec![QueryFacet::U8 {field: "age".into(), range_type: RangeType::CountWithinRange, ranges: vec![("0-20".into(), 0),("20-40".into(), 20), ("40-60".into(), 40),("60-80".into(), 60), ("80-100".into(), 80)]}];
+ /// query_facets = vec![QueryFacet::Point {field: "location".into(),base:vec![38.8951, -77.0364],unit:DistanceUnit::Kilometers,range_type: RangeType::CountWithinRange,ranges: vec![ ("0-200".into(), 0.0),("200-400".into(), 200.0), ("400-600".into(), 400.0), ("600-800".into(), 600.0), ("800-1000".into(), 800.0)]}];
+ /// * `facet_filter`: Search results are filtered to documents matching specific string values or numerical ranges in the facet fields. If set to Vec::new() then result are not facet filtered.
+ /// The filter parameter filters the returned results to those documents both matching the query AND matching for all (boolean AND) stated facet filter fields at least one (boolean OR) of the stated values.
+ /// If the query is changed then both facet counts and search results are changed. If the facet filter is changed then only the search results are changed, while facet counts remain unchanged.
+ /// The facet counts depend only from the query and not which facet filters are selected.
+ /// Examples:
+ /// facet_filter=vec![FacetFilter::String{field:"language".into(),filter:vec!["german".into()]},FacetFilter::String{field:"brand".into(),filter:vec!["apple".into(),"google".into()]}];
+ /// facet_filter=vec![FacetFilter::U8{field:"age".into(),filter: 21..65}];
+ /// facet_filter = vec![FacetFilter::Point {field: "location".into(),filter: (vec![38.8951, -77.0364], 0.0..1000.0, DistanceUnit::Kilometers)}];
+ /// * `result_sort`: Sort field and order: Search results are sorted by the specified facet field, either in ascending or descending order.
+ /// If no sort field is specified, then the search results are sorted by rank in descending order per default.
+ /// Multiple sort fields are combined by a "sort by, then sort by"-method ("tie-breaking"-algorithm).
+ /// The results are sorted by the first field, and only for those results where the first field value is identical (tie) the results are sub-sorted by the second field,
+ /// until the n-th field value is either not equal or the last field is reached.
+ /// A special _score field (BM25x), reflecting how relevant the result is for a given search query (phrase match, match in title etc.) can be combined with any of the other sort fields as primary, secondary or n-th search criterium.
+ /// Sort is only enabled on facet fields that are defined in schema at create_index!
+ /// Examples:
+ /// result_sort = vec![ResultSort {field: "price".into(), order: SortOrder::Descending, base: FacetValue::None},ResultSort {field: "language".into(), order: SortOrder::Ascending, base: FacetValue::None}];
+ /// result_sort = vec![ResultSort {field: "location".into(),order: SortOrder::Ascending, base: FacetValue::Point(vec![38.8951, -77.0364])}];
+ /// * `query_rewriting`: Enables query rewriting features such as spelling correction and query auto-completion (QAC).
+ /// The spelling correction of multi-term query strings handles three cases:
+ /// 1. mistakenly inserted space into a correct term led to two incorrect terms: `hels inki` -> `helsinki`
+ /// 2. mistakenly omitted space between two correct terms led to one incorrect combined term: `modernart` -> `modern art`
+ /// 3. multiple independent input terms with/without spelling errors: `cinese indastrialication` -> `chinese industrialization`
+ ///
+ /// Query correction/completion supports phrases "", but is disabled, if +- operators are used, or if a opening quote is used after the first term, or if a closing quote is used before the last term.
+ /// See QueryRewriting enum for details.
+ /// ⚠️ In addition to setting the query_rewriting parameter per query, the incremental creation of the Symspell dictionary during the indexing of documents has to be enabled via the create_index parameter `meta.spelling_correction`.
+ ///
+ /// Facets:
+ /// If query_string is empty, then index facets (collected at index time) are returned, otherwise query facets (collected at query time) are returned.
+ /// Facets are defined in 3 different places:
+ /// the facet fields are defined in schema at create_index,
+ /// the facet field values are set in index_document at index time,
+ /// the query_facets/facet_filter search parameters are specified at query time.
+ /// Facets are then returned in the search result object.
+ Future search(
+ {required String queryString,
+ required QueryType queryTypeDefault,
+ required BigInt offset,
+ required BigInt length,
+ required ResultType resultType,
+ required bool includeUncommited,
+ required List fieldFilter,
+ required List queryFacets,
+ required List facetFilter,
+ required List resultSort,
+ required QueryRewriting queryRewriting});
+}
+
+abstract class SearchShard {
+ Future searchShard(
+ {required String queryString,
+ required QueryType queryTypeDefault,
+ required BigInt offset,
+ required BigInt length,
+ required ResultType resultType,
+ required bool includeUncommited,
+ required List fieldFilter,
+ required List queryFacets,
+ required List facetFilter,
+ required List resultSort});
+}
+
+/// Specifies the default QueryType: The following query types are supported:
+/// - **Union** (OR, disjunction),
+/// - **Intersection** (AND, conjunction),
+/// - **Phrase** (""),
+/// - **Not** (-).
+///
+/// The default QueryType is superseded if the query parser detects that a different query type is specified within the query string (+ - "").
+enum QueryType {
+ /// Union (OR, disjunction)
+ union,
+
+ /// Intersection (AND, conjunction)
+ intersection,
+
+ /// Phrase ("")
+ phrase,
+
+ /// Not (-)
+ not,
+ ;
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormSearchQueryTypeDefault();
+}
+
+/// specifies how to count the frequency of numerical facet field values
+enum RangeType {
+ /// within the specified range
+ countWithinRange,
+
+ /// within the range and all ranges above
+ countAboveRange,
+
+ /// within the range and all ranges below
+ countBelowRange,
+ ;
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormSearchRangeTypeDefault();
+}
+
+/// The following result types are supported:
+/// - **Count** (count all results that match the query, but returning top-k results is not required)
+/// - **Topk** (returns the top-k results per query, but counting all results that match the query is not required)
+/// - **TopkCount** (returns the top-k results per query + count all results that match the query)
+enum ResultType {
+ /// Count all results that match the query, without returning top-k results
+ count,
+
+ /// Return the top-k results per query, without counting all results that match the query
+ topk,
+
+ /// Return the top-k results per query and count all results that match the query
+ topkCount,
+ ;
+
+ static Future default_() =>
+ RustLib.instance.api.seekstormSearchResultTypeDefault();
+}
+
+/// Specifies the sort order for the search results.
+enum SortOrder {
+ /// Ascending sort order
+ ascending,
+
+ /// Descending sort order
+ descending,
+ ;
+}
diff --git a/mobile_app/lib/src/rust/third_party/seekstorm/utils.dart b/mobile_app/lib/src/rust/third_party/seekstorm/utils.dart
new file mode 100644
index 0000000..91066ad
--- /dev/null
+++ b/mobile_app/lib/src/rust/third_party/seekstorm/utils.dart
@@ -0,0 +1,20 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.11.1.
+
+// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
+
+import '../../frb_generated.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
+
+/// Truncates a string to a maximum number of characters.
+Future truncate({required String source, required BigInt maxChars}) =>
+ RustLib.instance.api
+ .seekstormUtilsTruncate(source: source, maxChars: maxChars);
+
+/// Returns a substring of the given string, starting at the specified index and with the specified length.
+Future substring(
+ {required String source,
+ required BigInt start,
+ required BigInt length}) =>
+ RustLib.instance.api
+ .seekstormUtilsSubstring(source: source, start: start, length: length);
diff --git a/mobile_app/lib/src/rust/third_party/tokio/io.dart b/mobile_app/lib/src/rust/third_party/tokio/io.dart
new file mode 100644
index 0000000..7328f7f
--- /dev/null
+++ b/mobile_app/lib/src/rust/third_party/tokio/io.dart
@@ -0,0 +1,2712 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.11.1.
+
+// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
+
+import '../../frb_generated.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
+
+// These functions are ignored because they have generic arguments: `chain`, `chain`, `copy_bidirectional_with_sizes`, `copy_bidirectional`, `copy_buf`, `copy`, `join`, `read_buf`, `read_buf`, `read_exact`, `read_exact`, `read_line`, `read_line`, `read_to_end`, `read_to_end`, `read_to_string`, `read_to_string`, `read_until`, `read_until`, `read`, `read`, `split`, `write_all_buf`, `write_all_buf`, `write_all`, `write_all`, `write_buf`, `write_buf`, `write_vectored`, `write_vectored`, `write`, `write`
+// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `BufReader`, `BufStream`, `BufWriter`, `Chain`, `Join`
+// These functions are ignored (category: IgnoreBecauseNotAllowedOwner): `consume`, `fill_buf`, `flush`, `lines`, `read_f32_le`, `read_f32`, `read_f64_le`, `read_f64`, `read_i128_le`, `read_i128`, `read_i16_le`, `read_i16`, `read_i32_le`, `read_i32`, `read_i64_le`, `read_i64`, `read_i8`, `read_u128_le`, `read_u128`, `read_u16_le`, `read_u16`, `read_u32_le`, `read_u32`, `read_u64_le`, `read_u64`, `read_u8`, `rewind`, `seek`, `shutdown`, `split`, `stream_position`, `take`, `write_f32_le`, `write_f32`, `write_f64_le`, `write_f64`, `write_i128_le`, `write_i128`, `write_i16_le`, `write_i16`, `write_i32_le`, `write_i32`, `write_i64_le`, `write_i64`, `write_i8`, `write_u128_le`, `write_u128`, `write_u16_le`, `write_u16`, `write_u32_le`, `write_u32`, `write_u64_le`, `write_u64`, `write_u8`
+
+/// Constructs a new handle to the standard error of the current process.
+///
+/// The returned handle allows writing to standard error from the within the
+/// Tokio runtime.
+///
+/// Concurrent writes to stderr must be executed with care: Only individual
+/// writes to this [`AsyncWrite`] are guaranteed to be intact. In particular
+/// you should be aware that writes using [`write_all`] are not guaranteed
+/// to occur as a single write, so multiple threads writing data with
+/// [`write_all`] may result in interleaved output.
+///
+/// [`AsyncWrite`]: AsyncWrite
+/// [`write_all`]: crate::io::AsyncWriteExt::write_all()
+///
+/// # Examples
+///
+/// ```
+/// use tokio::io::{self, AsyncWriteExt};
+///
+/// #[tokio::main]
+/// async fn main() -> io::Result<()> {
+/// let mut stderr = io::stderr();
+/// stderr.write_all(b"Print some error here.").await?;
+/// Ok(())
+/// }
+/// ```
+Future stderr() => RustLib.instance.api.tokioIoStderr();
+
+/// Constructs a new handle to the standard input of the current process.
+///
+/// This handle is best used for non-interactive uses, such as when a file
+/// is piped into the application. For technical reasons, `stdin` is
+/// implemented by using an ordinary blocking read on a separate thread, and
+/// it is impossible to cancel that read. This can make shutdown of the
+/// runtime hang until the user presses enter.
+///
+/// For interactive uses, it is recommended to spawn a thread dedicated to
+/// user input and use blocking IO directly in that thread.
+Future stdin() => RustLib.instance.api.tokioIoStdin();
+
+/// Constructs a new handle to the standard output of the current process.
+///
+/// The returned handle allows writing to standard out from the within the
+/// Tokio runtime.
+///
+/// Concurrent writes to stdout must be executed with care: Only individual
+/// writes to this [`AsyncWrite`] are guaranteed to be intact. In particular
+/// you should be aware that writes using [`write_all`] are not guaranteed
+/// to occur as a single write, so multiple threads writing data with
+/// [`write_all`] may result in interleaved output.
+///
+/// [`AsyncWrite`]: AsyncWrite
+/// [`write_all`]: crate::io::AsyncWriteExt::write_all()
+///
+/// # Examples
+///
+/// ```
+/// use tokio::io::{self, AsyncWriteExt};
+///
+/// #[tokio::main]
+/// async fn main() -> io::Result<()> {
+/// let mut stdout = io::stdout();
+/// stdout.write_all(b"Hello world!").await?;
+/// Ok(())
+/// }
+/// ```
+///
+/// The following is an example of using `stdio` with loop.
+///
+/// ```
+/// use tokio::io::{self, AsyncWriteExt};
+///
+/// #[tokio::main]
+/// async fn main() {
+/// let messages = vec!["hello", " world\n"];
+///
+/// // When you use `stdio` in a loop, it is recommended to create
+/// // a single `stdio` instance outside the loop and call a write
+/// // operation against that instance on each loop.
+/// //
+/// // Repeatedly creating `stdout` instances inside the loop and
+/// // writing to that handle could result in mangled output since
+/// // each write operation is handled by a different blocking thread.
+/// let mut stdout = io::stdout();
+///
+/// for message in &messages {
+/// stdout.write_all(message.as_bytes()).await.unwrap();
+/// stdout.flush().await.unwrap();
+/// }
+/// }
+/// ```
+Future stdout() => RustLib.instance.api.tokioIoStdout();
+
+/// Creates a value that is always at EOF for reads, and ignores all data written.
+///
+/// All writes on the returned instance will return `Poll::Ready(Ok(buf.len()))`
+/// and the contents of the buffer will not be inspected.
+///
+/// All reads from the returned instance will return `Poll::Ready(Ok(0))`.
+///
+/// This is an asynchronous version of [`std::io::empty`][std].
+///
+/// [std]: std::io::empty
+///
+/// # Examples
+///
+/// A slightly sad example of not reading anything into a buffer:
+///
+/// ```
+/// use tokio::io::{self, AsyncReadExt};
+///
+/// # #[tokio::main(flavor = "current_thread")]
+/// # async fn main() {
+/// let mut buffer = String::new();
+/// io::empty().read_to_string(&mut buffer).await.unwrap();
+/// assert!(buffer.is_empty());
+/// # }
+/// ```
+///
+/// A convoluted way of getting the length of a buffer:
+///
+/// ```
+/// use tokio::io::{self, AsyncWriteExt};
+///
+/// # #[tokio::main(flavor = "current_thread")]
+/// # async fn main() {
+/// let buffer = vec![1, 2, 3, 5, 8];
+/// let num_bytes = io::empty().write(&buffer).await.unwrap();
+/// assert_eq!(num_bytes, 5);
+/// # }
+/// ```
+Future empty() => RustLib.instance.api.tokioIoEmpty();
+
+/// Create a new pair of `DuplexStream`s that act like a pair of connected sockets.
+///
+/// The `max_buf_size` argument is the maximum amount of bytes that can be
+/// written to a side before the write returns `Poll::Pending`.
+Future<(DuplexStream, DuplexStream)> duplex({required BigInt maxBufSize}) =>
+ RustLib.instance.api.tokioIoDuplex(maxBufSize: maxBufSize);
+
+/// Creates unidirectional buffer that acts like in memory pipe.
+///
+/// The `max_buf_size` argument is the maximum amount of bytes that can be
+/// written to a buffer before the it returns `Poll::Pending`.
+///
+/// # Unify reader and writer
+///
+/// The reader and writer half can be unified into a single structure
+/// of `SimplexStream` that supports both reading and writing or
+/// the `SimplexStream` can be already created as unified structure
+/// using [`SimplexStream::new_unsplit()`].
+///
+/// ```
+/// # async fn ex() -> std::io::Result<()> {
+/// # use tokio::io::{AsyncReadExt, AsyncWriteExt};
+/// let (reader, writer) = tokio::io::simplex(64);
+/// let mut simplex_stream = reader.unsplit(writer);
+/// simplex_stream.write_all(b"hello").await?;
+///
+/// let mut buf = [0u8; 5];
+/// simplex_stream.read_exact(&mut buf).await?;
+/// assert_eq!(&buf, b"hello");
+/// # Ok(())
+/// # }
+/// ```
+Future<(ReadHalfSimplexStream, WriteHalfSimplexStream)> simplex(
+ {required BigInt maxBufSize}) =>
+ RustLib.instance.api.tokioIoSimplex(maxBufSize: maxBufSize);
+
+/// Creates an instance of an async reader that infinitely repeats one byte.
+///
+/// All reads from this reader will succeed by filling the specified buffer with
+/// the given byte.
+///
+/// This is an asynchronous version of [`std::io::repeat`][std].
+///
+/// [std]: std::io::repeat
+///
+/// # Examples
+///
+/// ```
+/// use tokio::io::{self, AsyncReadExt};
+///
+/// # #[tokio::main(flavor = "current_thread")]
+/// # async fn main() {
+/// let mut buffer = [0; 3];
+/// io::repeat(0b101).read_exact(&mut buffer).await.unwrap();
+/// assert_eq!(buffer, [0b101, 0b101, 0b101]);
+/// # }
+/// ```
+Future repeat({required int byte}) =>
+ RustLib.instance.api.tokioIoRepeat(byte: byte);
+
+/// Creates an instance of an async writer which will successfully consume all
+/// data.
+///
+/// All calls to [`poll_write`] on the returned instance will return
+/// `Poll::Ready(Ok(buf.len()))` and the contents of the buffer will not be
+/// inspected.
+///
+/// This is an asynchronous version of [`std::io::sink`][std].
+///
+/// [`poll_write`]: crate::io::AsyncWrite::poll_write()
+/// [std]: std::io::sink
+///
+/// # Examples
+///
+/// ```
+/// use tokio::io::{self, AsyncWriteExt};
+///
+/// # #[tokio::main(flavor = "current_thread")]
+/// # async fn main() -> io::Result<()> {
+/// let buffer = vec![1, 2, 3, 5, 8];
+/// let num_bytes = io::sink().write(&buffer).await?;
+/// assert_eq!(num_bytes, 5);
+/// Ok(())
+/// # }
+/// ```
+Future sink() => RustLib.instance.api.tokioIoSink();
+
+// Rust type: RustOpaqueMoi>
+abstract class DuplexStream implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class Empty implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class FillBufSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class FlushSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class Interest implements RustOpaqueInterface {
+ /// Add together two `Interest` values.
+ ///
+ /// This function works from a `const` context.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tokio::io::Interest;
+ ///
+ /// const BOTH: Interest = Interest::READABLE.add(Interest::WRITABLE);
+ ///
+ /// assert!(BOTH.is_readable());
+ /// assert!(BOTH.is_writable());
+ Future add({required Interest other});
+
+ /// Returns true if the value includes error interest.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tokio::io::Interest;
+ ///
+ /// assert!(Interest::ERROR.is_error());
+ /// assert!(!Interest::WRITABLE.is_error());
+ ///
+ /// let combined = Interest::READABLE | Interest::ERROR;
+ /// assert!(combined.is_error());
+ /// ```
+ Future isError();
+
+ /// Returns true if the value includes readable interest.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tokio::io::Interest;
+ ///
+ /// assert!(Interest::READABLE.is_readable());
+ /// assert!(!Interest::WRITABLE.is_readable());
+ ///
+ /// let both = Interest::READABLE | Interest::WRITABLE;
+ /// assert!(both.is_readable());
+ /// ```
+ Future isReadable();
+
+ /// Returns true if the value includes writable interest.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tokio::io::Interest;
+ ///
+ /// assert!(!Interest::READABLE.is_writable());
+ /// assert!(Interest::WRITABLE.is_writable());
+ ///
+ /// let both = Interest::READABLE | Interest::WRITABLE;
+ /// assert!(both.is_writable());
+ /// ```
+ Future isWritable();
+
+ /// Remove `Interest` from `self`.
+ ///
+ /// Interests present in `other` but *not* in `self` are ignored.
+ ///
+ /// Returns `None` if the set would be empty after removing `Interest`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tokio::io::Interest;
+ ///
+ /// const RW_INTEREST: Interest = Interest::READABLE.add(Interest::WRITABLE);
+ ///
+ /// let w_interest = RW_INTEREST.remove(Interest::READABLE).unwrap();
+ /// assert!(!w_interest.is_readable());
+ /// assert!(w_interest.is_writable());
+ ///
+ /// // Removing all interests from the set returns `None`.
+ /// assert_eq!(w_interest.remove(Interest::WRITABLE), None);
+ ///
+ /// // Remove all interests at once.
+ /// assert_eq!(RW_INTEREST.remove(RW_INTEREST), None);
+ /// ```
+ Future remove({required Interest other});
+}
+
+// Rust type: RustOpaqueMoi>>
+abstract class LinesSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadF32LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadF32MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadF64LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadF64MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadHalfSimplexStream implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadI128LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadI128MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadI16LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadI16MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadI32LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadI32MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadI64LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadI64MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadI8MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadU128LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadU128MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadU16LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadU16MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadU32LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadU32MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadU64LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadU64MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ReadU8MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class Ready implements RustOpaqueInterface {
+ /// Returns true if `Ready` is the empty set.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tokio::io::Ready;
+ ///
+ /// assert!(Ready::EMPTY.is_empty());
+ /// assert!(!Ready::READABLE.is_empty());
+ /// ```
+ Future isEmpty();
+
+ /// Returns `true` if the value includes error `readiness`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tokio::io::Ready;
+ ///
+ /// assert!(!Ready::EMPTY.is_error());
+ /// assert!(!Ready::WRITABLE.is_error());
+ /// assert!(Ready::ERROR.is_error());
+ /// ```
+ Future isError();
+
+ /// Returns `true` if the value includes read-closed `readiness`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tokio::io::Ready;
+ ///
+ /// assert!(!Ready::EMPTY.is_read_closed());
+ /// assert!(!Ready::READABLE.is_read_closed());
+ /// assert!(Ready::READ_CLOSED.is_read_closed());
+ /// ```
+ Future isReadClosed();
+
+ /// Returns `true` if the value includes `readable`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tokio::io::Ready;
+ ///
+ /// assert!(!Ready::EMPTY.is_readable());
+ /// assert!(Ready::READABLE.is_readable());
+ /// assert!(Ready::READ_CLOSED.is_readable());
+ /// assert!(!Ready::WRITABLE.is_readable());
+ /// ```
+ Future isReadable();
+
+ /// Returns `true` if the value includes writable `readiness`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tokio::io::Ready;
+ ///
+ /// assert!(!Ready::EMPTY.is_writable());
+ /// assert!(!Ready::READABLE.is_writable());
+ /// assert!(Ready::WRITABLE.is_writable());
+ /// assert!(Ready::WRITE_CLOSED.is_writable());
+ /// ```
+ Future isWritable();
+
+ /// Returns `true` if the value includes write-closed `readiness`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tokio::io::Ready;
+ ///
+ /// assert!(!Ready::EMPTY.is_write_closed());
+ /// assert!(!Ready::WRITABLE.is_write_closed());
+ /// assert!(Ready::WRITE_CLOSED.is_write_closed());
+ /// ```
+ Future isWriteClosed();
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class Repeat implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class SeekFrom implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class SeekSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class ShutdownSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class SimplexStream implements RustOpaqueInterface {
+ /// Creates unidirectional buffer that acts like in memory pipe. To create split
+ /// version with separate reader and writer you can use [`simplex`] function.
+ ///
+ /// The `max_buf_size` argument is the maximum amount of bytes that can be
+ /// written to a buffer before the it returns `Poll::Pending`.
+ static Future newUnsplit({required BigInt maxBufSize}) =>
+ RustLib.instance.api
+ .tokioIoSimplexStreamNewUnsplit(maxBufSize: maxBufSize);
+}
+
+// Rust type: RustOpaqueMoi>
+abstract class Sink implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class SplitSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class Stderr implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class Stdin implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>
+abstract class Stdout implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class TakeSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteF32LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteF32MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteF64LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteF64MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteHalfSimplexStream implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteI128LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteI128MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteI16LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteI16MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteI32LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteI32MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteI64LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteI64MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteI8MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteU128LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteU128MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteU16LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteU16MutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi>>
+abstract class WriteU32LeMutSelf implements RustOpaqueInterface {}
+
+// Rust type: RustOpaqueMoi