seed_gen/
cli.rs

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    /// Execute the operation for a specified seed
10    Single {
11        /// The specified seed
12        seed: NumberOrString,
13    },
14    #[clap(visible_aliases = ["r", "rand"])]
15    /// Execute the operation for a specified number of random seeds
16    Random {
17        /// The number of seeds
18        count: Option<u64>,
19    },
20    /// Execute the operation across a specified range of seeds
21    Range {
22        /// The starting point of the range
23        min: u64,
24        /// The ending point of the range
25        max: u64,
26        #[clap(short, long)]
27        /// The interval between seeds in the range
28        step: Option<u64>,
29    },
30    /// Execute the operation across the full possible range of seeds
31    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; // The actual type doesn't matter since we never error, but it must implement `Display`
120    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}