Replace SecretString::from_plain/from_file with From impls
Assisted-by: GLM-5.1 via pi
change znnyoxryuxxsyxwlvwrqvwyumqzuuyun
commit d7c9b5b479c4220da0d36ec58caa856fed1cd8c1
author Alpha Chen <alpha@kejadlen.dev>
date
parent umkookyn
diff --git a/src/ci/mirror.rs b/src/ci/mirror.rs
index 0175796..1af69f2 100644
--- a/src/ci/mirror.rs
+++ b/src/ci/mirror.rs
@@ -481,10 +481,7 @@ mod tests {
         };
 
         let mut secrets = HashMap::new();
-        secrets.insert(
-            "github_token".to_string(),
-            SecretString::from_plain("fake_token"),
-        );
+        secrets.insert("github_token".to_string(), SecretString::from("fake_token"));
 
         let source = r#"(local ci (require :quire.ci))
 (ci.mirror "https://github.com/example/repo.git"
@@ -531,10 +528,7 @@ mod tests {
         };
 
         let mut secrets = HashMap::new();
-        secrets.insert(
-            "github_token".to_string(),
-            SecretString::from_plain("fake_token"),
-        );
+        secrets.insert("github_token".to_string(), SecretString::from("fake_token"));
 
         // :refs is set and the trigger ref matches, so the mirror
         // should push the listed refs verbatim.
@@ -571,10 +565,7 @@ mod tests {
         };
 
         let mut secrets = HashMap::new();
-        secrets.insert(
-            "github_token".to_string(),
-            SecretString::from_plain("fake_token"),
-        );
+        secrets.insert("github_token".to_string(), SecretString::from("fake_token"));
 
         // Trigger ref is feature, but :refs only lists main — mirror
         // should be a no-op.
diff --git a/src/ci/redact.rs b/src/ci/redact.rs
index 54ca4be..f625bfc 100644
--- a/src/ci/redact.rs
+++ b/src/ci/redact.rs
@@ -138,7 +138,7 @@ mod tests {
     fn plain_secrets(pairs: &[(&str, &str)]) -> HashMap<String, SecretString> {
         pairs
             .iter()
-            .map(|(k, v)| (k.to_string(), SecretString::from_plain(*v)))
+            .map(|(k, v)| (k.to_string(), SecretString::from(*v)))
             .collect()
     }
 
diff --git a/src/ci/runtime.rs b/src/ci/runtime.rs
index 2fc3671..60f759f 100644
--- a/src/ci/runtime.rs
+++ b/src/ci/runtime.rs
@@ -591,7 +591,7 @@ mod tests {
         let mut secrets = HashMap::new();
         secrets.insert(
             "github_token".to_string(),
-            SecretString::from_plain("ghp_test_value"),
+            SecretString::from("ghp_test_value"),
         );
         let source = r#"(local ci (require :quire.ci))
 (ci.job :grab [:quire/push] (fn [{: secret}] (secret :github_token)))"#;
@@ -638,7 +638,7 @@ mod tests {
         let mut secrets = HashMap::new();
         secrets.insert(
             "github_token".to_string(),
-            SecretString::from_plain("ghp_long_secret_value"),
+            SecretString::from("ghp_long_secret_value"),
         );
         let source = r#"(local ci (require :quire.ci))
 (ci.job :go [:quire/push]
diff --git a/src/secret.rs b/src/secret.rs
index d162d0c..c948738 100644
--- a/src/secret.rs
+++ b/src/secret.rs
@@ -87,18 +87,25 @@ impl std::fmt::Debug for SecretString {
     }
 }
 
-impl SecretString {
-    /// Build from a plain string literal.
-    pub fn from_plain(value: impl Into<String>) -> Self {
-        Self(SecretSource::Plain(value.into()))
+impl From<String> for SecretString {
+    fn from(value: String) -> Self {
+        Self(SecretSource::Plain(value))
     }
+}
+
+impl From<&str> for SecretString {
+    fn from(value: &str) -> Self {
+        Self(SecretSource::Plain(value.to_string()))
+    }
+}
 
+impl From<PathBuf> for SecretString {
     /// Build from a file path. Contents are read lazily on first [`reveal`].
     ///
     /// [`reveal`]: SecretString::reveal
-    pub fn from_file(path: impl Into<PathBuf>) -> Self {
+    fn from(path: PathBuf) -> Self {
         Self(SecretSource::File {
-            path: path.into(),
+            path,
             resolved: OnceLock::new(),
         })
     }
@@ -135,7 +142,7 @@ mod tests {
 
     #[test]
     fn debug_redacts_value() {
-        let secret = SecretString::from_plain("super_secret_password");
+        let secret = SecretString::from("super_secret_password");
         let debug_output = format!("{secret:?}");
         assert_eq!(debug_output, "SecretString(\"<redacted>\")");
         assert!(
@@ -146,13 +153,13 @@ mod tests {
 
     #[test]
     fn reveal_returns_plain_value() {
-        let secret = SecretString::from_plain("plain_value");
+        let secret = SecretString::from("plain_value");
         assert_eq!(secret.reveal().unwrap(), "plain_value");
     }
 
     #[test]
     fn clone_preserves_plain_value() {
-        let secret = SecretString::from_plain("clonable");
+        let secret = SecretString::from("clonable");
         let cloned = secret.clone();
         assert_eq!(cloned.reveal().unwrap(), "clonable");
     }
@@ -163,7 +170,7 @@ mod tests {
         let path = dir.path().join("token");
         fs_err::write(&path, "initial\n").expect("write");
 
-        let secret = SecretString::from_file(&path);
+        let secret = SecretString::from(path.clone());
         assert_eq!(secret.reveal().unwrap(), "initial");
 
         // Overwrite the file — cached value should not change.
@@ -177,7 +184,7 @@ mod tests {
         let path = dir.path().join("secret");
         fs_err::write(&path, "line1\nline2\n").expect("write");
 
-        let secret = SecretString::from_file(&path);
+        let secret = SecretString::from(path.clone());
         assert_eq!(secret.reveal().unwrap(), "line1\nline2");
     }
 
@@ -189,13 +196,13 @@ mod tests {
         // Any additional trailing newlines are part of the secret.
         fs_err::write(&path, "value\n\n\n").expect("write");
 
-        let secret = SecretString::from_file(&path);
+        let secret = SecretString::from(path.clone());
         assert_eq!(secret.reveal().unwrap(), "value\n\n");
     }
 
     #[test]
     fn reveal_errors_on_missing_file() {
-        let secret = SecretString::from_file(PathBuf::from("/no/such/file/ever").as_path());
+        let secret = SecretString::from(PathBuf::from("/no/such/file/ever"));
         let err = secret.reveal().unwrap_err();
         assert!(
             matches!(err, Error::Resolve(_)),
@@ -209,7 +216,7 @@ mod tests {
         let path = dir.path().join("pw");
         fs_err::write(&path, "initial\n").expect("write");
 
-        let original = SecretString::from_file(&path);
+        let original = SecretString::from(path.clone());
         assert_eq!(original.reveal().unwrap(), "initial");
 
         // Overwrite after the original cached "initial". The clone gets a fresh
diff --git a/tests/property.rs b/tests/property.rs
index e7d0dbf..3070cae 100644
--- a/tests/property.rs
+++ b/tests/property.rs
@@ -64,7 +64,7 @@ fn updated_refs_excludes_only_zero_sha_deletions(tc: TestCase) {
 #[hegel::test]
 fn secret_string_debug_never_leaks_plain_value(tc: TestCase) {
     let value = tc.draw(text());
-    let secret = SecretString::from_plain(value.clone());
+    let secret = SecretString::from(value.clone());
     let debug = format!("{secret:?}");
     assert_eq!(debug, "SecretString(\"<redacted>\")");
 }
@@ -84,7 +84,7 @@ fn secret_string_from_file_strips_one_trailing_newline(tc: TestCase) {
     let path = dir.path().join("secret");
     fs_err::write(&path, &content).expect("write");
 
-    let revealed = SecretString::from_file(&path)
+    let revealed = SecretString::from(path.clone())
         .reveal()
         .expect("reveal")
         .to_string();
@@ -124,7 +124,7 @@ fn push_event_updated_refs_is_subtractive(tc: TestCase) {
 fn plain_secrets(pairs: &[(&str, &str)]) -> HashMap<String, SecretString> {
     pairs
         .iter()
-        .map(|(k, v)| (k.to_string(), SecretString::from_plain(*v)))
+        .map(|(k, v)| (k.to_string(), SecretString::from(*v)))
         .collect()
 }
 
@@ -153,7 +153,7 @@ fn resolved_registry(tc: TestCase) -> SecretRegistry {
     let entries = tc.draw(unique_secret_entries());
     let mut map = HashMap::new();
     for (name, value) in &entries {
-        map.insert(name.clone(), SecretString::from_plain(value.clone()));
+        map.insert(name.clone(), SecretString::from(value.clone()));
     }
     let mut reg = SecretRegistry::new(map);
     // Resolve all secrets so they're registered for redaction.
@@ -169,7 +169,7 @@ fn text_with_secrets(tc: TestCase) -> (SecretRegistry, String) {
     let mut map = HashMap::new();
     let mut long_values: Vec<String> = Vec::new();
     for (name, value) in &entries {
-        map.insert(name.clone(), SecretString::from_plain(value.clone()));
+        map.insert(name.clone(), SecretString::from(value.clone()));
         if value.len() >= MIN_REDACT_LEN {
             long_values.push(value.clone());
         }
@@ -198,7 +198,7 @@ fn redact_never_contains_revealed_long_values(tc: TestCase) {
     let mut map = HashMap::new();
     let mut long_values: Vec<String> = Vec::new();
     for (name, value) in &entries {
-        map.insert(name.clone(), SecretString::from_plain(value.clone()));
+        map.insert(name.clone(), SecretString::from(value.clone()));
         if value.len() >= MIN_REDACT_LEN {
             long_values.push(value.clone());
         }
@@ -256,7 +256,7 @@ fn redact_unresolved_registry_is_identity(tc: TestCase) {
     let entries = tc.draw(unique_secret_entries());
     let map: HashMap<String, SecretString> = entries
         .into_iter()
-        .map(|(k, v)| (k, SecretString::from_plain(v)))
+        .map(|(k, v)| (k, SecretString::from(v)))
         .collect();
     let reg = SecretRegistry::new(map);
     let text = tc.draw(text());
@@ -286,7 +286,7 @@ fn redact_output_never_shows_long_secret_values(tc: TestCase) {
     let entries = tc.draw(unique_secret_entries());
     let mut map = HashMap::new();
     for (name, value) in &entries {
-        map.insert(name.clone(), SecretString::from_plain(value.clone()));
+        map.insert(name.clone(), SecretString::from(value.clone()));
     }
     let mut reg = SecretRegistry::new(map);
     for (name, _) in &entries {