Skip to main content

ankit_engine/
error.rs

1//! Error types for ankit-engine.
2//!
3//! Errors from engine workflows fall into two categories:
4//!
5//! 1. **Client errors**: Wrapped from the underlying [`ankit::Error`] type
6//! 2. **Workflow errors**: Specific to engine operations (e.g., deck not found)
7//!
8//! # Example
9//!
10//! ```no_run
11//! use ankit_engine::{Engine, Error};
12//!
13//! # async fn example() {
14//! let engine = Engine::new();
15//!
16//! match engine.analyze().study_summary("NonexistentDeck", 7).await {
17//!     Ok(stats) => println!("Reviews: {}", stats.total_reviews),
18//!     Err(Error::DeckNotFound(name)) => {
19//!         eprintln!("Deck '{}' not found", name);
20//!     }
21//!     Err(Error::Client(ankit::Error::ConnectionRefused)) => {
22//!         eprintln!("Is Anki running?");
23//!     }
24//!     Err(e) => eprintln!("Error: {}", e),
25//! }
26//! # }
27//! ```
28
29use std::fmt;
30
31/// Result type for ankit-engine operations.
32pub type Result<T> = std::result::Result<T, Error>;
33
34/// Errors that can occur during engine operations.
35///
36/// Engine errors wrap lower-level client errors and add workflow-specific
37/// error variants for common failure cases.
38#[derive(Debug)]
39pub enum Error {
40    /// An error from the underlying ankit client.
41    Client(ankit::Error),
42
43    /// A deck was not found.
44    DeckNotFound(String),
45
46    /// A model (note type) was not found.
47    ModelNotFound(String),
48
49    /// A required field is missing from a note.
50    MissingField {
51        /// The model name.
52        model: String,
53        /// The missing field name.
54        field: String,
55    },
56
57    /// No notes matched the query.
58    NoNotesFound(String),
59
60    /// An operation was cancelled.
61    Cancelled,
62
63    /// A validation error occurred.
64    Validation(String),
65
66    /// An I/O error occurred.
67    Io(std::io::Error),
68
69    /// A backup operation failed.
70    Backup(String),
71}
72
73impl std::error::Error for Error {
74    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
75        match self {
76            Error::Client(e) => Some(e),
77            Error::Io(e) => Some(e),
78            _ => None,
79        }
80    }
81}
82
83impl fmt::Display for Error {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        match self {
86            Error::Client(e) => write!(f, "{}", e),
87            Error::DeckNotFound(name) => write!(f, "deck not found: {}", name),
88            Error::ModelNotFound(name) => write!(f, "model not found: {}", name),
89            Error::MissingField { model, field } => {
90                write!(f, "missing field '{}' for model '{}'", field, model)
91            }
92            Error::NoNotesFound(query) => write!(f, "no notes found for query: {}", query),
93            Error::Cancelled => write!(f, "operation cancelled"),
94            Error::Validation(msg) => write!(f, "validation error: {}", msg),
95            Error::Io(e) => write!(f, "I/O error: {}", e),
96            Error::Backup(msg) => write!(f, "backup error: {}", msg),
97        }
98    }
99}
100
101impl From<std::io::Error> for Error {
102    fn from(err: std::io::Error) -> Self {
103        Error::Io(err)
104    }
105}
106
107impl From<ankit::Error> for Error {
108    fn from(err: ankit::Error) -> Self {
109        Error::Client(err)
110    }
111}