1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

//! A general wrapper to allow for feature flagged redactions.

use std::fmt::{Debug, Display, Error, Formatter};

use crate::instrumentation::MakeFmt;

use super::REDACTED;

/// A wrapper used to modify the [`Display`] and [`Debug`] implementation of the inner structure
/// based on the feature flag `unredacted-logging`. When the `unredacted-logging` feature is enabled, the
/// implementations will defer to those on `T`, when disabled they will defer to [`REDACTED`].
///
/// Note that there are [`Display`] and [`Debug`] implementations for `&T` where `T: Display`
/// and `T: Debug` respectively - wrapping references is allowed for the cases when consuming the
/// inner struct is not desired.
///
/// # Example
///
/// ```
/// # use aws_smithy_http_server::instrumentation::sensitivity::Sensitive;
/// # let address = "";
/// tracing::debug!(
///     name = %Sensitive("Alice"),
///     friends = ?Sensitive(["Bob"]),
///     address = ?Sensitive(&address)
/// );
/// ```
pub struct Sensitive<T>(pub T);

impl<T> Debug for Sensitive<T>
where
    T: Debug,
{
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        if cfg!(feature = "unredacted-logging") {
            self.0.fmt(f)
        } else {
            Debug::fmt(&REDACTED, f)
        }
    }
}

impl<T> Display for Sensitive<T>
where
    T: Display,
{
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        if cfg!(feature = "unredacted-logging") {
            self.0.fmt(f)
        } else {
            Display::fmt(&REDACTED, f)
        }
    }
}

/// A [`MakeFmt`] producing [`Sensitive`].
#[derive(Debug, Clone)]
pub struct MakeSensitive;

impl<T> MakeFmt<T> for MakeSensitive {
    type Target = Sensitive<T>;

    fn make(&self, source: T) -> Self::Target {
        Sensitive(source)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn debug() {
        let inner = "hello world";
        let sensitive = Sensitive(inner);
        let actual = format!("{:?}", sensitive);
        let expected = if cfg!(feature = "unredacted-logging") {
            format!("{:?}", inner)
        } else {
            format!("{:?}", REDACTED)
        };
        assert_eq!(actual, expected)
    }

    #[test]
    fn display() {
        let inner = "hello world";
        let sensitive = Sensitive(inner);
        let actual = format!("{}", sensitive);
        let expected = if cfg!(feature = "unredacted-logging") {
            inner.to_string()
        } else {
            REDACTED.to_string()
        };
        assert_eq!(actual, expected)
    }
}