Skip to main content

use_thread_name/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, num::NonZeroUsize, str::FromStr};
5use std::error::Error;
6
7/// A non-empty thread name value.
8#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub struct ThreadName(String);
10
11impl ThreadName {
12    /// Creates a thread name from non-empty text.
13    ///
14    /// # Errors
15    ///
16    /// Returns [`ThreadNameError::Empty`] when the trimmed input is empty.
17    pub fn new(value: impl AsRef<str>) -> Result<Self, ThreadNameError> {
18        let trimmed = value.as_ref().trim();
19
20        if trimmed.is_empty() {
21            return Err(ThreadNameError::Empty);
22        }
23
24        Ok(Self(trimmed.to_string()))
25    }
26
27    /// Returns the thread name text.
28    #[must_use]
29    pub fn as_str(&self) -> &str {
30        &self.0
31    }
32
33    /// Consumes the thread name and returns the owned string.
34    #[must_use]
35    pub fn into_string(self) -> String {
36        self.0
37    }
38}
39
40impl AsRef<str> for ThreadName {
41    fn as_ref(&self) -> &str {
42        self.as_str()
43    }
44}
45
46impl fmt::Display for ThreadName {
47    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
48        formatter.write_str(self.as_str())
49    }
50}
51
52impl FromStr for ThreadName {
53    type Err = ThreadNameError;
54
55    fn from_str(value: &str) -> Result<Self, Self::Err> {
56        Self::new(value)
57    }
58}
59
60/// Errors returned while constructing thread names.
61#[derive(Clone, Copy, Debug, Eq, PartialEq)]
62pub enum ThreadNameError {
63    /// The name was empty after trimming whitespace.
64    Empty,
65}
66
67impl fmt::Display for ThreadNameError {
68    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
69        match self {
70            Self::Empty => formatter.write_str("thread name cannot be empty"),
71        }
72    }
73}
74
75impl Error for ThreadNameError {}
76
77/// A non-zero thread count.
78#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
79pub struct ThreadCount(NonZeroUsize);
80
81impl ThreadCount {
82    /// Creates a thread count from a non-zero value.
83    ///
84    /// # Errors
85    ///
86    /// Returns [`ThreadCountError::Zero`] when `value` is zero.
87    pub fn new(value: usize) -> Result<Self, ThreadCountError> {
88        NonZeroUsize::new(value)
89            .map(Self)
90            .ok_or(ThreadCountError::Zero)
91    }
92
93    /// Returns the count value.
94    #[must_use]
95    pub const fn get(self) -> usize {
96        self.0.get()
97    }
98}
99
100impl From<NonZeroUsize> for ThreadCount {
101    fn from(value: NonZeroUsize) -> Self {
102        Self(value)
103    }
104}
105
106impl TryFrom<usize> for ThreadCount {
107    type Error = ThreadCountError;
108
109    fn try_from(value: usize) -> Result<Self, Self::Error> {
110        Self::new(value)
111    }
112}
113
114impl fmt::Display for ThreadCount {
115    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
116        self.get().fmt(formatter)
117    }
118}
119
120/// Errors returned while constructing thread counts.
121#[derive(Clone, Copy, Debug, Eq, PartialEq)]
122pub enum ThreadCountError {
123    /// A zero count is not accepted.
124    Zero,
125}
126
127impl fmt::Display for ThreadCountError {
128    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
129        match self {
130            Self::Zero => formatter.write_str("thread count must be non-zero"),
131        }
132    }
133}
134
135impl Error for ThreadCountError {}
136
137#[cfg(test)]
138mod tests {
139    use super::{ThreadCount, ThreadCountError, ThreadName, ThreadNameError};
140
141    #[test]
142    fn thread_names_reject_empty_values() {
143        assert_eq!(ThreadName::new("  "), Err(ThreadNameError::Empty));
144    }
145
146    #[test]
147    fn thread_names_store_trimmed_text() {
148        let name = ThreadName::new(" worker ").unwrap();
149
150        assert_eq!(name.as_str(), "worker");
151        assert_eq!(name.to_string(), "worker");
152    }
153
154    #[test]
155    fn thread_counts_reject_zero() {
156        assert_eq!(ThreadCount::new(0), Err(ThreadCountError::Zero));
157    }
158
159    #[test]
160    fn thread_counts_store_non_zero_values() {
161        let count = ThreadCount::try_from(4).unwrap();
162
163        assert_eq!(count.get(), 4);
164        assert_eq!(count.to_string(), "4");
165    }
166}