Repository: juspay/hyperswitch
File: crates/masking/src/secret.rs (Rust)

Code before:
                    }
                    first = false;
                    write!(f, "\"{key}\":")?;
                    Self::fmt(val, f)?;
                }
                write!(f, "}}")
            }
            serde_json::Value::Array(arr) => {
                write!(f, "[")?;
                let mut first = true;
                for val in arr {
                    if !first {
                        write!(f, ", ")?;
                    }
                    first = false;
                    Self::fmt(val, f)?;
                }
                write!(f, "]")
            }
            serde_json::Value::String(s) => {
                // For strings, we show a masked version that gives a hint about the content
                let char_count = s.chars().count();
                let masked = if char_count <= 2 {
                    "**".to_string()
                } else if char_count <= 6 {
                    let first = s.chars().next().unwrap_or('*');
                    format!("{first}**")
                } else {
                    // For longer strings, show first and last character with length in between
                    let first = s.chars().next().unwrap_or('*');
                    let last = s.chars().last().unwrap_or('*');
                    format!("{first}**{count}**{last}", count = char_count - 2)
                };
                write!(f, "\"{masked}\"")
            }
            serde_json::Value::Number(n) => {
                // For numbers, we can show the order of magnitude
                if n.is_i64() || n.is_u64() {
                    let num_str = n.to_string();
                    let masked_num = "*".repeat(num_str.len());
                    write!(f, "{masked_num}")
                } else if n.is_f64() {
                    // For floats, just use a generic mask
                    write!(f, "**.**")
                } else {
                    write!(f, "0")
                }
            }
            serde_json::Value::Bool(b) => {
                // For booleans, we can show a hint about which one it is
                write!(f, "{}", if *b { "**true" } else { "**false" })

Review comment (suggestion):
Nit: I feel we can add a (code) comment stating that the char count would be an approximate (could be greater than the actual count), since this does not consider graphemes (what we usually consider as a "character").

Code after:
                    }
                    first = false;
                    write!(f, "\"{key}\":")?;
                    Self::fmt(val, f)?;
                }
                write!(f, "}}")
            }
            serde_json::Value::Array(arr) => {
                write!(f, "[")?;
                let mut first = true;
                for val in arr {
                    if !first {
                        write!(f, ", ")?;
                    }
                    first = false;
                    Self::fmt(val, f)?;
                }
                write!(f, "]")
            }
            serde_json::Value::String(s) => {
                // For strings, we show a masked version that gives a hint about the content
                // String::chars().count() is an approximation and could be greater than
                // the actual "character" count, since it does not consider graphemes (what we
                // usually consider as a "character"). To be more accurate at the cost of some
                // performance we can use "graphemes()" from the unicode-segmentation crate
                let char_count = s.chars().count();
                let masked = if char_count <= 2 {
                    "**".to_string()
                } else if char_count <= 6 {
                    let first = s.chars().next().unwrap_or('*');
                    format!("{first}**")
                } else {
                    // For longer strings, show first and last character with length in between
                    let first = s.chars().next().unwrap_or('*');
                    let last = s.chars().last().unwrap_or('*');
                    format!("{first}**{count}**{last}", count = char_count - 2)
                };
                write!(f, "\"{masked}\"")
            }
            serde_json::Value::Number(n) => {
                // For numbers, we can show the order of magnitude
                if n.is_i64() || n.is_u64() {
                    let num_str = n.to_string();
                    let masked_num = "*".repeat(num_str.len());
                    write!(f, "{masked_num}")
                } else if n.is_f64() {
                    // For floats, just use a generic mask
                    write!(f, "**.**")
                } else {
                    write!(f, "0")
                }