nautilus_core/
string.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! String manipulation functionality.
17
18/// Masks an API key by showing only the first and last 4 characters.
19///
20/// For keys 8 characters or shorter, returns asterisks only.
21///
22/// # Examples
23///
24/// ```
25/// use nautilus_core::string::mask_api_key;
26///
27/// assert_eq!(mask_api_key("abcdefghijklmnop"), "abcd...mnop");
28/// assert_eq!(mask_api_key("short"), "*****");
29/// ```
30#[must_use]
31pub fn mask_api_key(key: &str) -> String {
32    // Work with Unicode scalars to avoid panicking on multibyte characters.
33    let chars: Vec<char> = key.chars().collect();
34    let len = chars.len();
35
36    if len <= 8 {
37        return "*".repeat(len);
38    }
39
40    let first: String = chars[..4].iter().collect();
41    let last: String = chars[len - 4..].iter().collect();
42
43    format!("{first}...{last}")
44}
45
46#[cfg(test)]
47mod tests {
48    use rstest::rstest;
49
50    use super::*;
51
52    #[rstest]
53    #[case("", "")]
54    #[case("a", "*")]
55    #[case("abc", "***")]
56    #[case("abcdefgh", "********")]
57    #[case("abcdefghi", "abcd...fghi")]
58    #[case("abcdefghijklmnop", "abcd...mnop")]
59    #[case("VeryLongAPIKey123456789", "Very...6789")]
60    fn test_mask_api_key(#[case] input: &str, #[case] expected: &str) {
61        assert_eq!(mask_api_key(input), expected);
62    }
63}