use zeroize::Zeroizing;
#[derive(Clone)]
pub struct Password {
inner: Zeroizing<String>,
}
impl Password {
pub fn new<S: Into<String>>(password: S) -> Self {
Self {
inner: Zeroizing::new(password.into()),
}
}
pub fn as_utf16_le(&self) -> Vec<u8> {
self.inner
.encode_utf16()
.flat_map(|c| c.to_le_bytes())
.collect()
}
pub fn as_str(&self) -> &str {
&self.inner
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn len(&self) -> usize {
self.inner.chars().count()
}
}
impl std::fmt::Debug for Password {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Password")
.field("len", &self.inner.len())
.finish()
}
}
impl From<&str> for Password {
fn from(s: &str) -> Self {
Self::new(s)
}
}
impl From<String> for Password {
fn from(s: String) -> Self {
Self::new(s)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_password_utf16le_ascii() {
let password = Password::new("test");
let bytes = password.as_utf16_le();
assert_eq!(bytes, vec![0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00]);
}
#[test]
fn test_password_utf16le_unicode() {
let password = Password::new("пароль"); let bytes = password.as_utf16_le();
assert_eq!(bytes.len(), 12); }
#[test]
fn test_password_utf16le_empty() {
let password = Password::new("");
let bytes = password.as_utf16_le();
assert!(bytes.is_empty());
}
#[test]
fn test_password_debug() {
let password = Password::new("secret");
let debug = format!("{:?}", password);
assert!(!debug.contains("secret"));
assert!(debug.contains("len"));
}
#[test]
fn test_password_from_str() {
let password: Password = "test".into();
assert_eq!(password.as_str(), "test");
}
#[test]
fn test_password_from_string() {
let password: Password = String::from("test").into();
assert_eq!(password.as_str(), "test");
}
#[test]
fn test_password_len() {
let password = Password::new("test");
assert_eq!(password.len(), 4);
assert!(!password.is_empty());
let empty = Password::new("");
assert_eq!(empty.len(), 0);
assert!(empty.is_empty());
}
}