Added utility library for parsing workspace status stamps. (#2982)
This change introduces a small utility for parsing workspace status
stamp files. I found similar snippets throughout projects I work on and
thought consolidation within `rules_rust` would be good. An example
usage can be seen here:
```rust
use workspace_status::parse_workspace_status_stamps;
fn main() {
let stable_status = std::fs::read_to_string("bazel-out/stable-status.txt").unwrap();
let volatile_status = std::fs::read_to_string("bazel-out/volatile-status.txt").unwrap();
let stamps = parse_workspace_status_stamps(&stable_status)
.chain(parse_workspace_status_stamps(&stable_status))
.flatten()
.collect::<BTreeMap<_, _>>();
// ...
// ...
// ...
}
```
diff --git a/tools/workspace_status/BUILD.bazel b/tools/workspace_status/BUILD.bazel
new file mode 100644
index 0000000..c207cf9
--- /dev/null
+++ b/tools/workspace_status/BUILD.bazel
@@ -0,0 +1,13 @@
+load("//rust:defs.bzl", "rust_library", "rust_test")
+
+rust_library(
+ name = "workspace_status",
+ srcs = ["workspace_status.rs"],
+ edition = "2021",
+ visibility = ["//visibility:public"],
+)
+
+rust_test(
+ name = "workspace_status_test",
+ crate = ":workspace_status",
+)
diff --git a/tools/workspace_status/workspace_status.rs b/tools/workspace_status/workspace_status.rs
new file mode 100644
index 0000000..8f05879
--- /dev/null
+++ b/tools/workspace_status/workspace_status.rs
@@ -0,0 +1,121 @@
+//! Utilities for parsing [workspace status stamps](https://bazel.build/docs/user-manual#workspace-status).
+
+/// The error type of workspace status parsing.
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum WorkspaceStatusError {
+ /// The workspace status data is malformed and cannot be parsed.
+ InvalidFormat(String),
+}
+
+/// Returns an iterator of workspace status stamp values parsed from the given text.
+pub fn parse_workspace_status_stamps(
+ text: &'_ str,
+) -> impl Iterator<Item = Result<(&'_ str, &'_ str), WorkspaceStatusError>> {
+ text.lines().map(|l| {
+ let pair = l.split_once(' ');
+ pair.ok_or_else(|| {
+ WorkspaceStatusError::InvalidFormat(format!(
+ "Invalid workspace status stamp value:\n{}",
+ l
+ ))
+ })
+ })
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ use std::collections::{BTreeMap, HashMap};
+
+ #[test]
+ fn test_parse_into_btree_map() {
+ let status = [
+ "BUILD_TIMESTAMP 1730574875",
+ "BUILD_USER user name",
+ "STABLE_STAMP_VALUE stable",
+ ]
+ .join("\n");
+
+ let stamps = parse_workspace_status_stamps(&status)
+ .flatten()
+ .collect::<BTreeMap<_, _>>();
+ assert_eq!(
+ BTreeMap::from([
+ ("BUILD_TIMESTAMP", "1730574875"),
+ ("BUILD_USER", "user name"),
+ ("STABLE_STAMP_VALUE", "stable"),
+ ]),
+ stamps
+ );
+ }
+
+ #[test]
+ fn test_parse_into_hash_map() {
+ let status = [
+ "BUILD_TIMESTAMP 1730574875",
+ "BUILD_USER user name",
+ "STABLE_STAMP_VALUE stable",
+ ]
+ .join("\n");
+
+ let stamps = parse_workspace_status_stamps(&status)
+ .flatten()
+ .collect::<HashMap<_, _>>();
+ assert_eq!(
+ HashMap::from([
+ ("BUILD_TIMESTAMP", "1730574875"),
+ ("BUILD_USER", "user name"),
+ ("STABLE_STAMP_VALUE", "stable"),
+ ]),
+ stamps
+ );
+ }
+
+ #[test]
+ fn test_chain() {
+ let stable_status =
+ ["STABLE_STAMP_VALUE1 stable1", "STABLE_STAMP_VALUE2 stable2"].join("\n");
+
+ let volatile_status = [
+ "VOLATILE_STAMP_VALUE1 volatile1",
+ "VOLATILE_STAMP_VALUE2 volatile2",
+ ]
+ .join("\n");
+
+ let stamps = parse_workspace_status_stamps(&stable_status)
+ .chain(parse_workspace_status_stamps(&volatile_status))
+ .flatten()
+ .collect::<BTreeMap<_, _>>();
+
+ assert_eq!(
+ BTreeMap::from([
+ ("STABLE_STAMP_VALUE1", "stable1"),
+ ("STABLE_STAMP_VALUE2", "stable2"),
+ ("VOLATILE_STAMP_VALUE1", "volatile1"),
+ ("VOLATILE_STAMP_VALUE2", "volatile2"),
+ ]),
+ stamps
+ );
+ }
+
+ #[test]
+ fn test_parse_invalid_stamps() {
+ let status = [
+ "BUILD_TIMESTAMP 1730574875",
+ "BUILD_USERusername",
+ "STABLE_STAMP_VALUE stable",
+ ]
+ .join("\n");
+
+ let error = parse_workspace_status_stamps(&status)
+ .find_map(|result| result.err())
+ .expect("No error found when one was expected");
+ assert_eq!(
+ WorkspaceStatusError::InvalidFormat(
+ "Invalid workspace status stamp value:\nBUILD_USERusername".to_owned()
+ ),
+ error
+ );
+ }
+}