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>;