#![allow(
non_local_definitions,
unreachable_pub,
dead_code,
reason = "necessary for pyo3::pymethods"
)]
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, fmt};
#[cfg(feature = "python")]
use rigetti_pyo3::impl_repr;
macro_rules! make_secret_string {
(
$(#[$attr:meta])*
$name:ident
) => {
#[derive(Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(transparent)]
#[cfg_attr(not(feature = "python"), ::optipy::strip_pyo3)]
#[cfg_attr(feature = "stubs", ::pyo3_stub_gen::derive::gen_stub_pyclass)]
#[cfg_attr(feature = "python",
::pyo3::pyclass(module = "qcs_api_client_common.configuration", eq, frozen, skip_from_py_object))]
$(#[$attr])*
pub struct $name(Cow<'static, str>);
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
const NAME: &str = stringify!($name);
let len = self.0.len();
write!(f, "{NAME}(<REDACTED len: {len}>)")
}
}
impl<T: Into<Cow<'static, str>>> From<T> for $name {
fn from(value: T) -> Self {
Self(value.into())
}
}
#[cfg_attr(not(feature = "python"), ::optipy::strip_pyo3)]
#[cfg_attr(feature = "stubs", ::pyo3_stub_gen::derive::gen_stub_pymethods)]
#[cfg_attr(feature = "python", ::pyo3::pymethods)]
impl $name {
#[must_use]
#[getter(is_empty)]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
#[getter(secret)]
pub fn secret(&self) -> &str {
self.0.as_ref()
}
}
#[cfg(feature = "python")]
impl_repr!($name);
#[cfg(feature = "python")]
#[cfg_attr(feature = "stubs", ::pyo3_stub_gen::derive::gen_stub_pymethods)]
#[::pyo3::pymethods]
impl $name {
#[new]
pub(crate) fn __new__(value: String) -> Self {
Self::from(value)
}
}
}
}
make_secret_string!(
SecretRefreshToken
);
make_secret_string!(
SecretAccessToken
);
make_secret_string!(
ClientSecret
);
#[cfg(test)]
mod test {
use super::*;
make_secret_string!(TestSecret);
#[test]
fn test_secret_string_serialization() {
const SECRET_VALUE: &str = "my_secret_value";
const SECRET_VALUE_JSON: &str = "\"my_secret_value\"";
assert_eq!(
serde_json::to_value(TestSecret::from(SECRET_VALUE)).unwrap(),
serde_json::Value::String(SECRET_VALUE.to_string()),
);
let test_secret: TestSecret = serde_json::from_str(SECRET_VALUE_JSON).unwrap();
assert_eq!(test_secret.secret(), SECRET_VALUE);
assert_eq!(
serde_json::to_string(&test_secret).unwrap(),
SECRET_VALUE_JSON
);
}
#[test]
fn test_secret_string_debug_does_not_leak() {
const SECRET_VALUE: &str = "my_secret_value";
let test_secret = TestSecret::from(SECRET_VALUE);
let debug_content = format!("{test_secret:?}");
assert_eq!(
debug_content,
format!("TestSecret(<REDACTED len: {}>)", SECRET_VALUE.len())
);
}
}