nu_command/random/
chars.rs

1use super::byte_stream::{RandomDistribution, random_byte_stream};
2use nu_engine::command_prelude::*;
3
4const DEFAULT_CHARS_LENGTH: usize = 25;
5
6#[derive(Clone)]
7pub struct RandomChars;
8
9impl Command for RandomChars {
10    fn name(&self) -> &str {
11        "random chars"
12    }
13
14    fn signature(&self) -> Signature {
15        Signature::build("random chars")
16            .input_output_types(vec![(Type::Nothing, Type::String)])
17            .allow_variants_without_examples(true)
18            .named(
19                "length",
20                SyntaxShape::OneOf(vec![SyntaxShape::Int, SyntaxShape::Filesize]),
21                "Number of chars (default 25)",
22                Some('l'),
23            )
24            .category(Category::Random)
25    }
26
27    fn description(&self) -> &str {
28        "Generate random chars uniformly distributed over ASCII letters and numbers: a-z, A-Z and 0-9."
29    }
30
31    fn search_terms(&self) -> Vec<&str> {
32        vec!["generate", "character", "symbol", "alphanumeric"]
33    }
34
35    fn run(
36        &self,
37        engine_state: &EngineState,
38        stack: &mut Stack,
39        call: &Call,
40        _input: PipelineData,
41    ) -> Result<PipelineData, ShellError> {
42        chars(engine_state, stack, call)
43    }
44
45    fn examples(&self) -> Vec<Example> {
46        vec![
47            Example {
48                description: "Generate a string with 25 random chars",
49                example: "random chars",
50                result: None,
51            },
52            Example {
53                description: "Generate random chars with specified length",
54                example: "random chars --length 20",
55                result: None,
56            },
57            Example {
58                description: "Generate one kilobyte of random chars",
59                example: "random chars --length 1kb",
60                result: None,
61            },
62        ]
63    }
64}
65
66fn chars(
67    engine_state: &EngineState,
68    stack: &mut Stack,
69    call: &Call,
70) -> Result<PipelineData, ShellError> {
71    let length: Option<Value> = call.get_flag(engine_state, stack, "length")?;
72    let length = if let Some(length_val) = length {
73        match length_val {
74            Value::Int { val, .. } => usize::try_from(val).map_err(|_| ShellError::InvalidValue {
75                valid: "a non-negative int or filesize".into(),
76                actual: val.to_string(),
77                span: length_val.span(),
78            }),
79            Value::Filesize { val, .. } => {
80                usize::try_from(val).map_err(|_| ShellError::InvalidValue {
81                    valid: "a non-negative int or filesize".into(),
82                    actual: engine_state.get_config().filesize.format(val).to_string(),
83                    span: length_val.span(),
84                })
85            }
86            val => Err(ShellError::RuntimeTypeMismatch {
87                expected: Type::custom("int or filesize"),
88                actual: val.get_type(),
89                span: val.span(),
90            }),
91        }?
92    } else {
93        DEFAULT_CHARS_LENGTH
94    };
95
96    Ok(random_byte_stream(
97        RandomDistribution::Alphanumeric,
98        length,
99        call.head,
100        engine_state.signals().clone(),
101    ))
102}
103
104#[cfg(test)]
105mod test {
106    use super::*;
107
108    #[test]
109    fn test_examples() {
110        use crate::test_examples;
111
112        test_examples(RandomChars {})
113    }
114}