+
+
+
+
+
\ No newline at end of file
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
index d048149..7509d9f 100644
--- a/mobile_app/lib/home.dart
+++ b/mobile_app/lib/home.dart
@@ -1,88 +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 StatelessWidget {
- const HomeApp({super.key});
+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;
- //TODO: cap at 5
- late List searchedItems = ['A', 'B', 'C'];
- late List recentDocuments = [
- 'A',
- 'B',
- 'C'
- ]; //TODO:switch to class, so we can access more attributes outside of title
+ 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
- ),
+ 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
+ backgroundColor: WidgetStateProperty.all(Colors.grey[200]),
+ elevation: WidgetStateProperty.all(0), // Flat style
- onChanged: (text) {
- //TODO: Handle search logic here
- //spawn process into a new isolate to prevent u.i jank
- },
- onSubmitted: (text) {
- //TODO: Handle enter key press,
- //TODO: similar to above depending on latency we may just use this
- },
- ),
+ 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(searchedItems.length, (int index) {
- return SizedBox(
- width: 70,
- child: TextButton(
- //TODO: style to make borders visible
- onPressed: () {
- //TODO: Handle click, popular search bar with text controller
- },
- child:
- Text(searchedItems[index]), // Display results from search
- ));
- })),
- const ListTile(
- leading: Text(
- "Recent Documents",
- style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
- )),
- Column(
- children: List.generate(recentDocuments.length, (int index) {
- return ListTile(
- leading: const Icon(Icons.picture_as_pdf),
- onTap: () {
- // Handle click
- },
- trailing: const Icon(Icons.chevron_right),
- title: Text(recentDocuments[index]), // Display results from search
- );
- }))
- ],
- ));
+ ),
+ 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 e62081b..abcdf67 100644
--- a/mobile_app/lib/main.dart
+++ b/mobile_app/lib/main.dart
@@ -1,10 +1,40 @@
+import 'dart:io';
+
import 'package:flutter/material.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());
}
@@ -17,6 +47,7 @@ class MyApp extends StatefulWidget {
class MyAppState extends State {
PageController pageController = PageController();
+ List folders = [];
int selectIndex = 0;
void onPageChanged(int index) {
@@ -25,6 +56,27 @@ class MyAppState extends State {
});
}
+ @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);
}
@@ -35,21 +87,21 @@ class MyAppState extends State {
home: Scaffold(
appBar: AppBar(title: const Text('Acho')),
body: PageView(
- children: [HomeApp(), SettingsApp()],
+ children: [HomeApp(files: folders), SettingsApp()],
controller: pageController,
onPageChanged: onPageChanged,
),
bottomNavigationBar: BottomNavigationBar(
onTap: onItemTap,
selectedItemColor: Colors.brown,
- items: const [
+ items: [
BottomNavigationBarItem(
backgroundColor: Colors.red,
label: 'Home',
icon: Icon(Icons.home_filled),
- activeIcon: HomeApp(),
+ activeIcon: HomeApp(files: []),
),
- BottomNavigationBarItem(
+ const BottomNavigationBarItem(
label: 'Settings',
icon: Icon(Icons.settings),
activeIcon: SettingsApp(),
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