radicle_ci_broker/
sensitive.rsuse std::fmt;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
const PLACEHOLDER: &str = "<REDACTED>";
#[derive()]
pub struct Sensitive {
#[allow(dead_code)]
data: String,
}
impl fmt::Display for Sensitive {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", PLACEHOLDER)
}
}
impl fmt::Debug for Sensitive {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}", PLACEHOLDER)
}
}
impl Serialize for Sensitive {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(PLACEHOLDER)
}
}
impl<'de> Deserialize<'de> for Sensitive {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(SensitiveVisitor)
}
}
struct SensitiveVisitor;
impl<'de> de::Visitor<'de> for SensitiveVisitor {
type Value = Sensitive;
fn expecting(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Sensitive { data: s.into() })
}
}
impl Sensitive {
#[cfg(test)]
fn new(data: &str) -> Self {
Self { data: data.into() }
}
pub fn as_str(&self) -> &str {
&self.data
}
}
#[cfg(test)]
mod test_sensitive {
use super::*;
#[test]
fn displayed() {
let s = Sensitive::new("foo");
let output = format!("{s}");
assert!(!output.contains("foo"));
}
#[test]
fn debugged() {
let s = Sensitive::new("foo");
let output = format!("{s:?}");
assert!(!output.contains("foo"));
}
#[test]
fn ser() -> Result<(), Box<dyn std::error::Error>> {
let s = Sensitive::new("foo");
let output = serde_yml::to_string(&s)?;
println!("{output:#?}");
assert!(!output.contains("foo"));
Ok(())
}
#[test]
fn deser() -> Result<(), Box<dyn std::error::Error>> {
#[derive(Deserialize)]
struct Foo {
secret: Sensitive,
}
let s: Foo = serde_yml::from_str("secret: foo")?;
assert_eq!(s.secret.data, "foo");
Ok(())
}
}