reqsign_core/
utils.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Utility functions and types.
19
20use std::fmt::Debug;
21
22/// Redacts a string by replacing all but the first and last three characters with asterisks.
23///
24/// - If the input string has fewer than 12 characters, it should be entirely redacted.
25/// - If the input string has 12 or more characters, only the first three and the last three.
26///
27/// This design is to allow users to distinguish between different redacted strings but avoid
28/// leaking sensitive information.
29pub struct Redact<'a>(&'a str);
30
31impl<'a> From<&'a str> for Redact<'a> {
32    fn from(value: &'a str) -> Self {
33        Redact(value)
34    }
35}
36
37impl<'a> From<&'a String> for Redact<'a> {
38    fn from(value: &'a String) -> Self {
39        Redact(value.as_str())
40    }
41}
42
43impl<'a> From<&'a Option<String>> for Redact<'a> {
44    fn from(value: &'a Option<String>) -> Self {
45        match value {
46            None => Redact(""),
47            Some(v) => Redact(v),
48        }
49    }
50}
51
52impl Debug for Redact<'_> {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        let length = self.0.len();
55        if length == 0 {
56            f.write_str("EMPTY")
57        } else if length < 12 {
58            f.write_str("***")
59        } else {
60            f.write_str(&self.0[..3])?;
61            f.write_str("***")?;
62            f.write_str(&self.0[length - 3..])
63        }
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_redact() {
73        let cases = vec![
74            ("Short", "***"),
75            ("Hello World!", "Hel***ld!"),
76            ("This is a longer string", "Thi***ing"),
77            ("", "EMPTY"),
78            ("HelloWorld", "***"),
79        ];
80
81        for (input, expected) in cases {
82            assert_eq!(
83                format!("{:?}", Redact(input)),
84                expected,
85                "Failed on input: {}",
86                input
87            );
88        }
89    }
90}