| Crates.io | offline_first_core |
| lib.rs | offline_first_core |
| version | 0.5.0 |
| created_at | 2025-02-20 06:17:25.752765+00 |
| updated_at | 2025-07-13 13:16:57.439999+00 |
| description | High-performance LMDB-based local storage library optimized for FFI integration with Flutter and cross-platform applications |
| homepage | |
| repository | https://github.com/JhonaCodes/offline_first_core |
| max_upload_size | |
| id | 1562290 |
| size | 194,161 |
High-performance LMDB-based local storage library optimized for FFI integration with Flutter and cross-platform applications.
import 'dart:ffi';
import 'dart:convert';
// 1. Load the native library
final dylib = DynamicLibrary.open('liboffline_first_core.so');
// 2. Define FFI functions
typedef CreateDbNative = Pointer Function(Pointer<Utf8>);
typedef CreateDb = Pointer Function(Pointer<Utf8>);
final createDb = dylib.lookupFunction<CreateDbNative, CreateDb>('create_db');
// 3. Use the database
final dbPointer = createDb("my_app_database".toNativeUtf8());
final jsonData = jsonEncode({
"id": "user_123",
"hash": "content_hash",
"data": {"name": "John Doe", "email": "john@example.com"}
});
final result = pushData(dbPointer, jsonData.toNativeUtf8());
use offline_first_core::{AppDbState, LocalDbModel};
use serde_json::json;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize database
let db = AppDbState::init("my_database".to_string())?;
// Create and store data
let user = LocalDbModel {
id: "user_123".to_string(),
hash: "content_hash".to_string(),
data: json!({"name": "John Doe", "email": "john@example.com"}),
};
db.push(user)?;
// Retrieve data
let user = db.get_by_id("user_123")?.unwrap();
println!("User: {}", user.data["name"]);
Ok(())
}
| Function | Rust | FFI | Description |
|---|---|---|---|
| Initialize | AppDbState::init(name) |
create_db(name) |
Create or open database |
| Insert | db.push(model) |
push_data(db, json) |
Add new record |
| Get by ID | db.get_by_id(id) |
get_by_id(db, id) |
Retrieve specific record |
| Get All | db.get() |
get_all(db) |
Retrieve all records |
| Update | db.update(model) |
update_data(db, json) |
Update existing record |
| Delete | db.delete_by_id(id) |
delete_by_id(db, id) |
Remove record |
| Clear | db.clear_all_records() |
clear_all_records(db) |
Remove all records |
| Reset | db.reset_database(name) |
reset_database(db, name) |
Reset database |
| Close | db.close_database() |
close_database(db) |
Close connection |
pub struct LocalDbModel {
pub id: String, // Unique identifier (cannot be empty)
pub hash: String, // Content hash for versioning
pub data: JsonValue, // Your JSON data
}
use offline_first_core::{AppDbState, LocalDbModel};
use serde_json::json;
fn save_preferences() -> Result<(), Box<dyn std::error::Error>> {
let db = AppDbState::init("user_settings".to_string())?;
let preferences = LocalDbModel {
id: "app_preferences".to_string(),
hash: "v1.0".to_string(),
data: json!({
"theme": "dark",
"language": "en",
"notifications": true
}),
};
db.push(preferences)?;
Ok(())
}
fn add_to_cart(product_id: &str, quantity: i32) -> Result<(), Box<dyn std::error::Error>> {
let db = AppDbState::init("shopping_cart".to_string())?;
let item = LocalDbModel {
id: product_id.to_string(),
hash: format!("cart_{}", chrono::Utc::now().timestamp()),
data: json!({
"product_id": product_id,
"quantity": quantity,
"price": 29.99
}),
};
db.push(item)?;
Ok(())
}
fn cache_article(article_id: &str, content: &str) -> Result<(), Box<dyn std::error::Error>> {
let db = AppDbState::init("article_cache".to_string())?;
let article = LocalDbModel {
id: article_id.to_string(),
hash: format!("article_{}", md5::compute(content)),
data: json!({
"title": "Article Title",
"content": content,
"cached_at": chrono::Utc::now().to_rfc3339()
}),
};
db.push(article)?;
Ok(())
}
use offline_first_core::{AppDbState, AppResponse};
match db.push(model) {
Ok(_) => println!("โ
Success"),
Err(AppResponse::DatabaseError(msg)) => eprintln!("๐พ Database error: {}", msg),
Err(AppResponse::SerializationError(msg)) => eprintln!("๐ JSON error: {}", msg),
Err(AppResponse::NotFound(msg)) => eprintln!("๐ Not found: {}", msg),
Err(other) => eprintln!("๐ฅ Other error: {}", other),
}
fn bulk_insert(records: Vec<LocalDbModel>) -> Result<usize, Box<dyn std::error::Error>> {
let db = AppDbState::init("bulk_data".to_string())?;
let mut success_count = 0;
for model in records {
if db.push(model).is_ok() {
success_count += 1;
}
}
Ok(success_count)
}
class DatabaseManager {
static Pointer? _dbPointer;
static void initDatabase() {
_dbPointer = createDb("my_app_db".toNativeUtf8());
}
static void closeDatabase() {
if (_dbPointer != null) {
closeDatabase(_dbPointer!);
_dbPointer = null;
}
}
}
Add to your Cargo.toml:
[dependencies]
offline_first_core = "0.3.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
For FFI projects:
[lib]
name = "my_storage_lib"
crate-type = ["staticlib", "cdylib"]
# Debug build
cargo build
# Release build
cargo build --release
# Cross-platform builds
cargo build --target aarch64-apple-ios --release # iOS
cargo build --target aarch64-linux-android --release # Android
cargo build --target x86_64-pc-windows-msvc --release # Windows
// โ Empty IDs not supported
let invalid = LocalDbModel {
id: "".to_string(), // Will fail!
// ...
};
// โ
Always use non-empty IDs
let valid = LocalDbModel {
id: "user_123".to_string(), // Good!
// ...
};
// โ
Always check null pointers
void* db = create_db("my_db");
if (db == NULL) {
// Handle error
return;
}
// โ
Free returned strings
const char* result = get_by_id(db, "user_1");
if (result != NULL) {
// Use result...
free((void*)result); // Important!
}
// โ
DO: Reuse connections
let db = AppDbState::init("my_db".to_string())?;
for i in 0..1000 {
db.push(create_model(i))?; // Efficient
}
// โ DON'T: Create new connections
for i in 0..1000 {
let db = AppDbState::init("my_db".to_string())?; // Slow!
db.push(create_model(i))?;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_crud() {
let db = AppDbState::init("test_db".to_string()).unwrap();
let model = LocalDbModel {
id: "test_1".to_string(),
hash: "test_hash".to_string(),
data: json!({"name": "Test User"}),
};
// Insert
assert!(db.push(model).is_ok());
// Read
let retrieved = db.get_by_id("test_1").unwrap();
assert!(retrieved.is_some());
// Update
let mut updated = retrieved.unwrap();
updated.data["name"] = json!("Updated User");
assert!(db.update(updated).is_ok());
// Delete
assert!(db.delete_by_id("test_1").unwrap());
}
}
// pubspec.yaml
dependencies:
ffi: ^2.0.0
// lib/database.dart
import 'dart:ffi';
import 'dart:io';
class NativeDatabase {
late DynamicLibrary _lib;
NativeDatabase() {
if (Platform.isAndroid) {
_lib = DynamicLibrary.open('liboffline_first_core.so');
} else if (Platform.isIOS) {
_lib = DynamicLibrary.process();
}
}
// Define your FFI functions here...
}
// Install react-native-ffi
npm install react-native-ffi
// Use the library
import { NativeModules } from 'react-native';
const { OfflineFirstCore } = NativeModules;
async function saveData(id, data) {
const result = await OfflineFirstCore.pushData(id, JSON.stringify(data));
return JSON.parse(result);
}
close_database() functionclear_all_records() and reset_database()git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)This project is licensed under the MIT License - see the LICENSE file for details.
Made with โค๏ธ for developers who need reliable offline storage
โญ Star on GitHub โข ๐ฆ View on Crates.io โข ๐ Read the Docs