use std::str::FromStr;
#[derive(Clone, Debug)]
pub struct Threads(ThreadsInner);
#[derive(Clone, Debug)]
enum ThreadsInner {
Count(usize),
Fraction(usize, usize),
}
impl Threads {
pub(crate) fn resolve(&self) -> usize {
let available = std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1);
match self.0 {
ThreadsInner::Count(0) => available,
ThreadsInner::Count(n) => n,
ThreadsInner::Fraction(num, den) => (available * num).div_ceil(den).max(1),
}
}
}
impl From<usize> for Threads {
fn from(n: usize) -> Self {
Self(ThreadsInner::Count(n))
}
}
impl FromStr for Threads {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some((num_str, den_str)) = s.split_once('/') {
let num = num_str
.trim()
.parse::<usize>()
.map_err(|_| format!("invalid numerator in '{s}'"))?;
let den = den_str
.trim()
.parse::<usize>()
.map_err(|_| format!("invalid denominator in '{s}'"))?;
if den == 0 {
return Err("denominator must be non-zero".into());
}
if num == 0 {
return Err("numerator must be non-zero".into());
}
if num > den {
return Err(format!("fraction {num}/{den} exceeds 1"));
}
return Ok(Self(ThreadsInner::Fraction(num, den)));
}
let n = s.trim().parse::<usize>().map_err(|_| {
format!(
"invalid thread spec '{s}': expected a number (e.g. '4') or fraction (e.g. '1/2')"
)
})?;
Ok(Self(ThreadsInner::Count(n)))
}
}