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}