Replace SecretRegistry::new with From impls
Assisted-by: GLM-5.1 via pi
diff --git a/src/ci/redact.rs b/src/ci/redact.rs
index f625bfc..132d3cf 100644
--- a/src/ci/redact.rs
+++ b/src/ci/redact.rs
@@ -59,14 +59,32 @@ impl std::fmt::Debug for SecretRegistry {
}
}
-impl SecretRegistry {
- pub fn new(declared: HashMap<String, SecretString>) -> Self {
+impl From<HashMap<String, SecretString>> for SecretRegistry {
+ fn from(declared: HashMap<String, SecretString>) -> Self {
Self {
declared,
revealed: HashMap::new(),
}
}
+}
+
+impl From<Vec<(String, SecretString)>> for SecretRegistry {
+ fn from(pairs: Vec<(String, SecretString)>) -> Self {
+ Self::from(pairs.into_iter().collect::<HashMap<_, _>>())
+ }
+}
+
+impl From<Vec<(&str, &str)>> for SecretRegistry {
+ fn from(pairs: Vec<(&str, &str)>) -> Self {
+ let declared: HashMap<String, SecretString> = pairs
+ .into_iter()
+ .map(|(k, v)| (k.to_string(), SecretString::from(v)))
+ .collect();
+ Self::from(declared)
+ }
+}
+impl SecretRegistry {
/// Resolve a declared secret by name, caching the revealed value
/// for redaction. Returns `Err` if the name isn't declared or
/// the source can't be read.
@@ -135,16 +153,9 @@ pub fn redact(text: &str, registry: &SecretRegistry) -> String {
mod tests {
use super::*;
- fn plain_secrets(pairs: &[(&str, &str)]) -> HashMap<String, SecretString> {
- pairs
- .iter()
- .map(|(k, v)| (k.to_string(), SecretString::from(*v)))
- .collect()
- }
-
#[test]
fn redact_replaces_secret_value() {
- let mut reg = SecretRegistry::new(plain_secrets(&[("github_token", "ghp_abc123xyz")]));
+ let mut reg = SecretRegistry::from(vec![("github_token", "ghp_abc123xyz")]);
reg.resolve("github_token").unwrap();
assert_eq!(
redact("push with token ghp_abc123xyz failed", ®),
@@ -154,10 +165,7 @@ mod tests {
#[test]
fn redact_handles_multiple_secrets() {
- let mut reg = SecretRegistry::new(plain_secrets(&[
- ("token_a", "aaaaaaaa"),
- ("token_b", "bbbbbbbb"),
- ]));
+ let mut reg = SecretRegistry::from(vec![("token_a", "aaaaaaaa"), ("token_b", "bbbbbbbb")]);
reg.resolve("token_a").unwrap();
reg.resolve("token_b").unwrap();
let result = redact("aaaaaaaa and bbbbbbbb", ®);
@@ -166,10 +174,8 @@ mod tests {
#[test]
fn redact_longest_first_prevents_partial_overlap() {
- let mut reg = SecretRegistry::new(plain_secrets(&[
- ("short", "abcdefgh"),
- ("long", "abcdefghijklmnop"),
- ]));
+ let mut reg =
+ SecretRegistry::from(vec![("short", "abcdefgh"), ("long", "abcdefghijklmnop")]);
reg.resolve("short").unwrap();
reg.resolve("long").unwrap();
assert_eq!(redact("abcdefghijklmnop here", ®), "{{ long }} here");
@@ -177,20 +183,20 @@ mod tests {
#[test]
fn redact_returns_unchanged_when_nothing_revealed() {
- let reg = SecretRegistry::new(plain_secrets(&[("key", "secret_value")]));
+ let reg = SecretRegistry::from(vec![("key", "secret_value")]);
assert_eq!(redact("nothing to see", ®), "nothing to see");
}
#[test]
fn redact_ignores_short_secrets() {
- let mut reg = SecretRegistry::new(plain_secrets(&[("tiny", "abcdefg")]));
+ let mut reg = SecretRegistry::from(vec![("tiny", "abcdefg")]);
reg.resolve("tiny").unwrap();
assert_eq!(redact("abcdefg is short", ®), "abcdefg is short");
}
#[test]
fn redact_similar_but_not_equal_passes_through() {
- let mut reg = SecretRegistry::new(plain_secrets(&[("token", "ghp_abc123xyz")]));
+ let mut reg = SecretRegistry::from(vec![("token", "ghp_abc123xyz")]);
reg.resolve("token").unwrap();
assert_eq!(
redact("ghp_abc124xyz is close but not equal", ®),
@@ -200,7 +206,7 @@ mod tests {
#[test]
fn redact_replaces_all_occurrences() {
- let mut reg = SecretRegistry::new(plain_secrets(&[("key", "secret_password")]));
+ let mut reg = SecretRegistry::from(vec![("key", "secret_password")]);
reg.resolve("key").unwrap();
assert_eq!(
redact("secret_password secret_password secret_password", ®),
@@ -210,8 +216,7 @@ mod tests {
#[test]
fn minimum_length_is_8() {
- let mut reg =
- SecretRegistry::new(plain_secrets(&[("short", "1234567"), ("ok", "12345678")]));
+ let mut reg = SecretRegistry::from(vec![("short", "1234567"), ("ok", "12345678")]);
// 7 chars — too short for redaction
reg.resolve("short").unwrap();
assert_eq!(redact("1234567", ®), "1234567");
@@ -223,7 +228,7 @@ mod tests {
#[test]
fn resolve_errors_for_unknown_name() {
- let mut reg = SecretRegistry::new(plain_secrets(&[]));
+ let mut reg = SecretRegistry::from(HashMap::new());
let err = reg.resolve("missing").unwrap_err();
assert!(
matches!(err, super::super::error::Error::UnknownSecret(ref n) if n == "missing"),
@@ -233,13 +238,13 @@ mod tests {
#[test]
fn resolve_returns_value() {
- let mut reg = SecretRegistry::new(plain_secrets(&[("key", "hunter2")]));
+ let mut reg = SecretRegistry::from(vec![("key", "hunter2")]);
assert_eq!(reg.resolve("key").unwrap(), "hunter2");
}
#[test]
fn redact_is_idempotent() {
- let mut reg = SecretRegistry::new(plain_secrets(&[("token", "ghp_long_secret_value")]));
+ let mut reg = SecretRegistry::from(vec![("token", "ghp_long_secret_value")]);
reg.resolve("token").unwrap();
let input = "hello ghp_long_secret_value world";
let first = redact(input, ®);
@@ -249,7 +254,7 @@ mod tests {
#[test]
fn redact_preserves_non_matching_text() {
- let mut reg = SecretRegistry::new(plain_secrets(&[("token", "ghp_long_secret_value")]));
+ let mut reg = SecretRegistry::from(vec![("token", "ghp_long_secret_value")]);
reg.resolve("token").unwrap();
let input = "nothing to see here";
assert_eq!(redact(input, ®), input);
@@ -257,7 +262,7 @@ mod tests {
#[test]
fn redact_with_no_resolves_is_identity() {
- let reg = SecretRegistry::new(plain_secrets(&[("token", "ghp_long_secret_value")]));
+ let reg = SecretRegistry::from(vec![("token", "ghp_long_secret_value")]);
// No resolve — no redactions registered.
let input = "contains ghp_long_secret_value but not resolved";
assert_eq!(redact(input, ®), input);
@@ -266,10 +271,8 @@ mod tests {
#[test]
fn redact_tiebreaks_equal_length_by_name() {
// Two names with the same revealed value: alphabetical name wins.
- let mut reg = SecretRegistry::new(plain_secrets(&[
- ("zzz_late", "samevalue"),
- ("aaa_early", "samevalue"),
- ]));
+ let mut reg =
+ SecretRegistry::from(vec![("zzz_late", "samevalue"), ("aaa_early", "samevalue")]);
reg.resolve("zzz_late").unwrap();
reg.resolve("aaa_early").unwrap();
assert_eq!(redact("samevalue", ®), "{{ aaa_early }}");
diff --git a/src/ci/runtime.rs b/src/ci/runtime.rs
index 60f759f..8685986 100644
--- a/src/ci/runtime.rs
+++ b/src/ci/runtime.rs
@@ -128,7 +128,7 @@ impl Runtime {
Self {
pipeline,
inputs,
- registry: RefCell::new(SecretRegistry::new(secrets)),
+ registry: RefCell::new(SecretRegistry::from(secrets)),
current_job: RefCell::new(None),
outputs: RefCell::new(HashMap::new()),
sh_timings: RefCell::new(HashMap::new()),
@@ -280,7 +280,7 @@ impl Runtime {
fn for_test(pipeline: Pipeline, secrets: HashMap<String, SecretString>) -> Self {
Self {
pipeline,
- registry: RefCell::new(SecretRegistry::new(secrets)),
+ registry: RefCell::new(SecretRegistry::from(secrets)),
inputs: HashMap::new(),
current_job: RefCell::new(None),
outputs: RefCell::new(HashMap::new()),
diff --git a/tests/property.rs b/tests/property.rs
index 3070cae..d16df78 100644
--- a/tests/property.rs
+++ b/tests/property.rs
@@ -121,13 +121,6 @@ fn push_event_updated_refs_is_subtractive(tc: TestCase) {
// ── Secret registry helpers ──────────────────────────────────────
-fn plain_secrets(pairs: &[(&str, &str)]) -> HashMap<String, SecretString> {
- pairs
- .iter()
- .map(|(k, v)| (k.to_string(), SecretString::from(*v)))
- .collect()
-}
-
/// Generate a secret name from an index.
fn secret_name(i: usize) -> String {
format!("secret_{i}")
@@ -155,7 +148,7 @@ fn resolved_registry(tc: TestCase) -> SecretRegistry {
for (name, value) in &entries {
map.insert(name.clone(), SecretString::from(value.clone()));
}
- let mut reg = SecretRegistry::new(map);
+ let mut reg = SecretRegistry::from(map);
// Resolve all secrets so they're registered for redaction.
for (name, _) in &entries {
let _ = reg.resolve(name);
@@ -174,7 +167,7 @@ fn text_with_secrets(tc: TestCase) -> (SecretRegistry, String) {
long_values.push(value.clone());
}
}
- let mut reg = SecretRegistry::new(map);
+ let mut reg = SecretRegistry::from(map);
for (name, _) in &entries {
let _ = reg.resolve(name);
}
@@ -203,7 +196,7 @@ fn redact_never_contains_revealed_long_values(tc: TestCase) {
long_values.push(value.clone());
}
}
- let mut reg = SecretRegistry::new(map);
+ let mut reg = SecretRegistry::from(map);
for (name, _) in &entries {
let _ = reg.resolve(name);
}
@@ -246,7 +239,7 @@ fn redact_preserves_text_without_secrets(tc: TestCase) {
#[hegel::test]
fn redact_empty_registry_is_identity(tc: TestCase) {
- let reg = SecretRegistry::new(HashMap::new());
+ let reg = SecretRegistry::from(HashMap::new());
let text = tc.draw(text());
assert_eq!(redact(&text, ®), text);
}
@@ -258,7 +251,7 @@ fn redact_unresolved_registry_is_identity(tc: TestCase) {
.into_iter()
.map(|(k, v)| (k, SecretString::from(v)))
.collect();
- let reg = SecretRegistry::new(map);
+ let reg = SecretRegistry::from(map);
let text = tc.draw(text());
assert_eq!(redact(&text, ®), text);
}
@@ -267,7 +260,7 @@ fn redact_unresolved_registry_is_identity(tc: TestCase) {
fn resolve_returns_consistent_value(tc: TestCase) {
let entries = tc.draw(unique_secret_entries());
let (name, value) = entries.into_iter().next().unwrap();
- let mut reg = SecretRegistry::new(plain_secrets(&[(&name, &value)]));
+ let mut reg = SecretRegistry::from(vec![(name.as_str(), value.as_str())]);
let first = reg.resolve(&name).unwrap();
let second = reg.resolve(&name).unwrap();
assert_eq!(first, second);
@@ -277,7 +270,7 @@ fn resolve_returns_consistent_value(tc: TestCase) {
#[hegel::test]
fn resolve_unknown_name_errors(tc: TestCase) {
let name = tc.draw(text());
- let mut reg = SecretRegistry::new(HashMap::new());
+ let mut reg = SecretRegistry::from(HashMap::new());
assert!(reg.resolve(&name).is_err());
}
@@ -288,7 +281,7 @@ fn redact_output_never_shows_long_secret_values(tc: TestCase) {
for (name, value) in &entries {
map.insert(name.clone(), SecretString::from(value.clone()));
}
- let mut reg = SecretRegistry::new(map);
+ let mut reg = SecretRegistry::from(map);
for (name, _) in &entries {
let _ = reg.resolve(name);
}
@@ -316,7 +309,7 @@ fn short_secrets_are_never_redacted(tc: TestCase) {
if value.is_empty() {
return;
}
- let mut reg = SecretRegistry::new(plain_secrets(&[("short", &value)]));
+ let mut reg = SecretRegistry::from(vec![("short", value.as_str())]);
let _ = reg.resolve("short");
let body = value.clone();
let result = redact(&body, ®);