1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Error types for Talon core operations.
use serde::{Deserialize, Serialize};
use thiserror::Error;
/// Result alias for Talon core operations.
pub type TalonResult<T> = Result<T, TalonError>;
/// Error codes used in the MCP and CLI output envelope.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum ErrorCode {
/// Invalid scope name in config.
InvalidScope,
/// Invalid `--where` filter expression.
InvalidWhere,
/// Invalid `--since` timestamp.
InvalidSince,
/// Database busy (sync lock held).
DbBusy,
/// Database corruption detected.
DbCorrupt,
/// No index exists for the configured vault.
NotIndexed,
/// Internal/server error.
Internal,
}
impl std::fmt::Display for ErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidScope => write!(f, "invalid-scope"),
Self::InvalidWhere => write!(f, "invalid-where"),
Self::InvalidSince => write!(f, "invalid-since"),
Self::DbBusy => write!(f, "db-busy"),
Self::DbCorrupt => write!(f, "db-corrupt"),
Self::NotIndexed => write!(f, "not-indexed"),
Self::Internal => write!(f, "internal"),
}
}
}
/// Typed failures produced by Talon core operations.
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum TalonError {
/// A feature surface exists but is intentionally not implemented yet.
#[error("{feature} is not implemented yet")]
NotImplemented {
/// Feature name.
feature: &'static str,
},
/// User input failed boundary validation.
#[error("invalid input for {field}: {message}")]
InvalidInput {
/// Input field name.
field: &'static str,
/// Validation failure detail.
message: String,
},
/// Scope name not found in configuration.
#[error("scope '{name}' is not declared in config")]
InvalidScope {
/// Invalid scope name.
name: String,
},
/// `--where` filter expression is malformed.
#[error("invalid --where filter: {message}")]
InvalidWhere {
/// Filter expression detail.
message: String,
},
/// `--since` timestamp could not be parsed.
#[error("invalid --since timestamp: {message}")]
InvalidSince {
/// Timestamp detail.
message: String,
},
/// Database is locked by another sync operation.
#[error("database busy: another sync is in progress")]
DbBusy,
/// Database file is corrupt or unreadable.
#[error("database corrupt: {message}")]
DbCorrupt {
/// Corruption detail.
message: String,
},
/// No index exists for the configured vault path.
#[error("not indexed: vault '{path}' has no index")]
NotIndexed {
/// Vault path.
path: String,
},
/// Configuration is invalid or incomplete.
#[error("config error: {message}")]
Config {
/// Configuration detail.
message: String,
},
/// Internal/server error.
#[error("internal error: {message}")]
Internal {
/// Error detail.
message: String,
},
/// `SQLite` operation failed.
#[error("sqlite error in {context}: {source}")]
Sqlite {
/// Where the error occurred (e.g. "open database", "run migrations").
context: &'static str,
/// Underlying `rusqlite` error.
#[source]
source: rusqlite::Error,
},
}
impl TalonError {
/// Returns the error code for this error variant.
#[must_use]
pub const fn code(&self) -> ErrorCode {
match self {
Self::InvalidScope { .. } => ErrorCode::InvalidScope,
Self::InvalidWhere { .. } => ErrorCode::InvalidWhere,
Self::InvalidSince { .. } => ErrorCode::InvalidSince,
Self::DbBusy => ErrorCode::DbBusy,
Self::DbCorrupt { .. } => ErrorCode::DbCorrupt,
Self::NotIndexed { .. } => ErrorCode::NotIndexed,
Self::Internal { .. }
| Self::NotImplemented { .. }
| Self::InvalidInput { .. }
| Self::Config { .. }
| Self::Sqlite { .. } => ErrorCode::Internal,
}
}
}