use std::path::PathBuf;
#[derive(Debug, thiserror::Error)]
pub enum GettextError {
#[error("Invalid PO format: {0}")]
InvalidFormat(String),
#[error("Translation not found for key: {key}, context: {context:?}")]
TranslationNotFound {
key: String,
context: Option<String>,
},
#[error("Path required for dynamic mode")]
PathRequired,
#[error("Invalid path: {0}")]
InvalidPath(String),
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("File is locked by another process: {path}")]
FileLocked { path: PathBuf },
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}
pub type ParseError = GettextError;
pub type StoreError = GettextError;
impl From<GettextError> for rmcp::model::ErrorData {
fn from(e: GettextError) -> Self {
rmcp::model::ErrorData::new(rmcp::model::ErrorCode::INTERNAL_ERROR, e.to_string(), None)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn translation_not_found_display() {
let err = GettextError::TranslationNotFound {
key: "Hello".into(),
context: Some("menu".into()),
};
let s = err.to_string();
assert!(s.contains("Hello"));
assert!(s.contains("menu"));
}
#[test]
fn file_locked_display_includes_path() {
let err = GettextError::FileLocked {
path: PathBuf::from("/tmp/messages.po"),
};
assert!(err.to_string().contains("/tmp/messages.po"));
}
#[test]
fn aliases_are_the_same_type() {
fn assert_same<T>(_: &T, _: &T) {}
let e1: GettextError = GettextError::PathRequired;
let e2: ParseError = GettextError::PathRequired;
let e3: StoreError = GettextError::PathRequired;
assert_same(&e1, &e2);
assert_same(&e1, &e3);
}
}