feather_tui/
error.rs

1use thiserror::Error;
2use std::io;
3
4/// An `enum` representing all possible errors that can occur in `Feather-TUI`.
5///
6/// # Derives
7/// `thiserror::Error`, `Debug`
8///
9/// # PartialEq Implementation
10/// This is necessary because the `StdInputOutputError` variant wraps a value of
11/// type `std::io::Error`, which does not implement `PartialEq`. As a result, we
12/// implement `PartialEq` manually, comparing all variants normally while treating
13/// any two `StdInputOutputError` values as equal regardless of their internal
14/// `io::Error`.
15#[repr(u8)]
16#[derive(Error, Debug)]
17pub enum FtuiError {
18    /// Occurs when `TextFlags::NONE` is used together with other flags.
19    ///
20    /// # Example
21    /// ```rust
22    /// fn main() -> FtuiResult<()> {
23    ///     // Using `TextFlags::NONE` with TextFlags::COLOR_RED results in an error.
24    ///     Text::new("Label", TextFlags::NONE | TextFlags::COLOR_RED)?;
25    /// 
26    ///     Ok(())
27    /// }
28    /// ```
29    #[error("TextFlags::NONE cannot be combined with other TextFlags.")]
30    TextFlagNoneWithOther,
31
32    /// Occurs when multiple color flags are set for a `Text` component.
33    ///
34    /// # Example
35    /// ```rust
36    /// fn main() -> FtuiResult<()> {
37    ///     // Setting both `COLOR_BLUE` and `COLOR_RED` results in an error.
38    ///     Text::new("Label", TextFlags::COLOR_BLUE | TextFlags::COLOR_RED)?;
39    /// 
40    ///     Ok(())
41    /// }
42    /// ```
43    #[error("TextFlags cannot contain multiple color.")]
44    TextFlagMultipleColor,
45
46    /// Occurs when attempting to use the `TextFlags::ALIGN_BOTTOM` flag with
47    /// a `List` element, which does not support bottom alignment.
48    ///
49    /// # Example
50    /// ```rust
51    /// fn main() -> FtuiResult<()> {
52    ///     let mut list = List::new(...)?;
53    ///
54    ///     // Using `TextFlags::ALIGN_BOTTOM` will result in an error.
55    ///     list.add(..., TextFlags::ALIGN_BOTTOM)?;
56    ///
57    ///     Ok(())
58    /// }
59    /// ```
60    #[error("TextFlags::ALIGN_BOTTOM is not supported for list elements.")]
61    TextFlagAlignBottomWithListElement,
62
63    /// Occurs when attempting to create a `Header` component with an empty label.
64    ///
65    /// # Example
66    /// ```rust
67    /// fn main() -> FtuiResult<()> {
68    ///     // Creating a header with an empty label results in an error.
69    ///     Header::new("")?;
70    ///     
71    ///     Ok(())
72    /// }
73    /// ```
74    #[error("A Header label cannot be empty.")]
75    HeaderLabelEmpty,
76
77    /// Occurs when attempting to create an `Option` component with an empty label.
78    ///
79    /// # Example
80    /// ```rust
81    /// fn main() -> FtuiResult<()> {
82    ///     // Creating an option with an empty label results in an error.
83    ///     // Assuming the callback is created elsewhere.
84    ///     Option::new("", ...)?;
85    ///     
86    ///     Ok(())
87    /// }
88    /// ```
89    #[error("An Option label cannot be empty.")]
90    OptionLabelEmpty,
91
92    /// Occurs when calling `Container::loop` on a container that has `Option`
93    /// components but does not have a `Selector`.
94    ///
95    /// # Example
96    /// ```rust
97    /// fn main() -> FtuiResult<()> {
98    ///     // Create a container with an option component. 
99    ///     let mut container = ContainerBuilder::new() 
100    ///         .option(...)?;
101    ///     
102    ///     // Attempting to call the loop method without a selector
103    ///     container.looper()?;
104    ///
105    ///     Ok(())
106    /// }
107    /// ```
108    #[error("The container's looper method requires a Selector.")]
109    ContainerLooperNoSelector,
110
111    /// Occurs when attempting to use `Container` functionality that
112    /// requires a `Selector`, but the `Container` does not have one.
113    ///
114    /// # Example
115    /// ```rust
116    /// fn main() -> FtuiResult<()> {
117    ///     // Create a container without a selector. 
118    ///     let mut container = ContainerBuilder::new().build();
119    ///     
120    ///     // Attempting to call `selector_mut` on a container without a selector
121    ///     // results in the error.
122    ///     container.selector_mut()?;
123    ///
124    ///     Ok(())
125    /// }
126    /// ```
127    #[error("Container doesnot have a Selector.")]
128    ContainerNoSelector,
129
130    /// Occurs when attempting to query a component by its ID, but no such
131    /// component exists in the container.
132    ///
133    /// # Example
134    /// ```rust
135    /// fn main() -> FtuiResult<()> {
136    ///     // Create an empty container.
137    ///     let mut container = ContainerBuilder::new().build();
138    ///
139    ///     // Attempt to query using a non-existent ID.
140    ///     // This results in the error.
141    ///     container.option(172)?;
142    ///
143    ///     Ok(())
144    /// }
145    /// ```
146    #[error("Failed to query for component by its ID")]
147    ContainerNoComponentById,
148
149    /// Cccurs when performing an operation on a `List` container using an element
150    /// index that does not exist.
151    ///
152    /// # Example
153    /// ```rust
154    /// // Create a simple `List` container.
155    /// let mut list = ListBuilder::new().build();
156    ///
157    /// // Add elements to the list.
158    /// list.add(...)?;
159    /// list.add(...)?;
160    ///
161    /// // Attempt to access an out-of-bounds index, which will trigger this error.
162    /// list.at(100)?; 
163    /// ```
164    #[error("List index is out of bound.")]
165    ListIndexOutOfBound,
166
167    /// Occurs when attempting to find the index of an element in a `List`
168    /// by its ID, but no element with the specified ID exists.
169    ///
170    /// # Example
171    /// ```rust
172    /// // Create a simple `List` container.
173    /// let mut list = ListBuilder::new().build();
174    ///
175    /// // Add an element to the list and store its ID.
176    /// let id = list.add(...)?;
177    ///
178    /// // Attempt to find an element by a non-existent ID.
179    /// list.find(id + 1)?; 
180    /// ```
181    #[error("No element found with the specified ID.")]
182    ListNoElementById,
183
184    /// Occurs when attempting to use `Selector` functionality that
185    /// requires triggers, but the `Selector` does not have one.
186    ///
187    /// # Example
188    /// ```rust
189    /// fn main() -> FtuiResult<()> {
190    ///     // Create a `Selector` component with no triggers.
191    ///     let mut selector = Selector::no_triggers();
192    ///
193    ///     // Attempting to use functionality that requires triggers.
194    ///     // This results in the error.
195    ///     selector.up_trig_mut()?;
196    ///
197    ///     Ok(())
198    /// }
199    /// ```
200    #[error("Selector does not have triggers.")]
201    SelectorNoTriggers,
202
203    /// Occurs when attempting to call the `Renderer::render` method with a container
204    /// that exceeds the dimensions of the renderer. There are two cases where a
205    /// container is considered "too big":
206    /// 
207    /// 1. A component's label is longer than the renderer's width.
208    /// 2. The total number of components exceeds the renderer's height.
209    /// 
210    /// # Example
211    /// ```rust
212    /// fn main() -> FtuiResult<()> {
213    ///     let mut container = ContainerBuilder::new()
214    ///         .header("Header!")?
215    ///         .text("Label", None)?;
216    ///
217    ///     // This will cause an error because the label "Header!" is 7 characters
218    ///     // long, which is wider than the renderer width of 5.
219    ///     let mut renderer = Renderer::new(5, 10);
220    ///     renderer.render(&mut container)?;
221    ///
222    ///     // This will cause an error because the container has 2 components,
223    ///     // but the renderer can only display 1 line (height = 1).
224    ///     let mut renderer = Renderer::new(10, 1);
225    ///     renderer.render(&mut container)?;
226    ///
227    ///     Ok(())
228    /// }
229    /// ```
230    #[error("Container is bigger than what the renderer can accommodate.")]
231    RendererContainerTooBig,
232
233    /// Occurs when functions in the `input` module fail. Affected functions 
234    /// include `line`, `key`, and `key_char`. This enum wraps an error
235    /// from `std::io::Error`.
236    ///
237    /// # Example
238    /// ```rust
239    /// fn main() -> tui::err::FtuiResult<()> {
240    ///     // This function may return an error if an I/O operation fails.
241    ///     line("Prompt")?;
242    /// 
243    ///     // This function may return an error if an I/O operation fails.
244    ///     key()?;
245    /// 
246    ///     // This function may return an error if an I/O operation fails.
247    ///     key_char()?;
248    /// 
249    ///     Ok(())
250    /// }
251    /// ```
252    #[error("Std Input Output Error: {0}")]
253    StdInputOutputError(#[from] io::Error),
254
255    /// Occurs when calling the `trigger::cast_arg` function with an argument 
256    /// that is a `None`.
257    ///
258    /// # Notes
259    /// - The trigger function argument has the type `&Option<Box<dyn Any>>`.
260    ///
261    /// # Example
262    /// ```rust
263    /// // When creating a trigger using the no_arg constructor, the argument
264    /// // will be set to None.
265    /// Trigger::no_arg(trigger_func);
266    ///
267    /// trg_new_trigger_func!(trigger_func, arg, {
268    ///     // An error occurs because arg is None.
269    ///     trg::cast_arg::<T>(arg)?;
270    /// });
271    ///
272    /// ```
273    #[error("Trigger function does not have an argument available for casting.")]
274    TriggerCastArgNoArgument,
275
276    /// Occurs when calling the `trigger::cast_arg` function with an argument of
277    /// the wrong type.
278    ///
279    /// # Notes
280    /// - The trigger function argument has the type `&Option<Box<dyn Any>>`.
281    ///
282    /// # Example
283    /// ```rust
284    /// // Creating a trigger with an argument of 5, which is a u32.
285    /// Trigger::new(trigger_func, 5u32);
286    /// 
287    /// trg_new_trigger_func!(trigger_func, arg, {
288    ///     // An error occurs because arg is a u32, but we're attempting to cast 
289    ///     // it to a char.
290    ///     trg::cast_arg::<char>(arg)?;
291    /// });
292    /// ```
293    #[error("Trigger function argument type mismatch unable to cast to the expected type.")]
294    TriggerCastArgWrongType,
295
296    /// Occurs when calling the `callback::cast_arg` function with an argument 
297    /// that is a `None`.
298    ///
299    /// # Notes
300    /// - The callback function argument has the type `&Option<Box<dyn Any>>`.
301    ///
302    /// # Example
303    /// ```rust
304    /// // When creating a callback using the no_arg constructor, the argument
305    /// // will be set to None.
306    /// cbk::Callback::no_arg(callback_func);
307    ///
308    /// cbk_new_callback_func!(callback_func, arg, {
309    ///     // An error occurs because arg is None.
310    ///     cbk::cast_arg::<T>(arg)?;
311    /// });
312    ///
313    /// ```
314    #[error("Callback function does not have an argument available for casting.")]
315    CallbackCastArgNoArgument,
316
317    /// Occurs when calling the `callback::cast_arg` function with an argument of
318    /// the wrong type.
319    ///
320    /// # Notes
321    /// - The callback function argument has the type `&Option<Box<dyn Any>>`.
322    ///
323    /// # Example
324    /// ```rust
325    /// // Creating a callback with an argument of 5, which is a u32.
326    /// Callback::new(callback_func, 5u32);
327    /// 
328    /// callback_new_callback_func!(callback_func, arg, {
329    ///     // An error occurs because arg is a u32, but we're attempting to cast 
330    ///     // it to a char.
331    ///     cbk::cast_arg::<char>(arg)?;
332    /// });
333    /// ```
334    #[error("Callback function argument type mismatch unable to cast to the expected type.")]
335    CallbackCastArgWrongType,
336}
337
338/// Implementation of the `PartialEq` trait for the `FtuiError` enum. This is necessary
339/// because the `StdInputOutputError` variant wraps a value of type `std::io::Error`,
340/// which does not implement `PartialEq`. As a result, we implement `PartialEq`
341/// manually, comparing all variants normally while treating any two `StdInputOutputError`
342/// values as equal regardless of their internal `io::Error`.
343///
344/// # Examples
345///
346/// ```rust
347/// // Variants of the same type are considered equal.
348/// assert_eq!(
349///     FtuiError::TextFlagNoneWithOther, FtuiError::TextFlagNoneWithOther);
350///
351/// // Variants of different types are not equal.
352/// assert_ne!(
353///     FtuiError::TextFlagNoneWithOther, FtuiError::TextFlagMultipleColor);
354///
355/// // StdInputOutputError variants are treated as equal even if their inner errors differ.
356/// use std::io::{Error, ErrorKind};
357/// 
358/// assert_eq!(
359///     FtuiError::StdInputOutputError(Error::from(ErrorKind::NotFound)),
360///     FtuiError::StdInputOutputError(Error::from(ErrorKind::PermissionDenied)));
361/// ```
362impl PartialEq for FtuiError {
363    fn eq(&self, other: &Self) -> bool {
364        use FtuiError::*;
365
366        match (self, other) {
367            (TextFlagNoneWithOther, TextFlagNoneWithOther) => true,
368            (TextFlagMultipleColor, TextFlagMultipleColor) => true,
369            (HeaderLabelEmpty, HeaderLabelEmpty) => true,
370            (OptionLabelEmpty, OptionLabelEmpty) => true,
371            (ContainerLooperNoSelector, ContainerLooperNoSelector) => true,
372            (ContainerNoSelector, ContainerNoSelector) => true,
373            (ContainerNoComponentById, ContainerNoComponentById) => true,
374            (ListIndexOutOfBound, ListIndexOutOfBound) => true,
375            (ListNoElementById, ListNoElementById) => true,
376            (SelectorNoTriggers, SelectorNoTriggers) => true,
377            (RendererContainerTooBig, RendererContainerTooBig) => true,
378            (StdInputOutputError(_), StdInputOutputError(_)) => true,
379            (TriggerCastArgNoArgument, TriggerCastArgNoArgument) => true,
380            (TriggerCastArgWrongType, TriggerCastArgWrongType) => true,
381            (CallbackCastArgNoArgument, CallbackCastArgNoArgument) => true,
382            (CallbackCastArgWrongType, CallbackCastArgWrongType) => true,
383
384            _ => false,
385        }
386    }
387}
388
389/// A convenient alias for `Result<T, FtuiError>`.
390///
391/// # Example
392/// ```rust
393/// // A main function that returns a Result<(), FtuiError>.
394/// fn main() -> FtuiResult<()> {
395///     Ok(())
396/// }
397// ```
398pub type FtuiResult<T> = Result<T, FtuiError>;