ankit_engine/lib.rs
1//! High-level workflow operations for Anki via AnkiConnect.
2//!
3//! This crate provides ergonomic, high-level operations built on top of the
4//! [`ankit`] client library. While `ankit` provides 1:1 API bindings, `ankit-engine`
5//! combines multiple API calls into cohesive workflows.
6//!
7//! # Quick Start
8//!
9//! ```no_run
10//! use ankit_engine::Engine;
11//!
12//! # async fn example() -> ankit_engine::Result<()> {
13//! let engine = Engine::new();
14//!
15//! // High-level workflows
16//! let stats = engine.analyze().study_summary("Japanese", 30).await?;
17//! println!("Cards reviewed: {}", stats.total_reviews);
18//!
19//! // Direct client access when needed
20//! let version = engine.client().misc().version().await?;
21//! # Ok(())
22//! # }
23//! ```
24//!
25//! # Feature Flags
26//!
27//! All workflow modules are enabled by default. Disable with:
28//!
29//! ```toml
30//! [dependencies]
31//! ankit-engine = { version = "0.1", default-features = false, features = ["analyze"] }
32//! ```
33//!
34//! Available features:
35//! - `import` - Bulk import with duplicate handling
36//! - `export` - Deck and review history export
37//! - `organize` - Deck cloning, merging, reorganization
38//! - `analyze` - Study statistics and problem card detection
39//! - `migrate` - Note type migration with field mapping
40//! - `media` - Media audit and cleanup
41//! - `progress` - Card state management and performance tagging
42//! - `enrich` - Find and update notes with empty fields
43//! - `deduplicate` - Duplicate detection and removal
44//! - `backup` - Deck backup and restore to .apkg files
45//! - `search` - Content search helpers (always enabled)
46
47mod error;
48pub mod search;
49
50#[cfg(feature = "analyze")]
51pub mod analyze;
52
53#[cfg(feature = "export")]
54pub mod export;
55
56#[cfg(feature = "import")]
57pub mod import;
58
59#[cfg(feature = "media")]
60pub mod media;
61
62#[cfg(feature = "migrate")]
63pub mod migrate;
64
65#[cfg(feature = "organize")]
66pub mod organize;
67
68#[cfg(feature = "progress")]
69pub mod progress;
70
71#[cfg(feature = "enrich")]
72pub mod enrich;
73
74#[cfg(feature = "deduplicate")]
75pub mod deduplicate;
76
77#[cfg(feature = "backup")]
78pub mod backup;
79
80pub use error::{Error, Result};
81
82// Re-export ankit types for convenience
83pub use ankit::{
84 AnkiClient, CanAddResult, CardAnswer, CardInfo, CardModTime, CardTemplate, ClientBuilder,
85 CreateModelParams, DeckConfig, DeckStats, DuplicateScope, Ease, FieldFont, FindReplaceParams,
86 LapseConfig, MediaAttachment, ModelField, ModelStyling, NewCardConfig, Note, NoteBuilder,
87 NoteField, NoteInfo, NoteModTime, NoteOptions, ReviewConfig, StoreMediaParams,
88};
89
90#[cfg(feature = "analyze")]
91use analyze::AnalyzeEngine;
92
93#[cfg(feature = "export")]
94use export::ExportEngine;
95
96#[cfg(feature = "import")]
97use import::ImportEngine;
98
99#[cfg(feature = "media")]
100use media::MediaEngine;
101
102#[cfg(feature = "migrate")]
103use migrate::MigrateEngine;
104
105#[cfg(feature = "organize")]
106use organize::OrganizeEngine;
107
108#[cfg(feature = "progress")]
109use progress::ProgressEngine;
110
111#[cfg(feature = "enrich")]
112use enrich::EnrichEngine;
113
114#[cfg(feature = "deduplicate")]
115use deduplicate::DeduplicateEngine;
116
117#[cfg(feature = "backup")]
118use backup::BackupEngine;
119
120use search::SearchEngine;
121
122/// High-level workflow engine for Anki operations.
123///
124/// The engine wraps an [`AnkiClient`] and provides access to workflow modules
125/// that combine multiple API calls into cohesive operations.
126///
127/// # Example
128///
129/// ```no_run
130/// use ankit_engine::Engine;
131///
132/// # async fn example() -> ankit_engine::Result<()> {
133/// // Create with default client settings
134/// let engine = Engine::new();
135///
136/// // Or with a custom client
137/// let client = ankit_engine::AnkiClient::builder()
138/// .url("http://localhost:8765")
139/// .build();
140/// let engine = Engine::from_client(client);
141///
142/// // Access workflow modules
143/// let stats = engine.analyze().study_summary("Default", 7).await?;
144/// # Ok(())
145/// # }
146/// ```
147#[derive(Debug, Clone)]
148pub struct Engine {
149 client: AnkiClient,
150}
151
152impl Engine {
153 /// Create a new engine with default client settings.
154 ///
155 /// Connects to AnkiConnect at `http://127.0.0.1:8765`.
156 pub fn new() -> Self {
157 Self {
158 client: AnkiClient::new(),
159 }
160 }
161
162 /// Create an engine from an existing client.
163 pub fn from_client(client: AnkiClient) -> Self {
164 Self { client }
165 }
166
167 /// Get a reference to the underlying client.
168 ///
169 /// Use this for direct API access when workflows don't cover your use case.
170 pub fn client(&self) -> &AnkiClient {
171 &self.client
172 }
173
174 /// Access import workflows.
175 ///
176 /// Provides bulk import with duplicate detection and conflict resolution.
177 #[cfg(feature = "import")]
178 pub fn import(&self) -> ImportEngine<'_> {
179 ImportEngine::new(&self.client)
180 }
181
182 /// Access export workflows.
183 ///
184 /// Provides deck export and review history extraction.
185 #[cfg(feature = "export")]
186 pub fn export(&self) -> ExportEngine<'_> {
187 ExportEngine::new(&self.client)
188 }
189
190 /// Access organization workflows.
191 ///
192 /// Provides deck cloning, merging, and tag-based reorganization.
193 #[cfg(feature = "organize")]
194 pub fn organize(&self) -> OrganizeEngine<'_> {
195 OrganizeEngine::new(&self.client)
196 }
197
198 /// Access analysis workflows.
199 ///
200 /// Provides study statistics and problem card (leech) detection.
201 #[cfg(feature = "analyze")]
202 pub fn analyze(&self) -> AnalyzeEngine<'_> {
203 AnalyzeEngine::new(&self.client)
204 }
205
206 /// Access migration workflows.
207 ///
208 /// Provides note type migration with field mapping.
209 #[cfg(feature = "migrate")]
210 pub fn migrate(&self) -> MigrateEngine<'_> {
211 MigrateEngine::new(&self.client)
212 }
213
214 /// Access media workflows.
215 ///
216 /// Provides media audit and cleanup operations.
217 #[cfg(feature = "media")]
218 pub fn media(&self) -> MediaEngine<'_> {
219 MediaEngine::new(&self.client)
220 }
221
222 /// Access progress management workflows.
223 ///
224 /// Provides card state management, performance tagging, and bulk operations.
225 #[cfg(feature = "progress")]
226 pub fn progress(&self) -> ProgressEngine<'_> {
227 ProgressEngine::new(&self.client)
228 }
229
230 /// Access enrichment workflows.
231 ///
232 /// Provides tools for finding notes with empty fields and updating them.
233 #[cfg(feature = "enrich")]
234 pub fn enrich(&self) -> EnrichEngine<'_> {
235 EnrichEngine::new(&self.client)
236 }
237
238 /// Access deduplication workflows.
239 ///
240 /// Provides duplicate detection and removal based on key fields.
241 #[cfg(feature = "deduplicate")]
242 pub fn deduplicate(&self) -> DeduplicateEngine<'_> {
243 DeduplicateEngine::new(&self.client)
244 }
245
246 /// Access backup and restore workflows.
247 ///
248 /// Provides deck backup to .apkg files and restore operations.
249 #[cfg(feature = "backup")]
250 pub fn backup(&self) -> BackupEngine<'_> {
251 BackupEngine::new(&self.client)
252 }
253
254 /// Access content search helpers.
255 ///
256 /// Provides simplified search methods that return full note info
257 /// instead of just IDs, abstracting away Anki query syntax.
258 ///
259 /// # Example
260 ///
261 /// ```no_run
262 /// # use ankit_engine::Engine;
263 /// # async fn example() -> ankit_engine::Result<()> {
264 /// let engine = Engine::new();
265 ///
266 /// // Search for text in any field
267 /// let notes = engine.search().text("conjugation", Some("Japanese")).await?;
268 ///
269 /// // Search in a specific field
270 /// let notes = engine.search().field("Front", "hello", None).await?;
271 /// # Ok(())
272 /// # }
273 /// ```
274 pub fn search(&self) -> SearchEngine<'_> {
275 SearchEngine::new(&self.client)
276 }
277}
278
279impl Default for Engine {
280 fn default() -> Self {
281 Self::new()
282 }
283}