use core::ffi::c_char;
use core::fmt;
use crate::ffi;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum FMError {
ModelUnavailable {
reason: Unavailability,
message: String,
},
GuardrailViolation(String),
ContextWindowExceeded(String),
UnsupportedLanguage(String),
AssetsUnavailable(String),
RateLimited(String),
DecodingFailure(String),
Refusal(String),
ConcurrentRequests(String),
UnsupportedGuide(String),
ToolCallFailed(String),
AdapterInvalidAsset(String),
AdapterInvalidName(String),
AdapterCompatibleNotFound(String),
Cancelled,
InvalidArgument(String),
Unknown { code: i32, message: String },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Unavailability {
DeviceNotEligible,
AppleIntelligenceNotEnabled,
ModelNotReady,
OsTooOld,
Unknown,
}
impl FMError {
#[must_use]
pub const fn code(&self) -> i32 {
match self {
Self::ModelUnavailable { .. } => ffi::status::MODEL_UNAVAILABLE,
Self::GuardrailViolation(_) => ffi::status::GUARDRAIL_VIOLATION,
Self::ContextWindowExceeded(_) => ffi::status::CONTEXT_WINDOW_EXCEEDED,
Self::UnsupportedLanguage(_) => ffi::status::UNSUPPORTED_LANGUAGE,
Self::AssetsUnavailable(_) => ffi::status::ASSETS_UNAVAILABLE,
Self::RateLimited(_) => ffi::status::RATE_LIMITED,
Self::DecodingFailure(_) => ffi::status::DECODING_FAILURE,
Self::Refusal(_) => ffi::status::REFUSAL,
Self::ConcurrentRequests(_) => ffi::status::CONCURRENT_REQUESTS,
Self::UnsupportedGuide(_) => ffi::status::UNSUPPORTED_GUIDE,
Self::ToolCallFailed(_) => ffi::status::TOOL_CALL_FAILED,
Self::AdapterInvalidAsset(_) => ffi::status::ADAPTER_INVALID_ASSET,
Self::AdapterInvalidName(_) => ffi::status::ADAPTER_INVALID_NAME,
Self::AdapterCompatibleNotFound(_) => ffi::status::ADAPTER_COMPATIBLE_NOT_FOUND,
Self::Cancelled => ffi::status::CANCELLED,
Self::InvalidArgument(_) => ffi::status::INVALID_ARGUMENT,
Self::Unknown { code, .. } => *code,
}
}
#[must_use]
pub fn message(&self) -> &str {
match self {
Self::ModelUnavailable { message, .. }
| Self::GuardrailViolation(message)
| Self::ContextWindowExceeded(message)
| Self::UnsupportedLanguage(message)
| Self::AssetsUnavailable(message)
| Self::RateLimited(message)
| Self::DecodingFailure(message)
| Self::Refusal(message)
| Self::ConcurrentRequests(message)
| Self::UnsupportedGuide(message)
| Self::ToolCallFailed(message)
| Self::AdapterInvalidAsset(message)
| Self::AdapterInvalidName(message)
| Self::AdapterCompatibleNotFound(message)
| Self::InvalidArgument(message)
| Self::Unknown { message, .. } => message,
Self::Cancelled => "generation cancelled",
}
}
}
impl fmt::Display for FMError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} (code {})", self.message(), self.code())
}
}
impl std::error::Error for FMError {}
pub(crate) fn from_swift(status: i32, error_str: *mut c_char) -> FMError {
let message = if error_str.is_null() {
String::new()
} else {
let s = unsafe { core::ffi::CStr::from_ptr(error_str) }
.to_string_lossy()
.into_owned();
unsafe { ffi::fm_string_free(error_str) };
s
};
match status {
ffi::status::MODEL_UNAVAILABLE => FMError::ModelUnavailable {
reason: Unavailability::Unknown,
message,
},
ffi::status::GUARDRAIL_VIOLATION => FMError::GuardrailViolation(message),
ffi::status::CONTEXT_WINDOW_EXCEEDED => FMError::ContextWindowExceeded(message),
ffi::status::UNSUPPORTED_LANGUAGE => FMError::UnsupportedLanguage(message),
ffi::status::ASSETS_UNAVAILABLE => FMError::AssetsUnavailable(message),
ffi::status::RATE_LIMITED => FMError::RateLimited(message),
ffi::status::DECODING_FAILURE => FMError::DecodingFailure(message),
ffi::status::REFUSAL => FMError::Refusal(message),
ffi::status::CONCURRENT_REQUESTS => FMError::ConcurrentRequests(message),
ffi::status::UNSUPPORTED_GUIDE => FMError::UnsupportedGuide(message),
ffi::status::TOOL_CALL_FAILED => FMError::ToolCallFailed(message),
ffi::status::ADAPTER_INVALID_ASSET => FMError::AdapterInvalidAsset(message),
ffi::status::ADAPTER_INVALID_NAME => FMError::AdapterInvalidName(message),
ffi::status::ADAPTER_COMPATIBLE_NOT_FOUND => FMError::AdapterCompatibleNotFound(message),
ffi::status::CANCELLED => FMError::Cancelled,
ffi::status::INVALID_ARGUMENT => FMError::InvalidArgument(message),
code => FMError::Unknown { code, message },
}
}