use std::cell::OnceCell;
use std::fmt;
use std::fmt::Formatter;
use std::fmt::Write;
use std::hash::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;
use serde::Serialize;
use tracing::Value;
use tracing::field::DebugValue;
use tracing::field::debug;
use crate::misc::redacted::Redacted;
pub fn json<T>(value: &T) -> impl Value + use<'_, T>
where
T: Serialize + ?Sized,
{
string_closure(move || {
let result = Redacted::with_tracing_serialize(|| serde_json::to_string(value));
result.unwrap_or_else(|_| "<error>".to_string())
})
}
pub fn hex(value: &[u8]) -> impl Value + use<'_> {
string_closure(move || {
let mut string = String::with_capacity(value.len() * 2);
for byte in value {
string
.write_fmt(format_args!("{byte:02x}"))
.expect("fmt::Write for String shouldn't fail");
}
string
})
}
pub fn hash<T>(value: &T) -> impl Value + use<'_, T>
where
T: Hash,
{
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
let hash = hasher.finish().to_ne_bytes();
let mut string = String::with_capacity(hash.len() * 2);
for byte in hash {
string
.write_fmt(format_args!("{byte:02x}"))
.expect("fmt::Write for String shouldn't fail");
}
string
}
fn string_closure<F>(closure: F) -> DebugValue<StringClosure<F>>
where
F: Fn() -> String,
{
debug(StringClosure {
closure,
cache: OnceCell::new(),
})
}
struct StringClosure<F> {
closure: F,
cache: OnceCell<String>,
}
impl<F> fmt::Debug for StringClosure<F>
where
F: Fn() -> String,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(self.cache.get_or_init(&self.closure))
}
}