1
1
mirror of https://github.com/casey/just.git synced 2024-11-26 00:24:22 +03:00

SHA-256 and UUID functions (#1170)

This commit is contained in:
mbodmer 2022-05-05 01:18:31 +02:00 committed by GitHub
parent b63efb70a5
commit 862d6a52ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 183 additions and 3 deletions

93
Cargo.lock generated
View File

@ -37,6 +37,15 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block-buffer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "bstr" name = "bstr"
version = "0.2.17" version = "0.2.17"
@ -76,6 +85,15 @@ dependencies = [
"vec_map", "vec_map",
] ]
[[package]]
name = "cpufeatures"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "cradle" name = "cradle"
version = "0.2.2" version = "0.2.2"
@ -85,6 +103,16 @@ dependencies = [
"rustversion", "rustversion",
] ]
[[package]]
name = "crypto-common"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
dependencies = [
"generic-array",
"typenum",
]
[[package]] [[package]]
name = "ctor" name = "ctor"
version = "0.1.22" version = "0.1.22"
@ -122,6 +150,16 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
[[package]]
name = "digest"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]] [[package]]
name = "doc-comment" name = "doc-comment"
version = "0.3.3" version = "0.3.3"
@ -182,6 +220,16 @@ dependencies = [
"pulldown-cmark-to-cmark", "pulldown-cmark-to-cmark",
] ]
[[package]]
name = "generic-array"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
dependencies = [
"typenum",
"version_check",
]
[[package]] [[package]]
name = "getopts" name = "getopts"
version = "0.2.21" version = "0.2.21"
@ -191,6 +239,17 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "getrandom"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.3.3" version = "0.3.3"
@ -259,6 +318,7 @@ dependencies = [
"regex", "regex",
"serde", "serde",
"serde_json", "serde_json",
"sha2",
"similar", "similar",
"snafu", "snafu",
"strum", "strum",
@ -267,6 +327,7 @@ dependencies = [
"temptree", "temptree",
"typed-arena", "typed-arena",
"unicode-width", "unicode-width",
"uuid",
"which", "which",
"yaml-rust", "yaml-rust",
] ]
@ -498,6 +559,17 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "sha2"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]] [[package]]
name = "similar" name = "similar"
version = "2.1.0" version = "2.1.0"
@ -657,6 +729,12 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae"
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "2.6.0" version = "2.6.0"
@ -684,6 +762,15 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
[[package]]
name = "uuid"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "vec_map" name = "vec_map"
version = "0.8.2" version = "0.8.2"
@ -696,6 +783,12 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]] [[package]]
name = "which" name = "which"
version = "4.2.5" version = "4.2.5"

View File

@ -32,6 +32,7 @@ log = "0.4.4"
regex = "1.5.4" regex = "1.5.4"
serde = { version = "1.0.130", features = ["derive", "rc"] } serde = { version = "1.0.130", features = ["derive", "rc"] }
serde_json = "1.0.68" serde_json = "1.0.68"
sha2 = "0.10"
similar = { version = "2.1.0", features = ["unicode"] } similar = { version = "2.1.0", features = ["unicode"] }
snafu = "0.7.0" snafu = "0.7.0"
strum = { version = "0.24.0", features = ["derive"] } strum = { version = "0.24.0", features = ["derive"] }
@ -39,6 +40,7 @@ target = "2.0.0"
tempfile = "3.0.0" tempfile = "3.0.0"
typed-arena = "2.0.1" typed-arena = "2.0.1"
unicode-width = "0.1.0" unicode-width = "0.1.0"
uuid = { version = "1.0.0", features = ["v4"] }
[dev-dependencies] [dev-dependencies]
cradle = "0.2.0" cradle = "0.2.0"

View File

@ -1030,6 +1030,12 @@ These functions can fail, for example if a path does not have an extension, whic
- `error(message)` - Abort execution and report error `message` to user. - `error(message)` - Abort execution and report error `message` to user.
#### UUID and Hash Generation
- `sha256(string)` - Return the SHA-256 hash of `string` as a hexadecimal string.
- `sha256_file(path)` - Return the SHA-256 hash of the file at `path` as a hexadecimal string.
- `uuid()` - Return a randomly generated UUID.
### Command Evaluation Using Backticks ### Command Evaluation Using Backticks
Backticks can be used to store the result of commands: Backticks can be used to store the result of commands:

View File

@ -35,6 +35,8 @@ lazy_static! {
("path_exists", Unary(path_exists)), ("path_exists", Unary(path_exists)),
("quote", Unary(quote)), ("quote", Unary(quote)),
("replace", Ternary(replace)), ("replace", Ternary(replace)),
("sha256", Unary(sha256)),
("sha256_file", Unary(sha256_file)),
("trim", Unary(trim)), ("trim", Unary(trim)),
("trim_end", Unary(trim_end)), ("trim_end", Unary(trim_end)),
("trim_end_match", Binary(trim_end_match)), ("trim_end_match", Binary(trim_end_match)),
@ -43,6 +45,7 @@ lazy_static! {
("trim_start_match", Binary(trim_start_match)), ("trim_start_match", Binary(trim_start_match)),
("trim_start_matches", Binary(trim_start_matches)), ("trim_start_matches", Binary(trim_start_matches)),
("uppercase", Unary(uppercase)), ("uppercase", Unary(uppercase)),
("uuid", Nullary(uuid)),
("without_extension", Unary(without_extension)), ("without_extension", Unary(without_extension)),
] ]
.into_iter() .into_iter()
@ -247,6 +250,26 @@ fn replace(_context: &FunctionContext, s: &str, from: &str, to: &str) -> Result<
Ok(s.replace(from, to)) Ok(s.replace(from, to))
} }
fn sha256(_context: &FunctionContext, s: &str) -> Result<String, String> {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(s);
let hash = hasher.finalize();
Ok(format!("{:x}", hash))
}
fn sha256_file(context: &FunctionContext, path: &str) -> Result<String, String> {
use sha2::{Digest, Sha256};
let justpath = context.search.working_directory.join(path);
let mut hasher = Sha256::new();
let mut file = std::fs::File::open(&justpath)
.map_err(|err| format!("Failed to open file at `{:?}`: {}", &justpath.to_str(), err))?;
std::io::copy(&mut file, &mut hasher)
.map_err(|err| format!("Failed to read file at `{:?}`: {}", &justpath.to_str(), err))?;
let hash = hasher.finalize();
Ok(format!("{:x}", hash))
}
fn trim(_context: &FunctionContext, s: &str) -> Result<String, String> { fn trim(_context: &FunctionContext, s: &str) -> Result<String, String> {
Ok(s.trim().to_owned()) Ok(s.trim().to_owned())
} }
@ -279,6 +302,10 @@ fn uppercase(_context: &FunctionContext, s: &str) -> Result<String, String> {
Ok(s.to_uppercase()) Ok(s.to_uppercase())
} }
fn uuid(_context: &FunctionContext) -> Result<String, String> {
Ok(uuid::Uuid::new_v4().to_string())
}
fn without_extension(_context: &FunctionContext, path: &str) -> Result<String, String> { fn without_extension(_context: &FunctionContext, path: &str) -> Result<String, String> {
let parent = Utf8Path::new(path) let parent = Utf8Path::new(path)
.parent() .parent()

View File

@ -476,3 +476,36 @@ fn path_exists_subdir() {
.stdout("true") .stdout("true")
.run(); .run();
} }
#[test]
fn uuid() {
Test::new()
.justfile("x := uuid()")
.args(&["--evaluate", "x"])
.stdout_regex("........-....-....-....-............")
.run();
}
#[test]
fn sha256() {
Test::new()
.justfile("x := sha256('5943ee37-0000-1000-8000-010203040506')")
.args(&["--evaluate", "x"])
.stdout("2330d7f5eb94a820b54fed59a8eced236f80b633a504289c030b6a65aef58871")
.run();
}
#[test]
fn sha256_file() {
Test::new()
.justfile("x := sha256_file('sub/shafile')")
.tree(tree! {
sub: {
shafile: "just is great\n",
}
})
.current_dir("sub")
.args(&["--evaluate", "x"])
.stdout("177b3d79aaafb53a7a4d7aaba99a82f27c73370e8cb0295571aade1e4fea1cd2")
.run();
}

View File

@ -10,6 +10,7 @@ macro_rules! test {
$(env: { $($env_key:literal : $env_value:literal,)* },)? $(env: { $($env_key:literal : $env_value:literal,)* },)?
$(stdin: $stdin:expr,)? $(stdin: $stdin:expr,)?
$(stdout: $stdout:expr,)? $(stdout: $stdout:expr,)?
$(stdout_regex: $stdout_regex:expr,)?
$(stderr: $stderr:expr,)? $(stderr: $stderr:expr,)?
$(stderr_regex: $stderr_regex:expr,)? $(stderr_regex: $stderr_regex:expr,)?
$(status: $status:expr,)? $(status: $status:expr,)?
@ -28,6 +29,7 @@ macro_rules! test {
$(let test = test.stderr_regex($stderr_regex);)? $(let test = test.stderr_regex($stderr_regex);)?
$(let test = test.stdin($stdin);)? $(let test = test.stdin($stdin);)?
$(let test = test.stdout($stdout);)? $(let test = test.stdout($stdout);)?
$(let test = test.stdout_regex($stdout_regex);)?
test.run(); test.run();
} }
@ -45,6 +47,7 @@ pub(crate) struct Test {
pub(crate) stderr_regex: Option<Regex>, pub(crate) stderr_regex: Option<Regex>,
pub(crate) stdin: String, pub(crate) stdin: String,
pub(crate) stdout: String, pub(crate) stdout: String,
pub(crate) stdout_regex: Option<Regex>,
pub(crate) tempdir: TempDir, pub(crate) tempdir: TempDir,
pub(crate) unindent_stdout: bool, pub(crate) unindent_stdout: bool,
} }
@ -66,6 +69,7 @@ impl Test {
stderr_regex: None, stderr_regex: None,
stdin: String::new(), stdin: String::new(),
stdout: String::new(), stdout: String::new(),
stdout_regex: None,
tempdir, tempdir,
unindent_stdout: true, unindent_stdout: true,
} }
@ -137,6 +141,11 @@ impl Test {
self self
} }
pub(crate) fn stdout_regex(mut self, stdout_regex: impl AsRef<str>) -> Self {
self.stdout_regex = Some(Regex::new(&format!("(?m)^{}$", stdout_regex.as_ref())).unwrap());
self
}
pub(crate) fn tree(self, mut tree: Tree) -> Self { pub(crate) fn tree(self, mut tree: Tree) -> Self {
tree.map(|_name, content| unindent(content)); tree.map(|_name, content| unindent(content));
tree.instantiate(self.tempdir.path()).unwrap(); tree.instantiate(self.tempdir.path()).unwrap();
@ -203,8 +212,18 @@ impl Test {
equal equal
} }
let output_stdout = str::from_utf8(&output.stdout).unwrap();
let output_stderr = str::from_utf8(&output.stderr).unwrap(); let output_stderr = str::from_utf8(&output.stderr).unwrap();
if let Some(ref stdout_regex) = self.stdout_regex {
if !stdout_regex.is_match(output_stdout) {
panic!(
"Stdout regex mismatch:\n{:?}\n!~=\n/{:?}/",
output_stderr, stdout_regex
);
}
}
if let Some(ref stderr_regex) = self.stderr_regex { if let Some(ref stderr_regex) = self.stderr_regex {
if !stderr_regex.is_match(output_stderr) { if !stderr_regex.is_match(output_stderr) {
panic!( panic!(
@ -215,7 +234,7 @@ impl Test {
} }
if !compare("status", output.status.code().unwrap(), self.status) if !compare("status", output.status.code().unwrap(), self.status)
| !compare("stdout", str::from_utf8(&output.stdout).unwrap(), &stdout) | (self.stdout_regex.is_none() && !compare("stdout", output_stdout, &stdout))
| (self.stderr_regex.is_none() && !compare("stderr", output_stderr, &stderr)) | (self.stderr_regex.is_none() && !compare("stderr", output_stderr, &stderr))
{ {
panic!("Output mismatch."); panic!("Output mismatch.");