| Crates.io | kanban-persistence |
| lib.rs | kanban-persistence |
| version | 0.1.16 |
| created_at | 2025-12-21 01:15:03.477303+00 |
| updated_at | 2025-12-21 20:48:21.927422+00 |
| description | Persistence layer for the kanban project management tool with progressive saving and multi-instance support |
| homepage | https://github.com/fulsomenko/kanban |
| repository | https://github.com/fulsomenko/kanban |
| max_upload_size | |
| id | 1997190 |
| size | 90,928 |
Persistence layer for the kanban project management tool. Handles JSON storage, format versioning, and data migration.
Add to your Cargo.toml:
[dependencies]
kanban-persistence = { path = "../kanban-persistence" }
.v1.backup before migrationMain persistence store implementation:
use kanban_persistence::{JsonFileStore, PersistenceStore};
// Create store
let store = JsonFileStore::new("board.json");
// Get instance ID
let instance_id = store.instance_id();
// Save data
let snapshot = StoreSnapshot {
data: serde_json::to_vec(&data)?,
metadata: PersistenceMetadata::new(instance_id),
};
store.save(snapshot).await?;
// Load data (automatically migrates V1 to V2)
let (snapshot, metadata) = store.load().await?;
Manages state mutations and persistence:
use kanban_tui::state::StateManager;
use kanban_domain::commands::Command;
let mut manager = StateManager::new(Some("board.json".into()));
// Execute command (sets dirty flag)
manager.execute_with_context(
&mut boards,
&mut columns,
&mut cards,
&mut sprints,
&mut archived_cards,
Box::new(CreateCard { /* ... */ }),
)?;
// Periodically save (respects 500ms debounce)
manager.save_if_needed(&snapshot).await?;
// Force save immediately (bypasses debounce)
manager.save_now(&snapshot).await?;
kanban-core
↑
└── kanban-domain
↑
└── kanban-persistence
↑
└── kanban-tui (StateManager uses persistence)
User Input
↓
Event Handler
↓
Command Creation
↓
StateManager::execute_command()
↓
CommandContext::execute()
↓
Data Mutation
↓
Dirty Flag = true
↓
[500ms timer]
↓
StateManager::save_if_needed()
↓
JsonFileStore::save()
↓
Atomic Write
↓
Disk (persisted)
{
"version": 2,
"metadata": {
"instance_id": "uuid-here",
"saved_at": "2024-01-15T10:30:00Z"
},
"data": {
"boards": [],
"columns": [],
"cards": [],
"sprints": [],
"archived_cards": []
}
}
Legacy format without version field or metadata:
{
"boards": [],
"columns": [],
"cards": [],
"sprints": []
}
Migration automatically adds metadata and wraps data.
Migrator::detect_version() checks for version field.v1.backupuse kanban_persistence::migration::{Migrator, FormatVersion};
// Detect current version
let version = Migrator::detect_version("board.json").await?;
// Migrate if needed
if version == FormatVersion::V1 {
Migrator::migrate(FormatVersion::V1, FormatVersion::V2, "board.json").await?;
}
use kanban_tui::state::StateManager;
use tokio::time::{interval, Duration};
let mut manager = StateManager::new(Some("board.json".into()));
// Periodic save task (runs in background)
tokio::spawn(async move {
let mut save_interval = interval(Duration::from_millis(100));
loop {
save_interval.tick().await;
// Respects 500ms debounce internally
if let Err(e) = manager.save_if_needed(&snapshot).await {
tracing::error!("Failed to save: {}", e);
}
}
});
// When file is modified externally (multi-instance editing)
// JsonFileStore detects the change via file watching
// Application can prompt user for reload with conflict resolution
// Last-write-wins strategy automatically applied
All public APIs return KanbanResult<T>:
use kanban_persistence::JsonFileStore;
match store.load().await {
Ok((snapshot, metadata)) => {
// Handle loaded data
}
Err(e) => {
// Could be serialization error, missing file, or version error
eprintln!("Failed to load: {}", e);
}
}
kanban-core - Foundation types and traitskanban-domain - Domain modelsserde, serde_json - Serializationtokio - Async runtimeuuid - ID generationchrono - Timestampsasync-trait - Async trait supportthiserror - Error handlingnotify - File watchingApache 2.0 - See LICENSE.md for details