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}