nu_command/random/
int.rs

1use nu_engine::command_prelude::*;
2use nu_protocol::Range;
3use rand::random_range;
4use std::ops::Bound;
5
6#[derive(Clone)]
7pub struct RandomInt;
8
9impl Command for RandomInt {
10    fn name(&self) -> &str {
11        "random int"
12    }
13
14    fn signature(&self) -> Signature {
15        Signature::build("random int")
16            .input_output_types(vec![(Type::Nothing, Type::Int)])
17            .allow_variants_without_examples(true)
18            .optional(
19                "range",
20                SyntaxShape::Range,
21                "Range of potential values, inclusive of both start and end values.",
22            )
23            .category(Category::Random)
24    }
25
26    fn description(&self) -> &str {
27        "Generate a random integer [min..max]."
28    }
29
30    fn search_terms(&self) -> Vec<&str> {
31        vec!["generate", "natural", "number"]
32    }
33
34    fn run(
35        &self,
36        engine_state: &EngineState,
37        stack: &mut Stack,
38        call: &Call,
39        _input: PipelineData,
40    ) -> Result<PipelineData, ShellError> {
41        integer(engine_state, stack, call)
42    }
43
44    fn examples(&self) -> Vec<Example> {
45        vec![
46            Example {
47                description: "Generate a non-negative random integer",
48                example: "random int",
49                result: None,
50            },
51            Example {
52                description: "Generate a random integer between 0 (inclusive) and 500 (inclusive)",
53                example: "random int ..500",
54                result: None,
55            },
56            Example {
57                description: "Generate a random integer greater than or equal to 100000",
58                example: "random int 100000..",
59                result: None,
60            },
61            Example {
62                description: "Generate a random integer between -10 (inclusive) and 10 (inclusive)",
63                example: "random int (-10)..10",
64                result: None,
65            },
66        ]
67    }
68}
69
70fn integer(
71    engine_state: &EngineState,
72    stack: &mut Stack,
73    call: &Call,
74) -> Result<PipelineData, ShellError> {
75    let span = call.head;
76    let range: Option<Spanned<Range>> = call.opt(engine_state, stack, 0)?;
77
78    match range {
79        Some(range) => {
80            let range_span = range.span;
81            match range.item {
82                Range::IntRange(range) => {
83                    if range.step() < 0 {
84                        return Err(ShellError::InvalidRange {
85                            left_flank: range.start().to_string(),
86                            right_flank: match range.end() {
87                                Bound::Included(end) | Bound::Excluded(end) => end.to_string(),
88                                Bound::Unbounded => "".into(),
89                            },
90                            span: range_span,
91                        });
92                    }
93
94                    let value = match range.end() {
95                        Bound::Included(end) => random_range(range.start()..=end),
96                        Bound::Excluded(end) => random_range(range.start()..end),
97                        Bound::Unbounded => random_range(range.start()..=i64::MAX),
98                    };
99
100                    Ok(PipelineData::Value(Value::int(value, span), None))
101                }
102                Range::FloatRange(_) => Err(ShellError::UnsupportedInput {
103                    msg: "float range".into(),
104                    input: "value originates from here".into(),
105                    msg_span: call.head,
106                    input_span: range.span,
107                }),
108            }
109        }
110        None => Ok(PipelineData::Value(
111            Value::int(random_range(0..=i64::MAX), span),
112            None,
113        )),
114    }
115}
116
117#[cfg(test)]
118mod test {
119    use super::*;
120
121    #[test]
122    fn test_examples() {
123        use crate::test_examples;
124
125        test_examples(RandomInt {})
126    }
127}