use std::fmt;
#[derive(Debug)]
pub enum ApiResult<T> {
Ok(T),
Fallback {
data: T,
reason: String,
},
ConfigError {
message: String,
},
}
impl<T> ApiResult<T> {
pub fn is_ok(&self) -> bool {
matches!(self, ApiResult::Ok(_))
}
pub fn is_fallback(&self) -> bool {
matches!(self, ApiResult::Fallback { .. })
}
pub fn is_config_error(&self) -> bool {
matches!(self, ApiResult::ConfigError { .. })
}
pub fn unwrap(self) -> T {
match self {
ApiResult::Ok(data) => data,
ApiResult::Fallback { data, reason } => {
eprintln!("⚠️ Fallback: {}", reason);
data
}
ApiResult::ConfigError { message } => {
panic!("❌ Configuration error: {}", message);
}
}
}
pub fn into_result(self) -> Result<T, String> {
match self {
ApiResult::Ok(data) => Ok(data),
ApiResult::Fallback { data, reason } => {
eprintln!("⚠️ Fallback: {}", reason);
Ok(data)
}
ApiResult::ConfigError { message } => Err(message),
}
}
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> ApiResult<U> {
match self {
ApiResult::Ok(data) => ApiResult::Ok(f(data)),
ApiResult::Fallback { data, reason } => ApiResult::Fallback {
data: f(data),
reason,
},
ApiResult::ConfigError { message } => ApiResult::ConfigError { message },
}
}
}
impl<T: fmt::Debug> fmt::Display for ApiResult<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ApiResult::Ok(data) => write!(f, "Ok({:?})", data),
ApiResult::Fallback { reason, .. } => write!(f, "Fallback({})", reason),
ApiResult::ConfigError { message } => write!(f, "ConfigError({})", message),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_api_result_ok() {
let result: ApiResult<i32> = ApiResult::Ok(42);
assert!(result.is_ok());
assert!(!result.is_fallback());
assert!(!result.is_config_error());
assert_eq!(result.unwrap(), 42);
}
#[test]
fn test_api_result_fallback() {
let result: ApiResult<i32> = ApiResult::Fallback {
data: 42,
reason: "Server error".to_string(),
};
assert!(!result.is_ok());
assert!(result.is_fallback());
assert!(!result.is_config_error());
assert_eq!(result.unwrap(), 42);
}
#[test]
#[should_panic(expected = "Configuration error: Bad token")]
fn test_api_result_config_error() {
let result: ApiResult<i32> = ApiResult::ConfigError {
message: "Bad token".to_string(),
};
assert!(!result.is_ok());
assert!(!result.is_fallback());
assert!(result.is_config_error());
result.unwrap(); }
#[test]
fn test_api_result_map() {
let result: ApiResult<i32> = ApiResult::Ok(21);
let mapped = result.map(|x| x * 2);
assert_eq!(mapped.unwrap(), 42);
}
}