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",
|
"log",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
@ -637,6 +638,12 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pretty_assertions"
|
name = "pretty_assertions"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@ -710,6 +717,36 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"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]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
|
@ -36,6 +36,7 @@ lexiclean = "0.0.1"
|
|||||||
libc = "0.2.0"
|
libc = "0.2.0"
|
||||||
log = "0.4.4"
|
log = "0.4.4"
|
||||||
num_cpus = "1.15.0"
|
num_cpus = "1.15.0"
|
||||||
|
rand = "0.8.5"
|
||||||
regex = "1.10.4"
|
regex = "1.10.4"
|
||||||
semver = "1.0.20"
|
semver = "1.0.20"
|
||||||
serde = { version = "1.0.130", features = ["derive", "rc"] }
|
serde = { version = "1.0.130", features = ["derive", "rc"] }
|
||||||
|
@ -1517,6 +1517,13 @@ which will halt execution.
|
|||||||
|
|
||||||
[BLAKE3]: https://github.com/BLAKE3-team/BLAKE3/
|
[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
|
#### Semantic Versions
|
||||||
|
|
||||||
- `semver_matches(version, requirement)`<sup>1.16.0</sup> - Check whether a
|
- `semver_matches(version, requirement)`<sup>1.16.0</sup> - Check whether a
|
||||||
|
@ -4,7 +4,9 @@ use {
|
|||||||
ToKebabCase, ToLowerCamelCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase,
|
ToKebabCase, ToLowerCamelCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase,
|
||||||
ToUpperCamelCase,
|
ToUpperCamelCase,
|
||||||
},
|
},
|
||||||
|
rand::{seq::SliceRandom, thread_rng},
|
||||||
semver::{Version, VersionReq},
|
semver::{Version, VersionReq},
|
||||||
|
std::collections::HashSet,
|
||||||
Function::*,
|
Function::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -27,6 +29,7 @@ pub(crate) fn get(name: &str) -> Option<Function> {
|
|||||||
"cache_directory" => Nullary(|_| dir("cache", dirs::cache_dir)),
|
"cache_directory" => Nullary(|_| dir("cache", dirs::cache_dir)),
|
||||||
"canonicalize" => Unary(canonicalize),
|
"canonicalize" => Unary(canonicalize),
|
||||||
"capitalize" => Unary(capitalize),
|
"capitalize" => Unary(capitalize),
|
||||||
|
"choose" => Binary(choose),
|
||||||
"clean" => Unary(clean),
|
"clean" => Unary(clean),
|
||||||
"config_directory" => Nullary(|_| dir("config", dirs::config_dir)),
|
"config_directory" => Nullary(|_| dir("config", dirs::config_dir)),
|
||||||
"config_local_directory" => Nullary(|_| dir("local config", dirs::config_local_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)
|
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> {
|
fn clean(_evaluator: &Evaluator, path: &str) -> Result<String, String> {
|
||||||
Ok(Path::new(path).lexiclean().to_str().unwrap().to_owned())
|
Ok(Path::new(path).lexiclean().to_str().unwrap().to_owned())
|
||||||
}
|
}
|
||||||
|
@ -661,6 +661,69 @@ fn uuid() {
|
|||||||
.run();
|
.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]
|
#[test]
|
||||||
fn sha256() {
|
fn sha256() {
|
||||||
Test::new()
|
Test::new()
|
||||||
|
Loading…
Reference in New Issue
Block a user