asana_cli/error.rs
1//! Typed error surface for the Asana CLI library.
2//!
3//! Per `REPO_INVARIANTS.md` (Error handling) the library exposes a `thiserror`
4//! enum with one variant per distinct failure mode and `#[from]` seams onto the
5//! domain error types that already live deeper in the crate ([`ApiError`],
6//! [`RichTextError`]). The binary edge (`main.rs`, `cli/`, `output/`) erases
7//! this type at the top of the call stack; library code never depends on the
8//! edge error crate.
9
10use std::path::PathBuf;
11
12use crate::api::error::ApiError;
13use crate::rich_text::RichTextError;
14
15/// All errors raised by the Asana CLI library layer.
16#[derive(Debug, thiserror::Error)]
17pub enum Error {
18 /// A filesystem operation failed.
19 #[error("I/O error: {0}")]
20 Io(#[from] std::io::Error),
21
22 /// A configuration or template TOML document failed to parse.
23 #[error("failed to parse TOML: {0}")]
24 TomlDeserialize(#[from] toml::de::Error),
25
26 /// Configuration could not be encoded to TOML for persistence.
27 #[error("failed to encode TOML: {0}")]
28 TomlSerialize(#[from] toml::ser::Error),
29
30 /// A user-supplied regular expression in a filter expression was invalid.
31 #[error("invalid filter regex: {0}")]
32 Regex(#[from] regex::Error),
33
34 /// An Asana API interaction failed.
35 #[error(transparent)]
36 Api(#[from] ApiError),
37
38 /// Rich-text preparation or validation failed.
39 #[error(transparent)]
40 RichText(#[from] RichTextError),
41
42 /// The platform's standard project directories could not be resolved.
43 #[error("{0}")]
44 ProjectDirs(String),
45
46 /// A filter expression was empty.
47 #[error("filter expression cannot be empty")]
48 EmptyFilter,
49
50 /// A filter expression did not match any supported operator syntax.
51 #[error(
52 "unable to parse filter expression '{0}'; \
53 expected syntax field=value | field!=value | field~regex | field:substring"
54 )]
55 UnparseableFilter(String),
56
57 /// A named saved-filter set could not be located on disk.
58 #[error("saved filter '{name}' not found in {path}")]
59 SavedFilterNotFound {
60 /// The requested filter set name.
61 name: String,
62 /// The directory searched.
63 path: PathBuf,
64 },
65
66 /// A named saved-filter set existed but contained no usable expressions.
67 #[error("saved filter '{0}' does not contain any expressions")]
68 EmptySavedFilter(String),
69
70 /// A filter set was asked to be saved without any expressions.
71 #[error("cannot save filter '{0}' without at least one filter expression")]
72 NoFilterExpressions(String),
73
74 /// A sort field name was not one of the supported values.
75 #[error("unsupported sort field '{0}'; supported values: name, created_at, modified_at")]
76 UnsupportedSortField(String),
77
78 /// A named project template could not be located.
79 #[error("template '{name}' not found; place templates in {search_dir}")]
80 TemplateNotFound {
81 /// The requested template identifier.
82 name: String,
83 /// The templates directory searched.
84 search_dir: PathBuf,
85 },
86
87 /// The global tracing subscriber could not be installed.
88 #[error("failed to initialise tracing: {0}")]
89 TracingInit(String),
90}
91
92/// Result type alias for the Asana CLI library, carrying the typed [`Error`].
93///
94/// The binary edge does not use this alias; it imports its own edge-level
95/// `Result` directly and collapses [`Error`] via `?` (the edge error crate
96/// converts any `std::error::Error`).
97pub type Result<T> = std::result::Result<T, Error>;