1use std::{borrow::Cow, fmt};
2
3use crate::text::Text;
4
5#[derive(Default, Clone, Eq, PartialEq, Hash)]
27pub struct Masked<'a> {
28 inner: Cow<'a, str>,
29 mask_char: char,
30}
31
32impl<'a> Masked<'a> {
33 pub fn new(s: impl Into<Cow<'a, str>>, mask_char: char) -> Self {
34 Self {
35 inner: s.into(),
36 mask_char,
37 }
38 }
39
40 pub const fn mask_char(&self) -> char {
42 self.mask_char
43 }
44
45 pub fn value(&self) -> Cow<'a, str> {
47 self.inner.chars().map(|_| self.mask_char).collect()
48 }
49}
50
51impl fmt::Debug for Masked<'_> {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 fmt::Display::fmt(&self.inner, f)
56 }
57}
58
59impl fmt::Display for Masked<'_> {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 fmt::Display::fmt(&self.value(), f)
63 }
64}
65
66impl<'a> From<&'a Masked<'a>> for Cow<'a, str> {
67 fn from(masked: &'a Masked) -> Self {
68 masked.value()
69 }
70}
71
72impl<'a> From<Masked<'a>> for Cow<'a, str> {
73 fn from(masked: Masked<'a>) -> Self {
74 masked.value()
75 }
76}
77
78impl<'a> From<&'a Masked<'_>> for Text<'a> {
79 fn from(masked: &'a Masked) -> Self {
80 Text::raw(masked.value())
81 }
82}
83
84impl<'a> From<Masked<'a>> for Text<'a> {
85 fn from(masked: Masked<'a>) -> Self {
86 Text::raw(masked.value())
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::text::Line;
94
95 #[test]
96 fn new() {
97 let masked = Masked::new("12345", 'x');
98 assert_eq!(masked.inner, "12345");
99 assert_eq!(masked.mask_char, 'x');
100 }
101
102 #[test]
103 fn value() {
104 let masked = Masked::new("12345", 'x');
105 assert_eq!(masked.value(), "xxxxx");
106 }
107
108 #[test]
109 fn mask_char() {
110 let masked = Masked::new("12345", 'x');
111 assert_eq!(masked.mask_char(), 'x');
112 }
113
114 #[test]
115 fn debug() {
116 let masked = Masked::new("12345", 'x');
117 assert_eq!(format!("{masked:?}"), "12345");
118 assert_eq!(format!("{masked:.3?}"), "123", "Debug truncates");
119 }
120
121 #[test]
122 fn display() {
123 let masked = Masked::new("12345", 'x');
124 assert_eq!(format!("{masked}"), "xxxxx");
125 assert_eq!(format!("{masked:.3}"), "xxx", "Display truncates");
126 }
127
128 #[test]
129 fn into_text() {
130 let masked = Masked::new("12345", 'x');
131
132 let text: Text = (&masked).into();
133 assert_eq!(text.lines, [Line::from("xxxxx")]);
134
135 let text: Text = masked.into();
136 assert_eq!(text.lines, [Line::from("xxxxx")]);
137 }
138
139 #[test]
140 fn into_cow() {
141 let masked = Masked::new("12345", 'x');
142 let cow: Cow<str> = (&masked).into();
143 assert_eq!(cow, "xxxxx");
144
145 let cow: Cow<str> = masked.into();
146 assert_eq!(cow, "xxxxx");
147 }
148}