1use thiserror::Error;
9
10#[derive(Error, Debug)]
12pub enum AptuError {
13 #[error("GitHub API error: {message}")]
15 GitHub {
16 message: String,
18 },
19
20 #[error("AI provider error: {message}")]
22 AI {
23 message: String,
25 status: Option<u16>,
27 provider: String,
29 },
30
31 #[error(
33 "Authentication required - run `aptu auth login` first, or set GITHUB_TOKEN environment variable"
34 )]
35 NotAuthenticated,
36
37 #[error("AI provider '{provider}' is not authenticated - set {env_var} environment variable")]
39 AiProviderNotAuthenticated {
40 provider: String,
42 env_var: String,
44 },
45
46 #[error("Rate limit exceeded on {provider}, retry after {retry_after}s")]
48 RateLimited {
49 provider: String,
51 retry_after: u64,
53 },
54
55 #[error("Truncated response from {provider} - response ended prematurely")]
57 TruncatedResponse {
58 provider: String,
60 },
61
62 #[error("Configuration error: {message}")]
64 Config {
65 message: String,
67 },
68
69 #[error("Invalid JSON response from AI")]
71 InvalidAIResponse(#[source] serde_json::Error),
72
73 #[error("Network error: {0}")]
75 Network(#[from] reqwest::Error),
76
77 #[cfg(feature = "keyring")]
79 #[error("Keyring error: {0}")]
80 Keyring(#[from] keyring::Error),
81
82 #[error("Circuit breaker is open - AI provider is temporarily unavailable")]
84 CircuitOpen,
85
86 #[error("#{number} is {actual}, not {expected}")]
88 TypeMismatch {
89 number: u64,
91 expected: ResourceType,
93 actual: ResourceType,
95 },
96
97 #[error("Model registry error: {message}")]
99 ModelRegistry {
100 message: String,
102 },
103
104 #[error("Invalid model ID: {model_id}. Did you mean one of these?\n{suggestions}")]
106 ModelValidation {
107 model_id: String,
109 suggestions: String,
111 },
112
113 #[error("Security scan error: {message}")]
115 SecurityScan {
116 message: String,
118 },
119
120 #[error(
122 "input field `{field}` exceeds limit: {actual_bytes} bytes (limit: {limit_bytes} bytes)"
123 )]
124 InputExceedsLimit {
125 field: String,
127 actual_bytes: usize,
129 limit_bytes: usize,
131 },
132}
133
134#[derive(Debug, Clone, Copy)]
136pub enum ResourceType {
137 Issue,
139 PullRequest,
141}
142
143impl std::fmt::Display for ResourceType {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 match self {
146 ResourceType::Issue => write!(f, "issue"),
147 ResourceType::PullRequest => write!(f, "pull request"),
148 }
149 }
150}
151
152impl From<octocrab::Error> for AptuError {
153 fn from(err: octocrab::Error) -> Self {
154 AptuError::GitHub {
155 message: err.to_string(),
156 }
157 }
158}
159
160impl From<config::ConfigError> for AptuError {
161 fn from(err: config::ConfigError) -> Self {
162 AptuError::Config {
163 message: err.to_string(),
164 }
165 }
166}