-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from upupnoah/main
feat(genpass): support random password generator and zxcvbn
- Loading branch information
Showing
7 changed files
with
204 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
use rand::seq::SliceRandom; | ||
use rand::thread_rng; | ||
|
||
fn main() { | ||
let choices = [1, 2, 4, 8, 16, 32]; | ||
let mut rng = thread_rng(); | ||
println!("{:?}", choices.choose(&mut rng)); | ||
assert_eq!(choices[..0].choose(&mut rng), None); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
mod opts; | ||
mod process; | ||
|
||
pub use opts::{Opts, SubCommand}; | ||
pub use process::process_csv; | ||
pub use opts::{Opts, SubCommand, GenPassOpts}; | ||
pub use process::{process_csv, process_genpass}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,22 @@ | ||
use clap::Parser; | ||
use rcli::{process_csv, Opts, SubCommand}; | ||
use rcli::{process_csv, process_genpass, Opts, SubCommand}; | ||
|
||
fn main() -> anyhow::Result<()> { | ||
let opts = Opts::parse(); | ||
match opts.cmd { | ||
SubCommand::Csv(opts) => { | ||
process_csv(&opts.input, &opts.output, &opts.format)?; | ||
} | ||
SubCommand::GenPass(opts) => { | ||
let password = process_genpass( | ||
opts.length, | ||
opts.upper, | ||
opts.lower, | ||
opts.number, | ||
opts.symbol, | ||
)?; | ||
println!("Generate password: {:?}", password); | ||
} | ||
} | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,5 @@ | ||
use std::fs; | ||
mod csv_convert; | ||
mod gen_pass; | ||
|
||
use csv::Reader; | ||
use serde::{Deserialize, Serialize}; | ||
use serde_json::Value; | ||
|
||
use crate::opts::OutputFormat; | ||
|
||
#[derive(Debug, Serialize, Deserialize)] | ||
#[serde(rename_all = "PascalCase")] | ||
struct Player { | ||
// #[serde(rename = "Name")] | ||
name: String, | ||
// #[serde(rename = "Position")] | ||
position: String, | ||
#[serde(rename = "DOB")] | ||
dob: String, | ||
// #[serde(rename = "Nationality")] | ||
nationality: String, | ||
#[serde(rename = "Kit Number")] | ||
kit_number: u8, | ||
} | ||
|
||
pub fn process_csv(input: &str, output: &str, format: &OutputFormat) -> anyhow::Result<()> { | ||
let mut rdr = Reader::from_path(input)?; | ||
// ***** from for loop to map ***** | ||
// let mut records = Vec::new(); | ||
// for result in rdr.deserialize() { | ||
// let record: Player = result.unwrap(); | ||
// records.push(record); | ||
// } | ||
// let records = rdr | ||
// .deserialize() | ||
// .map(|record| record.unwrap()) | ||
// .collect::<Vec<Player>>(); | ||
|
||
// ***** more universal way with map ***** | ||
// let headers = rdr.headers()?.clone(); | ||
// let ret = rdr | ||
// .records() | ||
// .map(|record| { | ||
// let record = record.unwrap(); | ||
// headers.iter().zip(record.iter()).collect::<Value>() // return Value type | ||
// }) | ||
// .collect::<Value>(); | ||
|
||
// ***** more universal way with for loop ***** | ||
let headers = rdr.headers()?.clone(); | ||
let mut ret = Vec::with_capacity(128); | ||
for record in rdr.records() { | ||
let record = record?; | ||
// headers.iter() -> use the iterator of headers | ||
// record.iter() -> use the iterator of record | ||
// zip() -> combine the two iterators into one iterator of tuples [(header, record), ...] | ||
// collect::<Value>() -> convert the iterator of tuples into a Value type | ||
let json_value = headers.iter().zip(record.iter()).collect::<Value>(); | ||
ret.push(json_value); | ||
} | ||
// let json = serde_json::to_string_pretty(&ret)?; | ||
let content = match format { | ||
OutputFormat::Json => serde_json::to_string_pretty(&ret)?, | ||
OutputFormat::Yaml => serde_yaml::to_string(&ret)?, | ||
// OutputFormat::Toml => toml::to_string(&ret)?, | ||
}; | ||
fs::write(output, content)?; | ||
Ok(()) | ||
} | ||
pub use csv_convert::process_csv; | ||
pub use gen_pass::process_genpass; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
use std::fs; | ||
|
||
use csv::Reader; | ||
use serde_json::Value; | ||
|
||
use crate::opts::OutputFormat; | ||
|
||
// #[derive(Debug, Serialize, Deserialize)] | ||
// #[serde(rename_all = "PascalCase")] | ||
// struct Player { | ||
// // #[serde(rename = "Name")] | ||
// name: String, | ||
// // #[serde(rename = "Position")] | ||
// position: String, | ||
// #[serde(rename = "DOB")] | ||
// dob: String, | ||
// // #[serde(rename = "Nationality")] | ||
// nationality: String, | ||
// #[serde(rename = "Kit Number")] | ||
// kit_number: u8, | ||
// } | ||
|
||
pub fn process_csv(input: &str, output: &str, format: &OutputFormat) -> anyhow::Result<()> { | ||
let mut rdr = Reader::from_path(input)?; | ||
// ***** from for loop to map ***** | ||
// let mut records = Vec::new(); | ||
// for result in rdr.deserialize() { | ||
// let record: Player = result.unwrap(); | ||
// records.push(record); | ||
// } | ||
// let records = rdr | ||
// .deserialize() | ||
// .map(|record| record.unwrap()) | ||
// .collect::<Vec<Player>>(); | ||
|
||
// ***** more universal way with map ***** | ||
// let headers = rdr.headers()?.clone(); | ||
// let ret = rdr | ||
// .records() | ||
// .map(|record| { | ||
// let record = record.unwrap(); | ||
// headers.iter().zip(record.iter()).collect::<Value>() // return Value type | ||
// }) | ||
// .collect::<Value>(); | ||
|
||
// ***** more universal way with for loop ***** | ||
let headers = rdr.headers()?.clone(); | ||
let mut ret = Vec::with_capacity(128); | ||
for record in rdr.records() { | ||
let record = record?; | ||
// headers.iter() -> use the iterator of headers | ||
// record.iter() -> use the iterator of record | ||
// zip() -> combine the two iterators into one iterator of tuples [(header, record), ...] | ||
// collect::<Value>() -> convert the iterator of tuples into a Value type | ||
let json_value = headers.iter().zip(record.iter()).collect::<Value>(); | ||
ret.push(json_value); | ||
} | ||
// let json = serde_json::to_string_pretty(&ret)?; | ||
let content = match format { | ||
OutputFormat::Json => serde_json::to_string_pretty(&ret)?, | ||
OutputFormat::Yaml => serde_yaml::to_string(&ret)?, | ||
// OutputFormat::Toml => toml::to_string(&ret)?, | ||
}; | ||
fs::write(output, content)?; | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
use rand::seq::SliceRandom; | ||
use zxcvbn::zxcvbn; | ||
|
||
const UPPER: &[u8] = b"ABCDEFGHJKLMNPQRSTUVWXYZ"; | ||
const LOWER: &[u8] = b"abcdefghijkmnopqrstuvwxyz"; | ||
const NUMBER: &[u8] = b"123456789"; | ||
const SYMBOL: &[u8] = b"!@#$%^&*_"; | ||
|
||
// pub fn process_genpass(opts: &GenPassOpts) -> anyhow::Result<String> { | ||
// let mut rng = rand::thread_rng(); | ||
// let mut password = Vec::new(); | ||
// let mut chars = Vec::new(); | ||
|
||
// if opts.uppercase { | ||
// chars.extend_from_slice(UPPER); | ||
// password.push(*UPPER.choose(&mut rng).expect("UPPER won't be empty")); | ||
// } | ||
// if opts.lowercase { | ||
// chars.extend_from_slice(LOWER); | ||
// password.push(*LOWER.choose(&mut rng).expect("LOWER won't be empty")); | ||
// } | ||
// if opts.number { | ||
// chars.extend_from_slice(NUMBER); | ||
// password.push(*NUMBER.choose(&mut rng).expect("NUMBER won't be empty")); | ||
// } | ||
// if opts.symbol { | ||
// chars.extend_from_slice(SYMBOL); | ||
// password.push(*SYMBOL.choose(&mut rng).expect("SYMBOL won't be empty")); | ||
// } | ||
|
||
// for _ in 0..(opts.length - password.len() as u8) { | ||
// let c = chars | ||
// .choose(&mut rng) | ||
// .expect("chars won't be empty in this context"); | ||
// password.push(*c); | ||
// } | ||
|
||
// password.shuffle(&mut rng); | ||
|
||
// Ok(String::from_utf8(password)?) | ||
// } | ||
|
||
// 把数据结构拆解出来, 不与 opts.rs 产生耦合 | ||
// 好处: 下次想把这部分逻辑拆解分来, 放到其他 repo 的时候, 就会比较方便了 | ||
pub fn process_genpass( | ||
length: u8, | ||
upper: bool, | ||
lower: bool, | ||
number: bool, | ||
symbol: bool, | ||
) -> anyhow::Result<String> { | ||
let mut rng = rand::thread_rng(); | ||
let mut password = Vec::new(); | ||
let mut chars = Vec::new(); | ||
|
||
if upper { | ||
chars.extend_from_slice(UPPER); | ||
password.push(*UPPER.choose(&mut rng).expect("UPPER won't be empty")); | ||
} | ||
if lower { | ||
chars.extend_from_slice(LOWER); | ||
password.push(*LOWER.choose(&mut rng).expect("LOWER won't be empty")); | ||
} | ||
if number { | ||
chars.extend_from_slice(NUMBER); | ||
password.push(*NUMBER.choose(&mut rng).expect("NUMBER won't be empty")); | ||
} | ||
if symbol { | ||
chars.extend_from_slice(SYMBOL); | ||
password.push(*SYMBOL.choose(&mut rng).expect("SYMBOL won't be empty")); | ||
} | ||
|
||
for _ in 0..(length - password.len() as u8) { | ||
let c = chars | ||
.choose(&mut rng) | ||
.expect("chars won't be empty in this context"); | ||
password.push(*c); | ||
} | ||
|
||
password.shuffle(&mut rng); | ||
|
||
let password = String::from_utf8(password)?; | ||
|
||
let estimate = zxcvbn(&password, &[]); | ||
println!("Password strength: {}", estimate.score()); | ||
|
||
Ok(password) | ||
} |