nu_command/random/
pass.rs1use 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}