cutoff_common/
lib.rs

1//! # Cutoff Common
2//!
3//! A collection of common utilities and helpers used across Cutoff projects.
4//!
5//! This crate provides various utility traits, functions, and modules that are
6//! commonly used throughout the Cutoff ecosystem.
7//!
8//! ## Features
9//!
10//! - Common traits like `IntoOk` and `MaybeFrom`
11//! - Thread utilities
12//! - Collections utilities
13//! - I/O utilities
14//! - URN handling
15//! - Optional logging utilities (with the `tracing-subscriber` feature)
16//! - Optional serialization support (with the `serde` feature)
17
18pub mod urn;
19pub mod collections;
20pub mod io;
21
22#[cfg(feature = "tracing-subscriber")]
23pub mod logging;
24
25use std::thread;
26use std::thread::JoinHandle;
27
28/// A trait for converting a value into a `Result::Ok` variant.
29///
30/// This trait provides a convenient way to wrap any value in a `Result::Ok`,
31/// which can be useful in contexts where a `Result` is expected but you have
32/// a value that cannot fail.
33///
34/// # Examples
35///
36/// ```
37/// use cutoff_common::IntoOk;
38///
39/// let value = 42;
40/// let result: Result<i32, &str> = value.into_ok();
41/// assert_eq!(result, Ok(42));
42/// ```
43pub trait IntoOk
44where
45    Self: Sized,
46{
47    /// Converts `self` into a `Result::Ok` variant.
48    ///
49    /// # Type Parameters
50    ///
51    /// * `E` - The error type for the resulting `Result`.
52    ///
53    /// # Returns
54    ///
55    /// A `Result` with `self` wrapped in the `Ok` variant.
56    fn into_ok<E>(self) -> Result<Self, E>;
57}
58
59/// A trait for attempting to convert from one type to another.
60///
61/// Similar to the standard library's `From` trait, but returns an `Option`
62/// to indicate whether the conversion was successful.
63///
64/// # Examples
65///
66/// ```
67/// use cutoff_common::MaybeFrom;
68///
69/// // Define a wrapper type for the example
70/// #[derive(Debug, PartialEq)]
71/// struct MyWrapper(String);
72///
73/// // Implementing MaybeFrom for our wrapper type
74/// impl MaybeFrom<i32> for MyWrapper {
75///     fn maybe_from(value: i32) -> Option<Self> {
76///         if value > 0 {
77///             Some(MyWrapper(value.to_string()))
78///         } else {
79///             None
80///         }
81///     }
82/// }
83///
84/// // Using the implementation
85/// let result = MyWrapper::maybe_from(42);
86/// assert_eq!(result.unwrap().0, "42");
87///
88/// let result = MyWrapper::maybe_from(0);
89/// assert_eq!(result, None);
90/// ```
91pub trait MaybeFrom<T> {
92    /// Attempts to convert from `value` to `Self`.
93    ///
94    /// # Parameters
95    ///
96    /// * `value` - The value to convert from.
97    ///
98    /// # Returns
99    ///
100    /// `Some(Self)` if the conversion was successful, `None` otherwise.
101    fn maybe_from(value: T) -> Option<Self>
102    where
103        Self: Sized;
104}
105
106impl<T> IntoOk for T
107where
108    Self: Sized,
109{
110    fn into_ok<E>(self) -> Result<Self, E> {
111        Ok(self)
112    }
113}
114
115/// Creates a new thread with the specified name and executes the provided function.
116///
117/// This is a convenience wrapper around the standard library's thread creation
118/// functionality. It creates a thread with the given name and executes the
119/// provided function in that thread.
120///
121/// # Parameters
122///
123/// * `name` - The name to assign to the thread.
124/// * `f` - The function to execute in the new thread.
125///
126/// # Returns
127///
128/// A `JoinHandle` that can be used to wait for the thread to complete and
129/// retrieve its result.
130///
131/// # Panics
132///
133/// This function will panic if thread creation fails.
134///
135/// # Examples
136///
137/// ```
138/// use cutoff_common::thread_spawn;
139/// use std::sync::mpsc;
140///
141/// let (tx, rx) = mpsc::channel();
142///
143/// let handle = thread_spawn("example-thread", move || {
144///     tx.send("Hello from thread").unwrap();
145///     42 // Return value
146/// });
147///
148/// assert_eq!(rx.recv().unwrap(), "Hello from thread");
149/// assert_eq!(handle.join().unwrap(), 42);
150/// ```
151pub fn thread_spawn<F, T>(name: &str, f: F) -> JoinHandle<T>
152where
153    F: FnOnce() -> T,
154    F: Send + 'static,
155    T: Send + 'static,
156{
157    thread::Builder::new().name(name.into()).spawn(f).unwrap()
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163    use std::sync::mpsc;
164
165    #[test]
166    fn test_into_ok() {
167        let value = 42;
168        let result: Result<i32, &str> = value.into_ok();
169        assert_eq!(result, Ok(42));
170    }
171
172    #[test]
173    fn test_maybe_from() {
174        // Test a simple implementation of MaybeFrom
175        #[allow(non_local_definitions)]
176        impl MaybeFrom<i32> for Option<String> {
177            fn maybe_from(value: i32) -> Option<Self> {
178                if value > 0 {
179                    Some(Some(value.to_string()))
180                } else {
181                    None
182                }
183            }
184        }
185
186        // Test with a positive value
187        let result = Option::<String>::maybe_from(42);
188        assert_eq!(result, Some(Some("42".to_string())));
189
190        // Test with a non-positive value
191        let result = Option::<String>::maybe_from(0);
192        assert_eq!(result, None);
193    }
194
195    #[test]
196    fn test_thread_spawn() {
197        let (tx, rx) = mpsc::channel();
198
199        // Spawn a thread that sends a value
200        let handle = thread_spawn("test-thread", move || {
201            tx.send(42).unwrap();
202            "thread result"
203        });
204
205        // Verify the thread name
206        assert_eq!(handle.thread().name(), Some("test-thread"));
207
208        // Verify the thread executed the closure
209        assert_eq!(rx.recv().unwrap(), 42);
210
211        // Verify the thread returned the expected value
212        assert_eq!(handle.join().unwrap(), "thread result");
213    }
214}