pants_store/
store.rs

1use std::{error::Error, fmt::Display};
2
3use inquire::Confirm;
4use pants_gen::password::Password;
5use serde::{Deserialize, Serialize};
6
7use crate::errors::SchemaError;
8
9#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
10pub enum Store {
11    Password(String),
12    UsernamePassword(String, String),
13}
14
15impl Display for Store {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        match self {
18            Self::Password(p) => write!(f, "{}", p),
19            Self::UsernamePassword(username, password) => write!(f, "{}: {}", username, password),
20        }
21    }
22}
23
24impl Store {
25    // how to represent the type in the schema
26    pub fn repr(&self) -> String {
27        match self {
28            Self::Password(_) => "password".to_string(),
29            Self::UsernamePassword(_, _) => "username-password".to_string(),
30        }
31    }
32
33    // // from schema type and field values
34    // pub fn from_fields(
35    //     repr: &str,
36    //     value: HashMap<String, String>,
37    // ) -> Result<Store, Box<dyn Error>> {
38    //     match repr {
39    //         "password" => {
40    //             if let Some(p) = value.get("password") {
41    //                 Ok(Self::Password(p.to_string()))
42    //             } else {
43    //                 Err(Box::new(SchemaError::BadValues))
44    //             }
45    //         }
46    //         _ => Err(Box::new(SchemaError::BadType)),
47    //     }
48    // }
49    //
50    // // from  schema type and array of values
51    // pub fn from_array(repr: &str, value: Vec<String>) -> Result<Store, Box<dyn Error>> {
52    //     match repr {
53    //         "password" => {
54    //             if let Some(p) = value.first() {
55    //                 Ok(Self::Password(p.to_string()))
56    //             } else {
57    //                 Err(Box::new(SchemaError::BadValues))
58    //             }
59    //         }
60    //         _ => Err(Box::new(SchemaError::BadType)),
61    //     }
62    // }
63
64    pub fn prompt(repr: &str) -> Result<Store, Box<dyn Error>> {
65        match repr {
66            "password" => Self::get_password().map(Store::Password),
67            "username-password" => {
68                let username_input = inquire::Text::new("Username:")
69                    .with_help_message("New username")
70                    .prompt();
71                let username = username_input?;
72                let password = Self::get_password()?;
73                Ok(Store::UsernamePassword(username, password))
74            }
75            _ => Err(Box::new(SchemaError::BadType)),
76        }
77    }
78
79    fn get_password() -> Result<String, Box<dyn Error>> {
80        let generate = Confirm::new("Generate password?")
81            .with_default(true)
82            .with_help_message("Create a random password or enter manually?")
83            .prompt();
84        match generate {
85            Ok(true) => {
86                let length_input = inquire::CustomType::<usize>::new("Length of password?")
87                    .with_help_message("Number of characters use in the generated password")
88                    .with_error_message("Please type a valid number")
89                    .with_default(32)
90                    .prompt()?;
91                let upper = inquire::Confirm::new("Use uppercase letters?")
92                    .with_default(true)
93                    .with_help_message(
94                        "Use the uppercase alphabetic characters (A-Z) in password generation",
95                    )
96                    .prompt()?;
97                let lower = inquire::Confirm::new("Use lowercase letters?")
98                    .with_default(true)
99                    .with_help_message(
100                        "Use the lowercase alphabetic characters (a-z) in password generation",
101                    )
102                    .prompt()?;
103                let numbers = inquire::Confirm::new("Use numbers?")
104                    .with_default(true)
105                    .with_help_message("Use the numbers (0-9) in password generation")
106                    .prompt()?;
107                let symbols = inquire::Confirm::new("Use symbols?")
108                    .with_default(true)
109                    .with_help_message("Use special symbols in password generation")
110                    .prompt()?;
111
112                let mut spec = Password::new().length(length_input);
113
114                if upper {
115                    spec = spec.upper_at_least(1);
116                }
117                if lower {
118                    spec = spec.lower_at_least(1);
119                }
120                if numbers {
121                    spec = spec.number_at_least(1);
122                }
123                if symbols {
124                    spec = spec.symbol_at_least(1);
125                }
126                let password = spec.generate().unwrap();
127                Ok(password)
128            }
129            Ok(false) => {
130                let password_input = inquire::Password::new("Password: ")
131                    .with_display_toggle_enabled()
132                    .with_display_mode(inquire::PasswordDisplayMode::Masked)
133                    .prompt();
134                match password_input {
135                    Ok(p) => Ok(p),
136                    Err(_) => Err(Box::new(SchemaError::BadValues)),
137                }
138            }
139            Err(_) => Err(Box::new(SchemaError::BadValues)),
140        }
141    }
142}