1use std::str::FromStr;
2
3use clap::Subcommand;
4use seahash::hash;
5
6#[derive(Subcommand, Debug, Clone)]
7pub enum Seeds {
8 #[clap(visible_alias = "s")]
9 Single {
11 seed: NumberOrString,
13 },
14 #[clap(visible_aliases = ["r", "rand"])]
15 Random {
17 count: Option<u64>,
19 },
20 Range {
22 min: u64,
24 max: u64,
26 #[clap(short, long)]
27 step: Option<u64>,
29 },
30 Full,
32}
33
34impl Seeds {
35 #[must_use]
36 pub fn iter(&self) -> SeedIter {
37 <&Self as IntoIterator>::into_iter(self)
38 }
39}
40
41#[derive(Debug, Clone)]
42pub enum NumberOrString {
43 Str(String),
44 Number(u64),
45}
46
47pub struct SeedIter {
48 current: Option<u64>,
49 max: u64,
50 step: u64,
51 randomize: bool,
52}
53
54impl SeedIter {
55 fn new(current: u64, max: u64, step: u64, randomize: bool) -> Self {
56 Self {
57 current: Some(current),
58 max,
59 step,
60 randomize,
61 }
62 }
63}
64
65impl Iterator for SeedIter {
66 type Item = u64;
67
68 fn next(&mut self) -> Option<Self::Item> {
69 if let Some(current) = self.current {
70 let seed = if self.randomize {
71 rand::random()
72 } else {
73 current
74 };
75 if current == self.max {
76 self.current = None;
77 } else {
78 let _ = self.current.replace(current + self.step);
79 }
80 Some(seed)
81 } else {
82 None
83 }
84 }
85}
86
87impl IntoIterator for &Seeds {
88 type Item = u64;
89
90 type IntoIter = SeedIter;
91
92 fn into_iter(self) -> Self::IntoIter {
93 match self {
94 Seeds::Single { seed } => match seed {
95 NumberOrString::Str(s) => {
96 let seed = hash(s.as_bytes());
97 SeedIter::new(seed, seed, 1, false)
98 }
99 NumberOrString::Number(n) => SeedIter::new(*n, *n, 1, false),
100 },
101 Seeds::Range { min, max, step } => SeedIter::new(*min, *max, step.unwrap_or(1), false),
102 Seeds::Random { count } => SeedIter::new(1, count.unwrap_or(1), 1, true),
103 Seeds::Full => SeedIter::new(u64::MIN, u64::MAX, 1, false),
104 }
105 }
106}
107
108impl IntoIterator for Seeds {
109 type Item = u64;
110
111 type IntoIter = SeedIter;
112
113 fn into_iter(self) -> Self::IntoIter {
114 (&self).into_iter()
115 }
116}
117
118impl FromStr for NumberOrString {
119 type Err = &'static str; fn from_str(s: &str) -> Result<Self, Self::Err> {
121 Ok(s.parse::<u64>().map_or_else(
122 |_| NumberOrString::Str(s.to_string()),
123 NumberOrString::Number,
124 ))
125 }
126}