This document explains the two custom error types defined in the project: RepositoryError and AppError.
Defined in src/todo_item.rs:12-18, RepositoryError handles errors that can occur during data persistence operations:
#[derive(Debug, thiserror::Error)]
pub enum RepositoryError {
#[error("IO Error: {0}")]
Io(#[from] std::io::Error),
#[error("JSON Error: {0}")]
Json(#[from] serde_json::Error),
}Purpose: Wraps low-level storage errors that can occur when reading from or writing to the file system.
Variants:
Io(std::io::Error)- Covers file not found, permission denied, disk full, etc. Uses the#[from]attribute for automatic conversion.Json(serde_json::Error)- Covers JSON serialization/deserialization errors (e.g., corrupted data, invalid format).
Used by: The TodoRepository trait and JsonFileRepository implementation.
Defined in src/todo_item.rs:20-26, AppError represents application-level errors:
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error("Repository Error: {0}")]
Repository(#[from] RepositoryError),
#[error("Task not found")]
NotFound,
}Purpose: Represents high-level business logic errors that the application can encounter.
Variants:
Repository(RepositoryError)- Wraps any repository error (IO or JSON). Uses#[from]for automatic conversion fromRepositoryError.NotFound- Indicates a requested task with a specific ID does not exist.
Used by: Public functions like add_task and mark_done to provide meaningful error messages to callers.
The project uses a layered error approach:
- Repository layer handles low-level I/O and serialization issues via
RepositoryError - Application layer handles business logic issues via
AppError - The
#[from]attribute enables automatic error propagation using the?operator
This design allows callers to handle errors at an appropriate level - they can either handle AppError for user-facing messages, or let RepositoryError bubble up for debugging/storage issues.