use std::str::FromStr;
use clap::Subcommand;
use seahash::hash;
#[derive(Subcommand, Debug, Clone)]
pub enum Seeds {
#[clap(visible_alias = "s")]
Single {
seed: NumberOrString,
},
#[clap(visible_aliases = ["r", "rand"])]
Random {
count: Option<u64>,
},
Range {
min: u64,
max: u64,
#[clap(short, long)]
step: Option<u64>,
},
Full,
}
impl Seeds {
#[must_use]
pub fn iter(&self) -> SeedIter {
<&Self as IntoIterator>::into_iter(self)
}
}
#[derive(Debug, Clone)]
pub enum NumberOrString {
Str(String),
Number(u64),
}
pub struct SeedIter {
current: Option<u64>,
max: u64,
step: u64,
randomize: bool,
}
impl SeedIter {
fn new(current: u64, max: u64, step: u64, randomize: bool) -> Self {
Self {
current: Some(current),
max,
step,
randomize,
}
}
}
impl Iterator for SeedIter {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
if let Some(current) = self.current {
let seed = if self.randomize {
rand::random()
} else {
current
};
if current == self.max {
self.current = None;
} else {
let _ = self.current.replace(current + self.step);
}
Some(seed)
} else {
None
}
}
}
impl IntoIterator for &Seeds {
type Item = u64;
type IntoIter = SeedIter;
fn into_iter(self) -> Self::IntoIter {
match self {
Seeds::Single { seed } => match seed {
NumberOrString::Str(s) => {
let seed = hash(s.as_bytes());
SeedIter::new(seed, seed, 1, false)
}
NumberOrString::Number(n) => SeedIter::new(*n, *n, 1, false),
},
Seeds::Range { min, max, step } => SeedIter::new(*min, *max, step.unwrap_or(1), false),
Seeds::Random { count } => SeedIter::new(1, count.unwrap_or(1), 1, true),
Seeds::Full => SeedIter::new(u64::MIN, u64::MAX, 1, false),
}
}
}
impl IntoIterator for Seeds {
type Item = u64;
type IntoIter = SeedIter;
fn into_iter(self) -> Self::IntoIter {
(&self).into_iter()
}
}
impl FromStr for NumberOrString {
type Err = &'static str; fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(s.parse::<u64>().map_or_else(
|_| NumberOrString::Str(s.to_string()),
NumberOrString::Number,
))
}
}