Skip to main content

dear_imgui_rs/context/
error.rs

1use std::error::Error;
2use std::fmt;
3
4use thiserror::Error;
5
6use super::{Context, ContextBindingError, SuspendedContext};
7
8/// Reason an active Context could not be suspended.
9#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
10#[non_exhaustive]
11pub enum ContextSuspensionReason {
12    /// A restorable Context binding scope is active.
13    #[error("a Context binding scope is active")]
14    BindingScopeActive,
15    /// The Context being suspended is not the current native Context.
16    #[error("the Context is not the current Context")]
17    NotCurrent,
18    /// A Dear ImGui frame is still open on the Context.
19    #[error("a Dear ImGui frame is still open")]
20    FrameOpen,
21}
22
23/// Failure to suspend a Context without losing its owner.
24#[derive(Debug, Error)]
25#[error("failed to suspend Context: {reason}")]
26pub struct ContextSuspensionError {
27    owner: Context,
28    reason: ContextSuspensionReason,
29}
30
31impl ContextSuspensionError {
32    pub(super) fn new(owner: Context, reason: ContextSuspensionReason) -> Self {
33        Self { owner, reason }
34    }
35
36    /// Returns the reason suspension was rejected.
37    pub fn reason(&self) -> ContextSuspensionReason {
38        self.reason
39    }
40
41    /// Borrows the still-owned Context.
42    pub fn owner(&self) -> &Context {
43        &self.owner
44    }
45
46    /// Mutably borrows the still-owned Context.
47    pub fn owner_mut(&mut self) -> &mut Context {
48        &mut self.owner
49    }
50
51    /// Recovers the Context so the caller can repair the conflict and retry.
52    pub fn into_owner(self) -> Context {
53        self.owner
54    }
55
56    /// Splits the error into the retained owner and rejection reason.
57    pub fn into_parts(self) -> (Context, ContextSuspensionReason) {
58        (self.owner, self.reason)
59    }
60}
61
62/// Reason a suspended Context could not be activated.
63#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
64#[non_exhaustive]
65pub enum ContextActivationReason {
66    /// Another Context is already current.
67    #[error("another Context is already active")]
68    ContextAlreadyActive,
69    /// A restorable Context binding scope is active.
70    #[error("a Context binding scope is active")]
71    BindingScopeActive,
72}
73
74/// Failure to activate a suspended Context without losing its owner.
75#[derive(Debug, Error)]
76#[error("failed to activate suspended Context: {reason}")]
77pub struct ContextActivationError {
78    owner: SuspendedContext,
79    reason: ContextActivationReason,
80}
81
82impl ContextActivationError {
83    pub(super) fn new(owner: SuspendedContext, reason: ContextActivationReason) -> Self {
84        Self { owner, reason }
85    }
86
87    /// Returns the reason activation was rejected.
88    pub fn reason(&self) -> ContextActivationReason {
89        self.reason
90    }
91
92    /// Borrows the still-owned suspended Context.
93    pub fn owner(&self) -> &SuspendedContext {
94        &self.owner
95    }
96
97    /// Mutably borrows the still-owned suspended Context.
98    pub fn owner_mut(&mut self) -> &mut SuspendedContext {
99        &mut self.owner
100    }
101
102    /// Recovers the suspended Context so activation can be retried.
103    pub fn into_owner(self) -> SuspendedContext {
104        self.owner
105    }
106
107    /// Splits the error into the retained owner and rejection reason.
108    pub fn into_parts(self) -> (SuspendedContext, ContextActivationReason) {
109        (self.owner, self.reason)
110    }
111}
112
113/// Failure to enter or finish a temporary active-Context scope.
114#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
115#[non_exhaustive]
116pub enum ContextScopeError {
117    /// The Context could not be made current before the closure ran.
118    #[error("failed to activate suspended Context: {0}")]
119    Activation(#[source] ContextActivationReason),
120    /// The closure returned success while leaving a Dear ImGui frame open.
121    #[error("active Context closure returned success with an open frame")]
122    FrameLeftOpen,
123    /// The Context became unavailable while entering its binding capability.
124    ///
125    /// Safe ownership normally keeps the Context available. This variant preserves a binding
126    /// failure caused by teardown or raw/unsafe lifecycle interference instead of panicking.
127    #[error("suspended Context became unavailable: {0}")]
128    ContextUnavailable(#[source] ContextBindingError),
129}
130
131impl ContextScopeError {
132    /// Returns the activation rejection reason, if the closure was not entered.
133    pub fn activation_reason(self) -> Option<ContextActivationReason> {
134        match self {
135            Self::Activation(reason) => Some(reason),
136            _ => None,
137        }
138    }
139}
140
141/// Failure while temporarily activating a borrowed [`SuspendedContext`].
142#[derive(Debug)]
143#[non_exhaustive]
144pub enum ScopedActivationError<E> {
145    /// The temporary Context scope could not be entered or completed.
146    Scope(ContextScopeError),
147    /// The caller's closure returned an error.
148    Closure(E),
149}
150
151impl<E> From<ContextScopeError> for ScopedActivationError<E> {
152    fn from(error: ContextScopeError) -> Self {
153        Self::Scope(error)
154    }
155}
156
157impl<E> ScopedActivationError<E> {
158    /// Maps only the caller-provided closure error.
159    pub fn map_closure<F>(self, f: impl FnOnce(E) -> F) -> ScopedActivationError<F> {
160        match self {
161            Self::Scope(error) => ScopedActivationError::Scope(error),
162            Self::Closure(error) => ScopedActivationError::Closure(f(error)),
163        }
164    }
165
166    /// Returns the Context-scope failure, if the caller's closure did not return an error.
167    pub fn scope_error(&self) -> Option<ContextScopeError> {
168        match self {
169            Self::Scope(error) => Some(*error),
170            _ => None,
171        }
172    }
173
174    /// Returns the activation rejection reason, if the closure was not entered.
175    pub fn activation_reason(&self) -> Option<ContextActivationReason> {
176        self.scope_error()
177            .and_then(ContextScopeError::activation_reason)
178    }
179
180    /// Returns the caller-provided closure error, if one was returned.
181    pub fn closure_error(&self) -> Option<&E> {
182        match self {
183            Self::Closure(error) => Some(error),
184            _ => None,
185        }
186    }
187
188    /// Extracts the caller-provided closure error or returns the Context-scope failure.
189    pub fn into_closure_error(self) -> Result<E, ContextScopeError> {
190        match self {
191            Self::Closure(error) => Ok(error),
192            Self::Scope(error) => Err(error),
193        }
194    }
195}
196
197impl<E: fmt::Display> fmt::Display for ScopedActivationError<E> {
198    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199        match self {
200            Self::Scope(error) => error.fmt(f),
201            Self::Closure(error) => write!(f, "active Context closure failed: {error}"),
202        }
203    }
204}
205
206impl<E> Error for ScopedActivationError<E>
207where
208    E: Error + 'static,
209{
210    fn source(&self) -> Option<&(dyn Error + 'static)> {
211        match self {
212            Self::Scope(error) => Some(error),
213            Self::Closure(error) => Some(error),
214        }
215    }
216}