1use std::path::PathBuf;
9
10#[derive(Debug, thiserror::Error)]
13pub enum GettextError {
14 #[error("Invalid PO format: {0}")]
16 InvalidFormat(String),
17
18 #[error("Translation not found for key: {key}, context: {context:?}")]
20 TranslationNotFound {
21 key: String,
22 context: Option<String>,
23 },
24
25 #[error("Path required for dynamic mode")]
28 PathRequired,
29
30 #[error("Invalid path: {0}")]
33 InvalidPath(String),
34
35 #[error("Invalid input: {0}")]
37 InvalidInput(String),
38
39 #[error("File is locked by another process: {path}")]
42 FileLocked { path: PathBuf },
43
44 #[error("IO error: {0}")]
46 Io(#[from] std::io::Error),
47}
48
49pub type ParseError = GettextError;
52
53pub type StoreError = GettextError;
55
56impl From<GettextError> for rmcp::model::ErrorData {
57 fn from(e: GettextError) -> Self {
58 rmcp::model::ErrorData::new(rmcp::model::ErrorCode::INTERNAL_ERROR, e.to_string(), None)
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn translation_not_found_display() {
68 let err = GettextError::TranslationNotFound {
69 key: "Hello".into(),
70 context: Some("menu".into()),
71 };
72 let s = err.to_string();
73 assert!(s.contains("Hello"));
74 assert!(s.contains("menu"));
75 }
76
77 #[test]
78 fn file_locked_display_includes_path() {
79 let err = GettextError::FileLocked {
80 path: PathBuf::from("/tmp/messages.po"),
81 };
82 assert!(err.to_string().contains("/tmp/messages.po"));
83 }
84
85 #[test]
86 fn aliases_are_the_same_type() {
87 fn assert_same<T>(_: &T, _: &T) {}
89 let e1: GettextError = GettextError::PathRequired;
90 let e2: ParseError = GettextError::PathRequired;
91 let e3: StoreError = GettextError::PathRequired;
92 assert_same(&e1, &e2);
93 assert_same(&e1, &e3);
94 }
95}