Skip to main content

rsomics_common/
threads.rs

1use std::num::NonZeroUsize;
2
3use clap::Args;
4
5/// Shared worker-count selection for parallel rsomics products.
6#[derive(Debug, Default, Clone, Args)]
7#[command(next_help_heading = "Global options")]
8pub struct ThreadArgs {
9    /// Number of worker threads.
10    #[arg(short = 't', long, global = true)]
11    threads: Option<NonZeroUsize>,
12}
13
14impl ThreadArgs {
15    #[must_use]
16    pub const fn requested(&self) -> Option<NonZeroUsize> {
17        self.threads
18    }
19}
20
21#[cfg(test)]
22mod tests {
23    use super::*;
24    use clap::{Parser, Subcommand};
25
26    #[derive(Debug, Parser)]
27    struct Cli {
28        #[command(flatten)]
29        threads: ThreadArgs,
30        #[command(subcommand)]
31        command: Command,
32    }
33
34    #[derive(Debug, Subcommand)]
35    enum Command {
36        Run,
37    }
38
39    #[test]
40    fn defaults_to_runtime_selection() {
41        let cli = Cli::parse_from(["test", "run"]);
42        assert_eq!(cli.threads.requested(), None);
43    }
44
45    #[test]
46    fn parses_global_short_and_long_forms() {
47        let before = Cli::parse_from(["test", "-t", "2", "run"]);
48        let after = Cli::parse_from(["test", "run", "--threads", "4"]);
49        assert_eq!(before.threads.requested().map(NonZeroUsize::get), Some(2));
50        assert_eq!(after.threads.requested().map(NonZeroUsize::get), Some(4));
51    }
52
53    #[test]
54    fn rejects_zero() {
55        assert!(Cli::try_parse_from(["test", "--threads", "0", "run"]).is_err());
56    }
57}