foundation_models/
error.rs1use core::ffi::c_char;
4use core::fmt;
5
6use crate::ffi;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum FMError {
12 ModelUnavailable {
16 reason: Unavailability,
17 message: String,
18 },
19 GuardrailViolation(String),
22 ContextWindowExceeded(String),
24 UnsupportedLanguage(String),
26 AssetsUnavailable(String),
28 RateLimited(String),
31 DecodingFailure(String),
34 Refusal(String),
37 ConcurrentRequests(String),
39 UnsupportedGuide(String),
41 ToolCallFailed(String),
43 AdapterInvalidAsset(String),
45 AdapterInvalidName(String),
47 AdapterCompatibleNotFound(String),
49 Cancelled,
51 InvalidArgument(String),
53 Unknown { code: i32, message: String },
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60#[non_exhaustive]
61pub enum Unavailability {
62 DeviceNotEligible,
64 AppleIntelligenceNotEnabled,
66 ModelNotReady,
68 OsTooOld,
70 Unknown,
73}
74
75impl FMError {
76 #[must_use]
79 pub const fn code(&self) -> i32 {
80 match self {
81 Self::ModelUnavailable { .. } => ffi::status::MODEL_UNAVAILABLE,
82 Self::GuardrailViolation(_) => ffi::status::GUARDRAIL_VIOLATION,
83 Self::ContextWindowExceeded(_) => ffi::status::CONTEXT_WINDOW_EXCEEDED,
84 Self::UnsupportedLanguage(_) => ffi::status::UNSUPPORTED_LANGUAGE,
85 Self::AssetsUnavailable(_) => ffi::status::ASSETS_UNAVAILABLE,
86 Self::RateLimited(_) => ffi::status::RATE_LIMITED,
87 Self::DecodingFailure(_) => ffi::status::DECODING_FAILURE,
88 Self::Refusal(_) => ffi::status::REFUSAL,
89 Self::ConcurrentRequests(_) => ffi::status::CONCURRENT_REQUESTS,
90 Self::UnsupportedGuide(_) => ffi::status::UNSUPPORTED_GUIDE,
91 Self::ToolCallFailed(_) => ffi::status::TOOL_CALL_FAILED,
92 Self::AdapterInvalidAsset(_) => ffi::status::ADAPTER_INVALID_ASSET,
93 Self::AdapterInvalidName(_) => ffi::status::ADAPTER_INVALID_NAME,
94 Self::AdapterCompatibleNotFound(_) => ffi::status::ADAPTER_COMPATIBLE_NOT_FOUND,
95 Self::Cancelled => ffi::status::CANCELLED,
96 Self::InvalidArgument(_) => ffi::status::INVALID_ARGUMENT,
97 Self::Unknown { code, .. } => *code,
98 }
99 }
100
101 #[must_use]
103 pub fn message(&self) -> &str {
104 match self {
105 Self::ModelUnavailable { message, .. }
106 | Self::GuardrailViolation(message)
107 | Self::ContextWindowExceeded(message)
108 | Self::UnsupportedLanguage(message)
109 | Self::AssetsUnavailable(message)
110 | Self::RateLimited(message)
111 | Self::DecodingFailure(message)
112 | Self::Refusal(message)
113 | Self::ConcurrentRequests(message)
114 | Self::UnsupportedGuide(message)
115 | Self::ToolCallFailed(message)
116 | Self::AdapterInvalidAsset(message)
117 | Self::AdapterInvalidName(message)
118 | Self::AdapterCompatibleNotFound(message)
119 | Self::InvalidArgument(message)
120 | Self::Unknown { message, .. } => message,
121 Self::Cancelled => "generation cancelled",
122 }
123 }
124}
125
126impl fmt::Display for FMError {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 write!(f, "{} (code {})", self.message(), self.code())
129 }
130}
131
132impl std::error::Error for FMError {}
133
134pub(crate) fn from_swift(status: i32, error_str: *mut c_char) -> FMError {
139 let message = if error_str.is_null() {
140 String::new()
141 } else {
142 let s = unsafe { core::ffi::CStr::from_ptr(error_str) }
143 .to_string_lossy()
144 .into_owned();
145 unsafe { ffi::fm_string_free(error_str) };
146 s
147 };
148
149 match status {
150 ffi::status::MODEL_UNAVAILABLE => FMError::ModelUnavailable {
151 reason: Unavailability::Unknown,
152 message,
153 },
154 ffi::status::GUARDRAIL_VIOLATION => FMError::GuardrailViolation(message),
155 ffi::status::CONTEXT_WINDOW_EXCEEDED => FMError::ContextWindowExceeded(message),
156 ffi::status::UNSUPPORTED_LANGUAGE => FMError::UnsupportedLanguage(message),
157 ffi::status::ASSETS_UNAVAILABLE => FMError::AssetsUnavailable(message),
158 ffi::status::RATE_LIMITED => FMError::RateLimited(message),
159 ffi::status::DECODING_FAILURE => FMError::DecodingFailure(message),
160 ffi::status::REFUSAL => FMError::Refusal(message),
161 ffi::status::CONCURRENT_REQUESTS => FMError::ConcurrentRequests(message),
162 ffi::status::UNSUPPORTED_GUIDE => FMError::UnsupportedGuide(message),
163 ffi::status::TOOL_CALL_FAILED => FMError::ToolCallFailed(message),
164 ffi::status::ADAPTER_INVALID_ASSET => FMError::AdapterInvalidAsset(message),
165 ffi::status::ADAPTER_INVALID_NAME => FMError::AdapterInvalidName(message),
166 ffi::status::ADAPTER_COMPATIBLE_NOT_FOUND => FMError::AdapterCompatibleNotFound(message),
167 ffi::status::CANCELLED => FMError::Cancelled,
168 ffi::status::INVALID_ARGUMENT => FMError::InvalidArgument(message),
169 code => FMError::Unknown { code, message },
170 }
171}