use std::{fmt, io};
const MAX_JOBS: usize = 64;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NinjaJobCount(usize);
impl NinjaJobCount {
pub fn try_new(value: usize) -> io::Result<Self> {
if (1..=MAX_JOBS).contains(&value) {
Ok(Self(value))
} else {
Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Ninja job count must be between 1 and {MAX_JOBS}, got {value}"),
))
}
}
}
impl fmt::Display for NinjaJobCount {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn accepts_the_supported_range_boundaries() {
for value in [1, MAX_JOBS] {
let count = NinjaJobCount::try_new(value)
.unwrap_or_else(|_| panic!("{value} should be a supported job count"));
assert_eq!(
count.to_string(),
value.to_string(),
"the accepted count should keep its value"
);
}
}
#[test]
fn rejects_counts_outside_the_supported_range() {
for value in [0, MAX_JOBS + 1] {
let Err(error) = NinjaJobCount::try_new(value) else {
panic!("{value} should be rejected as a job count");
};
assert_eq!(
error.kind(),
io::ErrorKind::InvalidInput,
"an out-of-range count should be an invalid-input error"
);
}
}
proptest! {
#[test]
fn validates_every_job_count(value in any::<usize>()) {
let result = NinjaJobCount::try_new(value);
if (1..=MAX_JOBS).contains(&value) {
let count = result.expect("supported job counts should be accepted");
prop_assert_eq!(count.to_string(), value.to_string());
} else {
let error = result.expect_err("out-of-range job counts should be rejected");
prop_assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
}
}
}
}