aster_forge_cloud_files_macos_bridge/
error.rs

1//! File Provider bridge errors and portable native classifications.
2
3use aster_forge_cloud_files_core::{CloudBackendError, CloudBackendErrorKind, SessionState};
4
5/// Result returned by the macOS File Provider bridge.
6pub type Result<T> = std::result::Result<T, MacosBridgeError>;
7
8/// Product-neutral classification mapped by Swift to `NSFileProviderError` or `CocoaError`.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[repr(i32)]
11pub enum MacosErrorCode {
12    /// Operation completed successfully.
13    Success = 0,
14    /// Item or container does not exist.
15    NotFound = 1,
16    /// User authentication is required before the operation can continue.
17    NotAuthenticated = 2,
18    /// The authenticated principal lacks permission for the operation.
19    PermissionDenied = 3,
20    /// Requested item version is stale.
21    VersionOutOfDate = 4,
22    /// Request should be retried after a transient condition.
23    TryAgain = 5,
24    /// Operation is unsupported by the active adapter/backend.
25    NotSupported = 6,
26    /// Native request or FFI input is invalid.
27    InvalidArgument = 7,
28    /// Enumeration state must be rebuilt from a clean anchor/page.
29    SyncAnchorExpired = 8,
30    /// Request was cancelled.
31    Cancelled = 9,
32    /// Extension session is closing or closed.
33    ProviderNotFound = 10,
34    /// Internal, contract, transport, or panic failure.
35    Internal = 11,
36}
37
38/// Errors produced by identifier, enumeration, session, and FFI mechanics.
39#[derive(Debug, thiserror::Error)]
40pub enum MacosBridgeError {
41    /// A persistent item identifier used an unsupported or malformed envelope.
42    #[error("invalid macOS File Provider identifier: {reason}")]
43    InvalidIdentifier {
44        /// Stable validation reason.
45        reason: &'static str,
46    },
47    /// A system container was used where a product item was required.
48    #[error("File Provider system container cannot be decoded as a cloud item")]
49    SystemContainerIsNotItem,
50    /// An item version cannot represent one or both revision values.
51    #[error("invalid macOS File Provider item version: {reason}")]
52    InvalidItemVersion {
53        /// Stable validation reason.
54        reason: &'static str,
55    },
56    /// A backend item violated its stable identity, parent, name, or content contract.
57    #[error("invalid cloud backend response: {reason}")]
58    InvalidBackendResponse {
59        /// Stable validation reason.
60        reason: &'static str,
61    },
62    /// A page request targeted a system container unsupported by this batch.
63    #[error("unsupported File Provider system container for enumeration")]
64    UnsupportedSystemContainer,
65    /// A callback generation did not match the active extension generation.
66    #[error("stale extension generation: expected {expected}, got {actual}")]
67    StaleSessionGeneration {
68        /// Active generation.
69        expected: u64,
70        /// Callback generation.
71        actual: u64,
72    },
73    /// The extension session rejected new work.
74    #[error("extension session is not accepting requests: {state:?}")]
75    SessionNotAccepting {
76        /// Current lifecycle state.
77        state: SessionState,
78    },
79    /// A lifecycle transition occurred out of order.
80    #[error("invalid extension lifecycle transition from {from:?} to {to:?}")]
81    InvalidSessionTransition {
82        /// Current state.
83        from: SessionState,
84        /// Requested state.
85        to: SessionState,
86    },
87    /// Accepted request count exceeded the host address space.
88    #[error("active File Provider request count overflow")]
89    ActiveRequestCountOverflow,
90    /// An FFI pointer/length pair or UTF-8 input was invalid.
91    #[error("invalid macOS bridge FFI input: {reason}")]
92    InvalidFfiInput {
93        /// Stable validation reason.
94        reason: &'static str,
95    },
96    /// A panic was contained at the C ABI boundary.
97    #[error("panic contained at macOS bridge FFI boundary")]
98    FfiPanic,
99    /// The product-owned backend reported a classified failure.
100    #[error(transparent)]
101    Backend(#[from] CloudBackendError),
102}
103
104impl MacosBridgeError {
105    /// Returns the native-facing portable classification.
106    #[must_use]
107    pub const fn error_code(&self) -> MacosErrorCode {
108        match self {
109            Self::InvalidIdentifier { .. }
110            | Self::SystemContainerIsNotItem
111            | Self::InvalidItemVersion { .. }
112            | Self::InvalidFfiInput { .. } => MacosErrorCode::InvalidArgument,
113            Self::UnsupportedSystemContainer => MacosErrorCode::NotSupported,
114            Self::StaleSessionGeneration { .. } => MacosErrorCode::ProviderNotFound,
115            Self::SessionNotAccepting { .. } | Self::InvalidSessionTransition { .. } => {
116                MacosErrorCode::ProviderNotFound
117            }
118            Self::ActiveRequestCountOverflow
119            | Self::InvalidBackendResponse { .. }
120            | Self::FfiPanic => MacosErrorCode::Internal,
121            Self::Backend(error) => backend_error_code(error.kind()),
122        }
123    }
124}
125
126/// Maps product-neutral backend failures to File Provider-facing classifications.
127#[must_use]
128pub const fn backend_error_code(kind: CloudBackendErrorKind) -> MacosErrorCode {
129    match kind {
130        CloudBackendErrorKind::NotFound => MacosErrorCode::NotFound,
131        CloudBackendErrorKind::AuthenticationRequired => MacosErrorCode::NotAuthenticated,
132        CloudBackendErrorKind::PermissionDenied => MacosErrorCode::PermissionDenied,
133        CloudBackendErrorKind::Conflict | CloudBackendErrorKind::PreconditionFailed => {
134            MacosErrorCode::VersionOutOfDate
135        }
136        CloudBackendErrorKind::InvalidRequest => MacosErrorCode::InvalidArgument,
137        CloudBackendErrorKind::RateLimited | CloudBackendErrorKind::TemporarilyUnavailable => {
138            MacosErrorCode::TryAgain
139        }
140        CloudBackendErrorKind::Unsupported => MacosErrorCode::NotSupported,
141        CloudBackendErrorKind::InvalidResponse | CloudBackendErrorKind::Internal => {
142            MacosErrorCode::Internal
143        }
144    }
145}