torex_local_store 0.1.6 copy "torex_local_store: ^0.1.6" to clipboard
torex_local_store: ^0.1.6 copied to clipboard

Ultra-high-performance local storage engine for Flutter powered by Rust. LSM-tree based, zero-copy reads, memory-mapped files, crash recovery, and reactive streams.

example/lib/main.dart

// TorexLocalStore — Example Application
//
// Demonstrates the full API surface:
//  • Basic CRUD (put / get / delete / exists)
//  • JSON storage
//  • Typed objects with TorexCodec
//  • Batch operations
//  • Query builder (prefix / range / reverse)
//  • Reactive watch streams
//  • Collection stats
//
// Run: flutter run
// Build native first: cd .. && ./scripts/build_native.sh <platform>

import 'dart:convert';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:torex_local_store/torex_local_store.dart';

void main() => runApp(const TorexExampleApp());

// ─── Example data model ───────────────────────────────────────────────────────

class User {
  final String id;
  final String name;
  final int age;

  const User({required this.id, required this.name, required this.age});

  Map<String, dynamic> toJson() => {'id': id, 'name': name, 'age': age};

  factory User.fromJson(Map<String, dynamic> j) =>
      User(id: j['id'] as String, name: j['name'] as String, age: j['age'] as int);

  @override
  String toString() => 'User($id, $name, $age)';
}

/// Custom TorexCodec for the User model.
class UserCodec extends TorexCodec<User> {
  const UserCodec();

  @override
  Uint8List encode(User value) =>
      Uint8List.fromList(utf8.encode(jsonEncode(value.toJson())));

  @override
  User decode(Uint8List bytes) =>
      User.fromJson(jsonDecode(utf8.decode(bytes)) as Map<String, dynamic>);
}

// ─── App ──────────────────────────────────────────────────────────────────────

class TorexExampleApp extends StatelessWidget {
  const TorexExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'TorexLocalStore Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const ExamplePage(),
    );
  }
}

// ─── ExamplePage ──────────────────────────────────────────────────────────────

class ExamplePage extends StatefulWidget {
  const ExamplePage({super.key});

  @override
  State<ExamplePage> createState() => _ExamplePageState();
}

class _ExamplePageState extends State<ExamplePage> {
  final List<String> _logs = [];
  bool _running = false;

  void _log(String line) => setState(() => _logs.insert(0, line));

  void _sep(String title) => _log('── $title ──────────────────');

  // ── Demos ──────────────────────────────────────────────────────────────────

  Future<void> _runBasicCrud() async {
    _sep('Basic CRUD');
    final box = Torex.box('demo');

    await box.put('greeting', 'Hello, Torex!');
    final val = await box.get('greeting');
    _log('put/get:     $val');

    final exists = await box.exists('greeting');
    _log('exists:      $exists');

    final missing = await box.getOrDefault('no_such_key', 'default_value');
    _log('getOrDefault: $missing');

    await box.update(
      'counter',
      (v) => '${(int.tryParse(v ?? '0') ?? 0) + 1}',
    );
    _log('update:      ${await box.get('counter')}');

    await box.delete('greeting');
    _log('after delete: ${await box.get('greeting')}');

    _log('✅ Basic CRUD OK');
  }

  Future<void> _runJsonStorage() async {
    _sep('JSON Storage');
    final box = Torex.box('demo');

    await box.putJson('config', {
      'theme': 'dark',
      'language': 'uz',
      'version': 1,
    });
    final config = await box.getJson('config');
    _log('putJson/getJson: $config');

    await box.updateJson(
      'config',
      (c) => {...?c, 'version': (c?['version'] as int? ?? 0) + 1},
    );
    _log('updateJson:  ${await box.getJson('config')}');

    final fallback = await box.getJsonOrDefault('no_key', {'status': 'empty'});
    _log('getJsonOrDefault: $fallback');

    _log('✅ JSON Storage OK');
  }

  Future<void> _runTypedObjects() async {
    _sep('Typed Objects (TorexCodec)');
    final box = Torex.box('users');
    const codec = UserCodec();

    final users = [
      const User(id: 'u:1', name: 'Ali', age: 25),
      const User(id: 'u:2', name: 'Vali', age: 30),
      const User(id: 'u:3', name: 'Hasan', age: 22),
    ];

    // Batch write with typed objects
    await box.batchPutObjects(
      users.map((u) => (u.id, u)).toList(),
      codec,
    );
    _log('batchPutObjects: ${users.length} users');

    final loaded = await box.getObject('u:2', codec);
    _log('getObject u:2:   $loaded');

    final fallback = await box.getObjectOrDefault(
      'u:999',
      codec,
      const User(id: 'u:999', name: 'Guest', age: 0),
    );
    _log('getObjectOrDefault: $fallback');

    _log('✅ Typed Objects OK');
  }

  Future<void> _runBatchOps() async {
    _sep('Batch Operations');
    final box = Torex.box('batch_demo');

    // Batch put — single WAL fsync for all 1000 entries
    final entries = List.generate(
      1000,
      (i) => ('item:${i.toString().padLeft(4, '0')}', 'value_$i'),
    );
    final sw = Stopwatch()..start();
    await box.batchPut(entries);
    sw.stop();
    _log('batchPut 1000: ${sw.elapsedMicroseconds} µs');

    // Batch JSON
    await box.batchPutJson([
      ('product:1', {'name': 'Phone', 'price': 500}),
      ('product:2', {'name': 'Laptop', 'price': 1200}),
    ]);
    _log('batchPutJson: 2 products');

    // getAll
    final results = await box.getAll(['item:0000', 'item:0001', 'no_key']);
    _log('getAll:       $results');

    // Batch delete
    await box.batchDelete(['item:0000', 'item:0001', 'item:0002']);
    _log('batchDelete:  3 keys removed');

    _log('✅ Batch Operations OK');
  }

  Future<void> _runQueryBuilder() async {
    _sep('Query Builder');
    final box = Torex.box('users');

    // All users with prefix "u:"
    final all = await box.query().prefix('u:').find();
    _log('prefix u::  ${all.length} results');

    // Limit + reverse
    final top2 = await box.query().prefix('u:').limit(2).reverse().find();
    _log('limit(2) reverse: ${top2.map((e) => e.$1).toList()}');

    // Range scan
    await Torex.box('items').batchPut(
      List.generate(5, (i) => ('item_${String.fromCharCode(65 + i)}', 'val')),
    );
    final range = await Torex.box('items')
        .query()
        .startKey('item_B')
        .endKey('item_D')
        .find();
    _log('range B..D: ${range.map((e) => e.$1).toList()}');

    // Scan shortcuts
    final prefixed = await box.scanPrefix('u:');
    _log('scanPrefix: ${prefixed.length} users');

    // findJson with query builder
    final jsonResults = await box
        .query()
        .prefix('product:')
        .findJson();
    _log('findJson products: ${jsonResults.map((e) => e.$2['name']).toList()}');

    _log('✅ Query Builder OK');
  }

  Future<void> _runWatchStream() async {
    _sep('Reactive Watch Stream');
    final box = Torex.box('watch_demo');

    final events = <String>[];
    final sub = box.watch().listen((e) {
      events.add('type=${e.changeType} key=${utf8.decode(e.key)}');
    });

    await box.put('watched_key', 'initial');
    await box.put('watched_key', 'updated');
    await box.delete('watched_key');

    // Give the poll loop time to catch up
    await Future.delayed(const Duration(milliseconds: 150));

    _log('watch events: ${events.length} received');
    for (final e in events) {
      _log('  → $e');
    }

    await sub.cancel();
    _log('✅ Watch Stream OK');
  }

  Future<void> _runStats() async {
    _sep('Stats & Management');
    final box = Torex.box('demo');

    final count = await box.count();
    _log('count:    $count');

    final keys = await box.keys();
    _log('keys:     $keys');

    final stats = await box.stats();
    _log('stats:    $stats');

    await box.flush();
    _log('flush:    OK');

    final path = await Torex.currentPath();
    _log('path:     $path');

    final version = await Torex.version();
    _log('version:  $version');

    _log('✅ Stats & Management OK');
  }

  Future<void> _runAll() async {
    setState(() {
      _running = true;
      _logs.clear();
    });
    try {
      await _runBasicCrud();
      await _runJsonStorage();
      await _runTypedObjects();
      await _runBatchOps();
      await _runQueryBuilder();
      await _runWatchStream();
      await _runStats();
      _log('');
      _log('🎉 All examples completed successfully!');
    } catch (e, st) {
      _log('❌ Error: $e');
      _log('   $st');
    } finally {
      setState(() => _running = false);
    }
  }

  // ── Build ──────────────────────────────────────────────────────────────────

  @override
  Widget build(BuildContext context) {
    final cs = Theme.of(context).colorScheme;

    return Scaffold(
      appBar: AppBar(
        backgroundColor: cs.inversePrimary,
        title: const Text('TorexLocalStore Example'),
        actions: [
          if (_logs.isNotEmpty)
            IconButton(
              icon: const Icon(Icons.clear_all),
              tooltip: 'Clear logs',
              onPressed: () => setState(_logs.clear),
            ),
        ],
      ),
      body: Column(
        children: [
          // ── Run button ────────────────────────────────────────────
          Padding(
            padding: const EdgeInsets.all(16),
            child: FilledButton.icon(
              onPressed: _running ? null : _runAll,
              icon: _running
                  ? const SizedBox(
                      width: 18,
                      height: 18,
                      child: CircularProgressIndicator(
                        strokeWidth: 2,
                        color: Colors.white,
                      ),
                    )
                  : const Icon(Icons.play_arrow_rounded),
              label: Text(_running ? 'Running...' : 'Run All Examples'),
              style: FilledButton.styleFrom(
                minimumSize: const Size(double.infinity, 48),
              ),
            ),
          ),

          // ── Individual demos ──────────────────────────────────────
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            child: Wrap(
              spacing: 8,
              runSpacing: 4,
              children: [
                _chip('CRUD', _runBasicCrud),
                _chip('JSON', _runJsonStorage),
                _chip('Typed', _runTypedObjects),
                _chip('Batch', _runBatchOps),
                _chip('Query', _runQueryBuilder),
                _chip('Watch', _runWatchStream),
                _chip('Stats', _runStats),
              ],
            ),
          ),

          const SizedBox(height: 8),

          // ── Console log ───────────────────────────────────────────
          Expanded(
            child: Container(
              color: Colors.black87,
              child: ListView.builder(
                padding: const EdgeInsets.all(10),
                itemCount: _logs.length,
                itemBuilder: (_, i) {
                  final line = _logs[i];
                  Color color = Colors.white60;
                  if (line.startsWith('✅') || line.startsWith('🎉')) {
                    color = Colors.greenAccent;
                  } else if (line.startsWith('❌')) {
                    color = Colors.redAccent;
                  } else if (line.startsWith('──')) {
                    color = Colors.yellowAccent;
                  }
                  return Text(
                    line,
                    style: TextStyle(
                      color: color,
                      fontFamily: 'monospace',
                      fontSize: 12,
                    ),
                  );
                },
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _chip(String label, Future<void> Function() fn) {
    return ActionChip(
      label: Text(label, style: const TextStyle(fontSize: 12)),
      onPressed: _running
          ? null
          : () async {
              setState(() {
                _running = true;
                _logs.clear();
              });
              try {
                await fn();
              } catch (e) {
                _log('❌ $e');
              } finally {
                setState(() => _running = false);
              }
            },
    );
  }
}
1
likes
150
points
27
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Ultra-high-performance local storage engine for Flutter powered by Rust. LSM-tree based, zero-copy reads, memory-mapped files, crash recovery, and reactive streams.

Repository (GitHub)
View/report issues

Topics

#database #storage #rust #ffi #nosql

License

MIT (license)

Dependencies

ffi, flutter, flutter_rust_bridge, meta, path, path_provider

More

Packages that depend on torex_local_store

Packages that implement torex_local_store