tiny-counter 0.1.0

Track event counts across time windows with fixed memory and fast queries
Documentation
// Storage backends and serialization formats.
//
// This example shows how to:
// - Use different storage backends (Memory, FilePerEvent, SQLite)
// - Choose serialization formats (Bincode, JSON)
// - Handle dirty tracking for efficient saves
// - Use close() for explicit cleanup
//
// Run with: cargo run --example persistence
// For SQLite: cargo run --example persistence --features storage-sqlite
// For JSON format: cargo run --example persistence --features serde-json

use tiny_counter::EventStore;

#[cfg(feature = "serde")]
use tiny_counter::storage::MemoryStorage;

#[cfg(not(feature = "serde"))]
fn main() {
    println!("This example requires the 'serde' feature.");
    println!("Run with: cargo run --example persistence --all-features");
}

#[cfg(feature = "serde")]
fn main() {
    println!("=== Persistence with Storage Backends ===\n");

    // Memory storage (no actual file I/O, useful for testing)
    println!("1. MemoryStorage:");
    {
        let store = EventStore::builder()
            .with_storage(MemoryStorage::new())
            .build()
            .unwrap();

        store.record("event1");
        store.record("event2");
        store.record_count("event3", 5);

        println!("  Recorded events");
        println!("  Is dirty: {}", store.is_dirty());

        // Explicitly persist
        store.persist().expect("Failed to persist");
        println!("  Persisted to memory storage");
        println!("  Is dirty after persist: {}\n", store.is_dirty());
    }

    // File-per-event storage
    #[cfg(feature = "storage-fs")]
    {
        use std::fs;
        use tiny_counter::storage::FilePerEvent;

        println!("2. FilePerEvent storage:");
        let dir = "example_storage_data";

        // Clean up from previous runs
        let _ = fs::remove_dir_all(dir);

        {
            let store = EventStore::builder()
                .with_storage(FilePerEvent::new(dir, ".dat").unwrap())
                .build()
                .unwrap();

            store.record("file_event_1");
            store.record_count("file_event_2", 10);

            println!("  Recorded events");
            store.persist().expect("Failed to persist");
            println!("  Persisted to disk at: {}/", dir);
        }

        // Load from disk in new instance
        {
            let store = EventStore::builder()
                .with_storage(FilePerEvent::new(dir, ".dat").unwrap())
                .build()
                .unwrap();

            let count1 = store.query("file_event_1").last_days(7).sum().unwrap_or(0);
            let count2 = store.query("file_event_2").last_days(7).sum().unwrap_or(0);

            println!("  Loaded from disk:");
            println!("    file_event_1: {}", count1);
            println!("    file_event_2: {}\n", count2);
        }

        // Clean up
        let _ = fs::remove_dir_all(dir);
    }

    #[cfg(not(feature = "storage-fs"))]
    {
        println!("2. FilePerEvent storage: (enable with --features storage-fs)\n");
    }

    // SQLite storage
    #[cfg(feature = "storage-sqlite")]
    {
        use std::fs;
        use tiny_counter::storage::Sqlite;

        println!("3. SQLite storage:");
        let db_path = "example_events.db";

        // Clean up from previous runs
        let _ = fs::remove_file(db_path);

        {
            let store = EventStore::builder()
                .with_storage(Sqlite::open(db_path).unwrap())
                .build()
                .unwrap();

            store.record("sqlite_event_1");
            store.record_count("sqlite_event_2", 15);

            println!("  Recorded events");
            store.persist().expect("Failed to persist");
            println!("  Persisted to SQLite database: {}", db_path);
        }

        // Load from database
        {
            let store = EventStore::builder()
                .with_storage(Sqlite::open(db_path).unwrap())
                .build()
                .unwrap();

            let count1 = store
                .query("sqlite_event_1")
                .last_days(7)
                .sum()
                .unwrap_or(0);
            let count2 = store
                .query("sqlite_event_2")
                .last_days(7)
                .sum()
                .unwrap_or(0);

            println!("  Loaded from database:");
            println!("    sqlite_event_1: {}", count1);
            println!("    sqlite_event_2: {}\n", count2);
        }

        // Clean up
        let _ = fs::remove_file(db_path);
    }

    #[cfg(not(feature = "storage-sqlite"))]
    {
        println!("3. SQLite storage: (enable with --features storage-sqlite)\n");
    }

    // JSON format (human-readable)
    #[cfg(all(feature = "storage-fs", feature = "serde-json"))]
    {
        use std::fs;
        use tiny_counter::formatter::JsonFormat;
        use tiny_counter::storage::FilePerEvent;

        println!("4. JSON format (human-readable):");
        let dir = "example_json_data";

        // Clean up from previous runs
        let _ = fs::remove_dir_all(dir);

        {
            let store = EventStore::builder()
                .with_storage(FilePerEvent::new(dir, ".json").unwrap())
                .with_format(JsonFormat)
                .build()
                .unwrap();

            store.record("json_event");
            store.record_count("json_counter", 42);

            store.persist().expect("Failed to persist");
            println!("  Persisted as JSON files in: {}/", dir);

            // Show a sample file
            if let Ok(entries) = fs::read_dir(dir) {
                for entry in entries.flatten().take(1) {
                    if let Ok(content) = fs::read_to_string(entry.path()) {
                        println!("\n  Sample JSON content:");
                        println!("  {}\n", &content[..200.min(content.len())]);
                        if content.len() > 200 {
                            println!("  ...");
                        }
                    }
                }
            }
        }

        // Clean up
        let _ = fs::remove_dir_all(dir);
    }

    #[cfg(not(all(feature = "storage-fs", feature = "serde-json")))]
    {
        println!("4. JSON format: (enable with --features storage-fs,serde-json)\n");
    }

    // Dirty tracking and close() pattern
    println!("5. Dirty tracking and cleanup:");
    {
        let store = EventStore::builder()
            .with_storage(MemoryStorage::new())
            .build()
            .unwrap();

        store.record("event1");
        println!("  After record: dirty={}", store.is_dirty());

        store.persist().expect("Failed to persist");
        println!("  After persist: dirty={}", store.is_dirty());

        store.record("event2");
        println!("  After another record: dirty={}", store.is_dirty());

        // Best practice: use close() for explicit cleanup
        store.close().expect("Failed to close");
        println!("  Called close() (persists and drops)\n");
    }

    println!("✓ Persistence complete!");
}