1use thiserror::Error;
6
7#[derive(Error, Debug)]
11pub enum CliError {
12 #[error("Database error: {0}")]
14 Database(#[from] alopex_embedded::Error),
15
16 #[error("IO error: {0}")]
18 Io(#[from] std::io::Error),
19
20 #[error("Invalid argument: {0}")]
22 InvalidArgument(String),
23
24 #[error("プロファイル '{0}' が見つかりません。alopex profile list で一覧を確認してください。")]
26 #[allow(dead_code)]
27 ProfileNotFound(String),
28
29 #[error(
31 "`--profile` と `--data-dir` は同時に指定できません。どちらか一方を使用してください。"
32 )]
33 #[allow(dead_code)]
34 ConflictingOptions,
35
36 #[error(
38 "トランザクション ID '{0}' は無効です。alopex kv txn begin で新しいトランザクションを開始してください。"
39 )]
40 #[allow(dead_code)]
41 InvalidTransactionId(String),
42
43 #[error(
45 "トランザクション '{0}' がタイムアウトしました(60秒)。自動的にロールバックされました。"
46 )]
47 #[allow(dead_code)]
48 TransactionTimeout(String),
49
50 #[error("Query timeout: {0}. Suggestion: Use --deadline 120s to increase timeout.")]
52 #[allow(dead_code)]
53 Timeout(String),
54
55 #[error("Query cancelled by user.")]
57 #[allow(dead_code)]
58 Cancelled,
59
60 #[error("SQL クエリを指定してください。引数、-f オプション、または標準入力から入力できます。")]
62 #[allow(dead_code)]
63 NoQueryProvided,
64
65 #[error("未知のインデックスタイプ: '{0}'。許可値: minmax, bloom")]
67 #[allow(dead_code)]
68 UnknownIndexType(String),
69
70 #[error("未知の圧縮形式: '{0}'。許可値: lz4, zstd, none")]
72 #[allow(dead_code)]
73 UnknownCompressionType(String),
74
75 #[error("ファイルフォーマット v{file} は CLI v{cli} でサポートされていません。CLI をアップグレードしてください。")]
77 #[allow(dead_code)]
78 IncompatibleVersion { cli: String, file: String },
79
80 #[error("S3 error: {0}")]
82 #[allow(dead_code)]
83 S3(String),
84
85 #[error("Credentials error: {0}")]
87 Credentials(String),
88
89 #[error("Parse error: {0}")]
91 #[allow(dead_code)]
92 Parse(String),
93
94 #[error("JSON error: {0}")]
96 Json(#[from] serde_json::Error),
97
98 #[error("Server connection error: {0}")]
100 #[allow(dead_code)]
101 ServerConnection(String),
102
103 #[error("Server does not support this command: {0}")]
105 #[allow(dead_code)]
106 ServerUnsupported(String),
107}
108
109pub type Result<T> = std::result::Result<T, CliError>;
111
112pub fn handle_error(error: CliError, verbose: bool) {
117 if verbose {
118 eprintln!("Error: {:?}", error); } else {
120 eprintln!("Error: {}", error); }
122 std::process::exit(1);
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_invalid_argument_error() {
131 let err = CliError::InvalidArgument("test message".to_string());
132 assert_eq!(format!("{}", err), "Invalid argument: test message");
133 }
134
135 #[test]
136 fn test_s3_error() {
137 let err = CliError::S3("bucket not found".to_string());
138 assert_eq!(format!("{}", err), "S3 error: bucket not found");
139 }
140
141 #[test]
142 fn test_credentials_error() {
143 let err = CliError::Credentials("AWS_ACCESS_KEY_ID not set".to_string());
144 assert_eq!(
145 format!("{}", err),
146 "Credentials error: AWS_ACCESS_KEY_ID not set"
147 );
148 }
149
150 #[test]
151 fn test_parse_error() {
152 let err = CliError::Parse("invalid JSON".to_string());
153 assert_eq!(format!("{}", err), "Parse error: invalid JSON");
154 }
155
156 #[test]
157 fn test_io_error_from() {
158 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
159 let err: CliError = io_err.into();
160 assert!(matches!(err, CliError::Io(_)));
161 }
162}