# ankit-engine
High-level workflow operations for Anki via AnkiConnect.
[](https://crates.io/crates/ankit-engine)
[](https://docs.rs/ankit-engine)
## Overview
While [`ankit`](https://crates.io/crates/ankit) provides 1:1 API bindings for AnkiConnect,
`ankit-engine` builds higher-level workflows that combine multiple API calls into
cohesive operations.
## Features
- **Import** - Bulk import with duplicate detection and conflict resolution
- **Export** - Deck and review history export
- **Organize** - Deck cloning, merging, and tag-based reorganization
- **Analyze** - Study statistics, retention rates, and problem card detection
- **Progress** - Deck health reports, performance tagging, bulk operations
- **Migrate** - Note type migration with field mapping
- **Media** - Media file audit and cleanup
- **Enrich** - Find and fill empty fields
- **Deduplicate** - Find and remove duplicate notes
- **Backup** - Deck backup and restore to .apkg files
All features are enabled by default but can be individually disabled.
## Quick Start
```toml
[dependencies]
ankit-engine = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```
```rust
use ankit_engine::Engine;
#[tokio::main]
async fn main() -> ankit_engine::Result<()> {
let engine = Engine::new();
// Get study statistics
let stats = engine.analyze().study_summary("Japanese", 30).await?;
println!("Reviews in last 30 days: {}", stats.total_reviews);
// Find problem cards (leeches)
use ankit_engine::analyze::ProblemCriteria;
let problems = engine.analyze()
.find_problems("deck:Japanese", ProblemCriteria::default())
.await?;
println!("Found {} problem cards", problems.len());
// Direct API access when needed
let version = engine.client().misc().version().await?;
println!("AnkiConnect version: {}", version);
Ok(())
}
```
## Workflow Examples
### Bulk Import with Duplicate Handling
```rust
use ankit_engine::{Engine, NoteBuilder};
use ankit_engine::import::OnDuplicate;
let engine = Engine::new();
let notes = vec![
NoteBuilder::new("Japanese", "Basic")
.field("Front", "hello")
.field("Back", "world")
.build(),
];
// Skip duplicates
let report = engine.import().notes(¬es, OnDuplicate::Skip).await?;
println!("Added: {}, Skipped: {}", report.added, report.skipped);
// Or update existing notes
let report = engine.import().notes(¬es, OnDuplicate::Update).await?;
```
### Clone a Deck
```rust
use ankit_engine::Engine;
let engine = Engine::new();
let report = engine.organize()
.clone_deck("Japanese", "Japanese Backup")
.await?;
println!("Cloned {} notes", report.notes_cloned);
```
### Deck Health Report
```rust
use ankit_engine::Engine;
let engine = Engine::new();
let health = engine.progress().deck_health("Japanese").await?;
println!("Total cards: {}", health.total_cards);
println!("Average ease: {:.1}%", health.avg_ease);
println!("Leeches: {}", health.leeches);
```
### Find and Remove Duplicates
```rust
use ankit_engine::Engine;
use ankit_engine::deduplicate::KeepStrategy;
let engine = Engine::new();
// Preview what would be removed
let duplicates = engine.deduplicate()
.find("deck:Vocabulary", "Front")
.await?;
// Remove duplicates, keeping the note with most content
let result = engine.deduplicate()
.remove("deck:Vocabulary", "Front", KeepStrategy::MostContent)
.await?;
println!("Removed {} duplicate notes", result.removed);
```
### Media Audit
```rust
use ankit_engine::Engine;
let engine = Engine::new();
let audit = engine.media().audit().await?;
println!("Total files: {}", audit.total_files);
println!("Orphaned: {}", audit.orphaned.len());
println!("Missing: {}", audit.missing.len());
// Clean up orphaned files (dry run first)
let preview = engine.media().cleanup_orphaned(true).await?;
println!("Would delete {} files", preview.deleted.len());
```
### Backup and Restore
```rust
use ankit_engine::Engine;
let engine = Engine::new();
// Backup a deck
let result = engine.backup()
.backup_deck("Japanese", "/home/user/backups")
.await?;
println!("Backed up to: {}", result.path.display());
// Restore from backup
let restore = engine.backup()
.restore_deck(&result.path)
.await?;
// List and rotate backups
let backups = engine.backup().list_backups("/home/user/backups").await?;
engine.backup().rotate_backups("/home/user/backups", 5).await?; // Keep last 5
```
## Feature Flags
All workflow modules are enabled by default. To use only specific features:
```toml
[dependencies]
ankit-engine = { version = "0.1", default-features = false, features = ["analyze", "import"] }
```
Available features: `import`, `export`, `organize`, `analyze`, `migrate`, `media`, `progress`, `enrich`, `deduplicate`, `backup`
## Related Crates
- [`ankit`](https://crates.io/crates/ankit) - Core AnkiConnect client
- [`ankit-builder`](https://crates.io/crates/ankit-builder) - TOML-based deck builder
- [`ankit-mcp`](https://crates.io/crates/ankit-mcp) - MCP server for AI assistant integration
## Requirements
- [Anki](https://apps.ankiweb.net/) with [AnkiConnect](https://ankiweb.net/shared/info/2055492159) add-on
- Rust 1.85+ (Edition 2024)
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)