1use std::path::PathBuf;
2
3#[derive(Debug, thiserror::Error)]
4pub enum XcStringsError {
5 #[error("file not found: {path}")]
6 FileNotFound { path: PathBuf },
7
8 #[error("invalid path {path}: {reason}")]
9 InvalidPath { path: PathBuf, reason: String },
10
11 #[error("not an .xcstrings file: {path}")]
12 NotXcStrings { path: PathBuf },
13
14 #[error("invalid format: {0}")]
15 InvalidFormat(String),
16
17 #[error("JSON parse error: {0}")]
18 JsonParse(String),
19
20 #[error("locale not found: {0}")]
21 LocaleNotFound(String),
22
23 #[error("locale already exists: {0}")]
24 LocaleAlreadyExists(String),
25
26 #[error("no active file — call parse_xcstrings first")]
27 NoActiveFile,
28
29 #[error("validation failed: {0}")]
30 ValidationFailed(String),
31
32 #[error("format specifier mismatch: {0}")]
33 FormatSpecifierMismatch(String),
34
35 #[error("missing plural form: {0}")]
36 MissingPluralForm(String),
37
38 #[error("invalid batch size: {0}")]
39 InvalidBatchSize(String),
40
41 #[error("file too large: {size_mb}MB (max {max_mb}MB)")]
42 FileTooLarge { size_mb: u64, max_mb: u64 },
43
44 #[error("key marked as shouldTranslate=false: {0}")]
45 ShouldNotTranslate(String),
46
47 #[error("file is locked by another process (likely Xcode): {path}")]
48 FileLocked { path: PathBuf },
49
50 #[error(transparent)]
51 Io(#[from] std::io::Error),
52
53 #[error(transparent)]
54 Serde(#[from] serde_json::Error),
55
56 #[error("unexpected error: {0}")]
57 Unexpected(String),
58}
59
60impl From<XcStringsError> for rmcp::model::ErrorData {
61 fn from(e: XcStringsError) -> Self {
62 rmcp::model::ErrorData::new(rmcp::model::ErrorCode::INTERNAL_ERROR, e.to_string(), None)
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn error_converts_to_mcp_error() {
72 let err = XcStringsError::NoActiveFile;
73 let mcp_err: rmcp::model::ErrorData = err.into();
74 assert!(mcp_err.message.contains("no active file"));
75 }
76
77 #[test]
78 fn error_display() {
79 let err = XcStringsError::FileNotFound {
80 path: PathBuf::from("/test.xcstrings"),
81 };
82 assert!(err.to_string().contains("/test.xcstrings"));
83
84 let err = XcStringsError::FileTooLarge {
85 size_mb: 100,
86 max_mb: 50,
87 };
88 assert!(err.to_string().contains("100"));
89 assert!(err.to_string().contains("50"));
90 }
91}