aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--README.md48
-rwxr-xr-xmakepass.pl2
-rw-r--r--rust/Cargo.lock363
-rw-r--r--rust/Cargo.toml13
-rw-r--r--rust/src/main.rs260
6 files changed, 670 insertions, 18 deletions
diff --git a/.gitignore b/.gitignore
index e720fd5..8976dda 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
# ignore binaries
/makepass.go
+/makepass.rs
+rust/target
diff --git a/README.md b/README.md
index eee6ee2..e9e9832 100644
--- a/README.md
+++ b/README.md
@@ -163,31 +163,36 @@ Here are the results so far:
```
% hyperfine --time-unit=millisecond --warmup=1 --shell=none ./makepass.*
Benchmark 1: ./makepass.bash
- Time (mean ± σ): 75.4 ms ± 1.8 ms [User: 29.5 ms, System: 37.9 ms]
- Range (min … max): 71.4 ms … 78.3 ms 39 runs
+ Time (mean ± σ): 77.4 ms ± 2.1 ms [User: 27.8 ms, System: 33.8 ms]
+ Range (min … max): 72.8 ms … 80.8 ms 37 runs
Benchmark 2: ./makepass.go
- Time (mean ± σ): 7.0 ms ± 0.2 ms [User: 1.6 ms, System: 4.8 ms]
- Range (min … max): 6.4 ms … 7.5 ms 424 runs
+ Time (mean ± σ): 7.2 ms ± 0.2 ms [User: 1.2 ms, System: 4.9 ms]
+ Range (min … max): 6.6 ms … 7.6 ms 409 runs
Benchmark 3: ./makepass.pl
- Time (mean ± σ): 38.7 ms ± 0.3 ms [User: 18.8 ms, System: 18.5 ms]
- Range (min … max): 38.1 ms … 39.9 ms 78 runs
+ Time (mean ± σ): 39.4 ms ± 0.3 ms [User: 19.6 ms, System: 15.4 ms]
+ Range (min … max): 38.8 ms … 40.4 ms 76 runs
-Benchmark 4: ./makepass.sh
- Time (mean ± σ): 624.1 ms ± 2.5 ms [User: 159.0 ms, System: 903.0 ms]
- Range (min … max): 620.1 ms … 626.7 ms 10 runs
+Benchmark 4: ./makepass.rs
+ Time (mean ± σ): 4.8 ms ± 0.2 ms [User: 1.3 ms, System: 2.3 ms]
+ Range (min … max): 4.3 ms … 5.7 ms 606 runs
-Benchmark 5: ./makepass.zsh
- Time (mean ± σ): 26.3 ms ± 0.6 ms [User: 12.2 ms, System: 11.1 ms]
- Range (min … max): 25.1 ms … 27.8 ms 114 runs
+Benchmark 5: ./makepass.sh
+ Time (mean ± σ): 646.5 ms ± 2.6 ms [User: 145.0 ms, System: 1005.0 ms]
+ Range (min … max): 643.2 ms … 651.3 ms 10 runs
+
+Benchmark 6: ./makepass.zsh
+ Time (mean ± σ): 26.9 ms ± 0.6 ms [User: 12.4 ms, System: 12.3 ms]
+ Range (min … max): 25.5 ms … 28.4 ms 111 runs
Summary
- './makepass.go' ran
- 3.76 ± 0.12 times faster than './makepass.zsh'
- 5.53 ± 0.13 times faster than './makepass.pl'
- 10.78 ± 0.36 times faster than './makepass.bash'
- 89.19 ± 2.10 times faster than './makepass.sh'
+ './makepass.rs' ran
+ 1.49 ± 0.06 times faster than './makepass.go'
+ 5.56 ± 0.23 times faster than './makepass.zsh'
+ 8.16 ± 0.28 times faster than './makepass.pl'
+ 16.02 ± 0.69 times faster than './makepass.bash'
+ 133.85 ± 4.52 times faster than './makepass.sh'
```
## Versions
@@ -205,6 +210,15 @@ $ go build -o ../makepass.go -C go/ -ldflags "-s -w" makepass.go
### Perl
Perl version. I like this version <3
+### Rust
+Rust-version. It's fast!
+
+Build with (from root):
+
+```
+$ cd rust; cargo build --release; cd ../; ln -s rust/target/release/makepass makepass.rs
+```
+
### Shell
*Should* be pure POSIX sh. Needs some cleanup.
diff --git a/makepass.pl b/makepass.pl
index 36bfcf1..bfe43f8 100755
--- a/makepass.pl
+++ b/makepass.pl
@@ -5,7 +5,7 @@
# Created : 2023-07-27
# License : BSD-3-Clause
#
-# Copyright (c) 2018-2022 Dennis Eriksen • d@ennis.no
+# Copyright (c) 2018-2023 Dennis Eriksen • d@ennis.no
use strict;
use warnings;
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
new file mode 100644
index 0000000..50747ce
--- /dev/null
+++ b/rust/Cargo.lock
@@ -0,0 +1,363 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "anstream"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is-terminal",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c"
+dependencies = [
+ "anstyle",
+ "windows-sys",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb690e81c7840c0d7aade59f242ea3b41b9bc27bcd5997890e7702ae4b32e487"
+dependencies = [
+ "clap_builder",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ed2e96bc16d8d740f6f48d663eddf4b8a0983e79210fd55479b7bcd0a69860e"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "once_cell",
+ "strsim",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "errno"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
+dependencies = [
+ "hermit-abi",
+ "rustix 0.38.13",
+ "windows-sys",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128"
+
+[[package]]
+name = "makepass"
+version = "0.1.0"
+dependencies = [
+ "clap",
+ "rand",
+ "terminal_size",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rustix"
+version = "0.37.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
+dependencies = [
+ "bitflags 1.3.2",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys 0.3.8",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662"
+dependencies = [
+ "bitflags 2.4.0",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.7",
+ "windows-sys",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "terminal_size"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
+dependencies = [
+ "rustix 0.37.23",
+ "windows-sys",
+]
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
new file mode 100644
index 0000000..99ede9d
--- /dev/null
+++ b/rust/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "makepass"
+version = "0.1.0"
+edition = "2021"
+rust-version = "1.68"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+
+rand = "0.8.4"
+clap = { version = "4.3.24", features = ["cargo"] }
+terminal_size = "0.2.6"
diff --git a/rust/src/main.rs b/rust/src/main.rs
new file mode 100644
index 0000000..3873850
--- /dev/null
+++ b/rust/src/main.rs
@@ -0,0 +1,260 @@
+use clap::{value_parser, Arg, ArgAction, Command};
+use rand::{seq::SliceRandom, Rng};
+use std::env;
+use std::fs::File;
+use std::io::prelude::*;
+use std::io::BufReader;
+use std::path::Path;
+use std::process;
+use terminal_size::{terminal_size, Width};
+
+const MAX: u8 = 255;
+const RANGE_MAX: u8 = 42;
+const RANGE_MIN: u8 = 8;
+const PASS_WORDS: u8 = 8;
+
+const LOWER: &str = "abcdefghijklmnopqrstuvwxyz";
+const UPPER: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+const DIGIT: &str = "0123456789";
+const OTHER: &str = "!$%&#/()=?+-_,.;:<>[]{}|@*";
+const EXTRA: &str = "-_";
+
+const DEF_LENGTH: u8 = 0;
+const DEF_NUMBER: u8 = 10;
+const DEF_WORDLIST: &str = "/usr/share/dict/words";
+
+struct CommonPrintData<'a> {
+ length: u8,
+ alpha: &'a Vec<char>,
+ alnum: &'a Vec<char>,
+ printlen: u8,
+ col_width: u8,
+ col_num: u16,
+}
+
+fn main() {
+ let lower = LOWER.chars().collect::<Vec<_>>();
+ let upper = UPPER.chars().collect::<Vec<_>>();
+ let digit = DIGIT.chars().collect::<Vec<_>>();
+ let other = OTHER.chars().collect::<Vec<_>>();
+ let extra = EXTRA.chars().collect::<Vec<_>>();
+ let alpha = [&lower[..], &upper[..]].concat();
+ let alnum = [&alpha[..], &digit[..]].concat();
+ let every = [&alnum[..], &other[..]].concat();
+
+ let mut normal = [&alnum[..], &extra[..]].concat();
+ let mut special = every;
+
+ let mut length = DEF_LENGTH;
+ let mut number = DEF_NUMBER;
+
+ let mut wordlist = DEF_WORDLIST.to_string();
+
+ //
+ // env
+ // Deal with env before cli-args, because cli-args take precedence.
+ //
+ let env_length = env::var("MAKEPASS_LENGTH").unwrap_or_default();
+ let env_number = env::var("MAKEPASS_NUMBER").unwrap_or_default();
+ let env_wordlist = env::var("MAKEPASS_WORDLIST").unwrap_or_default();
+ let env_normal = env::var("MAKEPASS_NORMAL").unwrap_or_default();
+ let env_special = env::var("MAKEPASS_SPECIAL").unwrap_or_default();
+
+ if !env_length.is_empty() {
+ length = env_length.parse().unwrap_or_else(|_| {
+ eprintln!(
+ "Error: MAKEPASS_LENGTH is not a valid number. Valid numbers are between 0 and 255"
+ );
+ process::exit(1);
+ });
+ }
+ if !env_number.is_empty() {
+ match env_number.parse::<u8>() {
+ Ok(n) if n >= 1 => number = n,
+ _ => {
+ eprintln!("Error: MAKEPASS_NUMBER is not a valid number. Valid numbers are between 1 and 255");
+ process::exit(1);
+ }
+ }
+ }
+ if !env_wordlist.is_empty() {
+ wordlist = env_wordlist;
+ }
+ if !env_normal.is_empty() {
+ normal = env_number.chars().collect::<Vec<_>>();
+ }
+ if !env_special.is_empty() {
+ special = env_special.chars().collect::<Vec<_>>();
+ }
+
+ //
+ // Args
+ //
+
+ let opts = Command::new("makepass")
+ .arg(
+ Arg::new("length_flag")
+ .short('l')
+ .value_name("length")
+ .help(format!("Length of passwords (0..={MAX}). 0 = random."))
+ .value_parser(value_parser!(u8).range(0..)),
+ )
+ .arg(
+ Arg::new("number")
+ .short('n')
+ .help(format!("Number of passwords (1..={MAX}). [default: 10]"))
+ .value_parser(value_parser!(u8).range(1..)),
+ )
+ .arg(
+ Arg::new("printlen")
+ .short('p')
+ .help("Print length")
+ .action(ArgAction::SetTrue),
+ )
+ .arg(
+ Arg::new("length")
+ .help(format!(
+ "Length of passwords (0..={MAX}). 0 = random length."
+ ))
+ .value_parser(value_parser!(u8).range(0..)),
+ )
+ .get_matches();
+
+ match opts.get_one::<u8>("length_flag") {
+ Some(n) => length = *n,
+ _ => (),
+ }
+ match opts.get_one::<u8>("number") {
+ Some(n) => number = *n,
+ _ => (),
+ }
+ match opts.get_one::<u8>("length") {
+ Some(n) => length = *n,
+ _ => (),
+ }
+ let printbool = if opts.get_flag("printlen") {
+ true
+ } else {
+ false
+ };
+
+ //
+ // Other logic
+ //
+ let printlen: u8 = if printbool && length < 100 {
+ 3
+ } else if printbool {
+ 4
+ } else {
+ 0
+ };
+
+ let size = terminal_size();
+ let term_width: u16 = match size {
+ Some((Width(w), _)) => w,
+ None => 1,
+ };
+
+ let col_width: u8 = match length {
+ 0 => RANGE_MAX + 2,
+ _ => length + 2,
+ };
+
+ let col_num: u16 = match term_width / (col_width + printlen) as u16 {
+ 0 => 1,
+ n => n,
+ };
+
+ let data = CommonPrintData {
+ length: length,
+ alpha: &alpha,
+ alnum: &alnum,
+ printlen: printlen,
+ col_width: col_width,
+ col_num: col_num,
+ };
+
+ //
+ // Do the dirty!
+ //
+
+ print_columns("Normal passwords", number, &normal, &data);
+
+ println!("");
+
+ print_columns(
+ "Passwords with special characters",
+ number / 3 * 2 + 1,
+ &special,
+ &data,
+ );
+
+ if Path::new(&wordlist).exists() {
+ println!("");
+ println!("Passphrases:");
+ let words = read_file(&wordlist);
+
+ for _ in 0..number / 2 {
+ println!("{}", passphrase(&words));
+ }
+ }
+}
+
+fn print_columns(title: &str, num: u8, chars: &Vec<char>, data: &CommonPrintData) {
+ let mut strings: Vec<String> = Vec::new();
+ for _ in 0..num {
+ strings.push(randstring(data.length, chars, data.alpha, data.alnum));
+ }
+
+ println!("{}:", title);
+
+ let mut i: u16 = 0;
+ for s in &strings {
+ i = i + 1;
+
+ if data.printlen > 0 {
+ print!("{1:00$} ", (data.printlen - 1) as usize, s.len());
+ }
+ print!("{1:<0$}", data.col_width as usize, s);
+ if (i % data.col_num == 0) || (i == num as u16 && i % data.col_num > 0) {
+ println!("");
+ }
+ }
+}
+
+fn randstring(len: u8, chars: &Vec<char>, alpha: &Vec<char>, alnum: &Vec<char>) -> String {
+ let mut rng = rand::thread_rng();
+ let length = if len == 0 {
+ rng.gen_range(RANGE_MIN..RANGE_MAX)
+ } else {
+ len
+ };
+
+ let mut s = String::with_capacity(length as usize);
+ for i in 0..length {
+ if i == 0 {
+ s.push(*alpha.choose(&mut rng).unwrap());
+ } else if i == length - 1 {
+ s.push(*alnum.choose(&mut rng).unwrap());
+ } else {
+ s.push(*chars.choose(&mut rng).unwrap());
+ }
+ }
+ s
+}
+
+fn read_file(filename: &str) -> Vec<String> {
+ let file = File::open(filename).unwrap();
+ let reader = BufReader::new(file);
+
+ reader.lines().map(|l| l.unwrap()).collect()
+}
+
+fn passphrase(wordlist: &Vec<String>) -> String {
+ let mut rng = rand::thread_rng();
+ (0..PASS_WORDS)
+ .map(|_| wordlist.choose(&mut rng).unwrap())
+ .cloned()
+ .collect::<Vec<_>>()
+ .join("-")
+}