Add choose
function for generating random strings (#2049)
This commit is contained in:
parent
6907847a77
commit
7fa6ed8149
37
Cargo.lock
generated
37
Cargo.lock
generated
@ -537,6 +537,7 @@ dependencies = [
|
||||
"log",
|
||||
"num_cpus",
|
||||
"pretty_assertions",
|
||||
"rand",
|
||||
"regex",
|
||||
"semver",
|
||||
"serde",
|
||||
@ -637,6 +638,12 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.4.0"
|
||||
@ -710,6 +717,36 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[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 = "rayon"
|
||||
version = "1.10.0"
|
||||
|
@ -36,6 +36,7 @@ lexiclean = "0.0.1"
|
||||
libc = "0.2.0"
|
||||
log = "0.4.4"
|
||||
num_cpus = "1.15.0"
|
||||
rand = "0.8.5"
|
||||
regex = "1.10.4"
|
||||
semver = "1.0.20"
|
||||
serde = { version = "1.0.130", features = ["derive", "rc"] }
|
||||
|
@ -1517,6 +1517,13 @@ which will halt execution.
|
||||
|
||||
[BLAKE3]: https://github.com/BLAKE3-team/BLAKE3/
|
||||
|
||||
#### Random
|
||||
|
||||
- `choose(n, alphabet)`<sup>master</sup> - Generate a string of `n` randomly
|
||||
selected characters from `alphabet`, which may not contain repeated
|
||||
characters. For example, `choose('64', HEX)` will generate a random
|
||||
64-character lowercase hex string.
|
||||
|
||||
#### Semantic Versions
|
||||
|
||||
- `semver_matches(version, requirement)`<sup>1.16.0</sup> - Check whether a
|
||||
|
@ -4,7 +4,9 @@ use {
|
||||
ToKebabCase, ToLowerCamelCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase,
|
||||
ToUpperCamelCase,
|
||||
},
|
||||
rand::{seq::SliceRandom, thread_rng},
|
||||
semver::{Version, VersionReq},
|
||||
std::collections::HashSet,
|
||||
Function::*,
|
||||
};
|
||||
|
||||
@ -27,6 +29,7 @@ pub(crate) fn get(name: &str) -> Option<Function> {
|
||||
"cache_directory" => Nullary(|_| dir("cache", dirs::cache_dir)),
|
||||
"canonicalize" => Unary(canonicalize),
|
||||
"capitalize" => Unary(capitalize),
|
||||
"choose" => Binary(choose),
|
||||
"clean" => Unary(clean),
|
||||
"config_directory" => Nullary(|_| dir("config", dirs::config_dir)),
|
||||
"config_local_directory" => Nullary(|_| dir("local config", dirs::config_local_dir)),
|
||||
@ -157,6 +160,30 @@ fn capitalize(_evaluator: &Evaluator, s: &str) -> Result<String, String> {
|
||||
Ok(capitalized)
|
||||
}
|
||||
|
||||
fn choose(_evaluator: &Evaluator, n: &str, alphabet: &str) -> Result<String, String> {
|
||||
if alphabet.is_empty() {
|
||||
return Err("empty alphabet".into());
|
||||
}
|
||||
|
||||
let mut chars = HashSet::<char>::with_capacity(alphabet.len());
|
||||
|
||||
for c in alphabet.chars() {
|
||||
if !chars.insert(c) {
|
||||
return Err(format!("alphabet contains repeated character `{c}`"));
|
||||
}
|
||||
}
|
||||
|
||||
let alphabet = alphabet.chars().collect::<Vec<char>>();
|
||||
|
||||
let n = n
|
||||
.parse::<usize>()
|
||||
.map_err(|err| format!("failed to parse `{n}` as positive integer: {err}"))?;
|
||||
|
||||
let mut rng = thread_rng();
|
||||
|
||||
Ok((0..n).map(|_| alphabet.choose(&mut rng).unwrap()).collect())
|
||||
}
|
||||
|
||||
fn clean(_evaluator: &Evaluator, path: &str) -> Result<String, String> {
|
||||
Ok(Path::new(path).lexiclean().to_str().unwrap().to_owned())
|
||||
}
|
||||
|
@ -661,6 +661,69 @@ fn uuid() {
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn choose() {
|
||||
Test::new()
|
||||
.justfile(r#"x := choose('10', 'xXyYzZ')"#)
|
||||
.args(["--evaluate", "x"])
|
||||
.stdout_regex("^[X-Zx-z]{10}$")
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn choose_bad_alphabet_empty() {
|
||||
Test::new()
|
||||
.justfile("x := choose('10', '')")
|
||||
.args(["--evaluate"])
|
||||
.status(1)
|
||||
.stderr(
|
||||
"
|
||||
error: Call to function `choose` failed: empty alphabet
|
||||
——▶ justfile:1:6
|
||||
│
|
||||
1 │ x := choose('10', '')
|
||||
│ ^^^^^^
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn choose_bad_alphabet_repeated() {
|
||||
Test::new()
|
||||
.justfile("x := choose('10', 'aa')")
|
||||
.args(["--evaluate"])
|
||||
.status(1)
|
||||
.stderr(
|
||||
"
|
||||
error: Call to function `choose` failed: alphabet contains repeated character `a`
|
||||
——▶ justfile:1:6
|
||||
│
|
||||
1 │ x := choose('10', 'aa')
|
||||
│ ^^^^^^
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn choose_bad_length() {
|
||||
Test::new()
|
||||
.justfile("x := choose('foo', HEX)")
|
||||
.args(["--evaluate"])
|
||||
.status(1)
|
||||
.stderr(
|
||||
"
|
||||
error: Call to function `choose` failed: failed to parse `foo` as positive integer: invalid digit found in string
|
||||
——▶ justfile:1:6
|
||||
│
|
||||
1 │ x := choose('foo', HEX)
|
||||
│ ^^^^^^
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sha256() {
|
||||
Test::new()
|
||||
|
Loading…
Reference in New Issue
Block a user