Skip to main content

nu_command/random/
pass.rs

1use aegis_password_generator::types::PasswordConfig;
2use nu_engine::command_prelude::*;
3use nu_protocol::shell_error::generic::GenericError;
4
5const DEFAULT_PASSWORD_LENGTH: usize = 12;
6
7#[derive(Clone)]
8pub struct RandomPass;
9
10impl Command for RandomPass {
11    fn name(&self) -> &str {
12        "random pass"
13    }
14
15    fn signature(&self) -> Signature {
16        Signature::build("random pass")
17            .input_output_types(vec![(Type::Nothing, Type::String)])
18            .allow_variants_without_examples(true)
19            .named(
20                "chars",
21                SyntaxShape::Int,
22                "Length of the generated password (default 12).",
23                Some('c'),
24            )
25            .switch("no-uppercase", "Exclude uppercase letters A-Z.", Some('u'))
26            .switch("no-lowercase", "Exclude lowercase letters a-z.", Some('l'))
27            .switch("no-numbers", "Exclude numbers 0-9.", Some('n'))
28            .switch("no-symbols", "Exclude symbols like !@#$%.", Some('s'))
29            .switch(
30                "include-ambiguous",
31                "Include ambiguous characters O, 0, l, 1.",
32                None,
33            )
34            .switch(
35                "include-similar",
36                "Include similar characters i, l, 1.",
37                None,
38            )
39            .switch(
40                "require-each-type",
41                "Guarantee at least one char from each enabled character type.",
42                None,
43            )
44            .category(Category::Random)
45    }
46
47    fn description(&self) -> &str {
48        "Generate a cryptologically secure password."
49    }
50
51    fn search_terms(&self) -> Vec<&str> {
52        vec!["password", "generate", "crypto", "secure"]
53    }
54
55    fn run(
56        &self,
57        engine_state: &EngineState,
58        stack: &mut Stack,
59        call: &Call,
60        _input: PipelineData,
61    ) -> Result<PipelineData, ShellError> {
62        let span = call.head;
63
64        let chars: Option<i64> = call.get_flag(engine_state, stack, "chars")?;
65        let no_uppercase = call.has_flag(engine_state, stack, "no-uppercase")?;
66        let no_lowercase = call.has_flag(engine_state, stack, "no-lowercase")?;
67        let no_numbers = call.has_flag(engine_state, stack, "no-numbers")?;
68        let no_symbols = call.has_flag(engine_state, stack, "no-symbols")?;
69        let include_ambiguous = call.has_flag(engine_state, stack, "include-ambiguous")?;
70        let include_similar = call.has_flag(engine_state, stack, "include-similar")?;
71        let require_each_type = call.has_flag(engine_state, stack, "require-each-type")?;
72
73        let length = chars.map(|c| c as usize).unwrap_or(DEFAULT_PASSWORD_LENGTH);
74
75        let config = PasswordConfig::default()
76            .with_length(length)
77            .with_uppercase(!no_uppercase)
78            .with_lowercase(!no_lowercase)
79            .with_numbers(!no_numbers)
80            .with_symbols(!no_symbols)
81            .with_exclude_ambiguous(!include_ambiguous)
82            .with_exclude_similar(!include_similar)
83            .with_require_each_type(require_each_type);
84
85        match config.generate() {
86            Ok(password) => Ok(Value::string(password, span).into_pipeline_data()),
87            Err(e) => Err(ShellError::Generic(GenericError::new(
88                "Password generation failed",
89                e.to_string(),
90                span,
91            ))),
92        }
93    }
94
95    fn examples(&self) -> Vec<Example<'_>> {
96        vec![
97            Example {
98                description: "Generate a 12-character password with defaults",
99                example: "random pass",
100                result: None,
101            },
102            Example {
103                description: "Generate a 20-character password",
104                example: "random pass --chars 20",
105                result: None,
106            },
107            Example {
108                description: "Generate a password without symbols",
109                example: "random pass --no-symbols",
110                result: None,
111            },
112            Example {
113                description: "Generate a password with only uppercase letters and numbers",
114                example: "random pass --no-lowercase --no-symbols",
115                result: None,
116            },
117            Example {
118                description: "Generate a password including ambiguous characters and requiring each type",
119                example: "random pass --include-ambiguous --require-each-type",
120                result: None,
121            },
122        ]
123    }
124}
125
126#[cfg(test)]
127mod test {
128    use super::*;
129
130    #[test]
131    fn test_examples() -> nu_test_support::Result {
132        nu_test_support::test().examples(RandomPass)
133    }
134}