#[cfg(feature = "alloc")]
use alloc::string::String;
mod private {
pub trait Sealed {}
impl Sealed for &str {}
impl Sealed for &[u8] {}
impl<const N: usize> Sealed for &[u8; N] {}
#[cfg(feature = "alloc")]
impl Sealed for &alloc::string::String {}
}
#[cfg(feature = "alloc")]
pub trait GuardInput: private::Sealed {
fn as_utf8_lossy(&self) -> (String, bool);
fn raw_bytes(&self) -> Option<&[u8]>;
}
#[cfg(feature = "alloc")]
impl GuardInput for &str {
fn as_utf8_lossy(&self) -> (String, bool) {
(String::from(*self), false)
}
fn raw_bytes(&self) -> Option<&[u8]> { None }
}
#[cfg(feature = "alloc")]
impl GuardInput for &[u8] {
fn as_utf8_lossy(&self) -> (String, bool) {
use alloc::borrow::Cow;
let cow = String::from_utf8_lossy(self);
let lossy = matches!(cow, Cow::Owned(_));
(cow.into_owned(), lossy)
}
fn raw_bytes(&self) -> Option<&[u8]> { Some(*self) }
}
#[cfg(feature = "alloc")]
impl GuardInput for &alloc::string::String {
fn as_utf8_lossy(&self) -> (String, bool) {
((*self).clone(), false)
}
fn raw_bytes(&self) -> Option<&[u8]> { None }
}
#[cfg(feature = "alloc")]
impl<const N: usize> GuardInput for &[u8; N] {
fn as_utf8_lossy(&self) -> (String, bool) {
use alloc::borrow::Cow;
let cow = String::from_utf8_lossy(*self);
let lossy = matches!(cow, Cow::Owned(_));
(cow.into_owned(), lossy)
}
fn raw_bytes(&self) -> Option<&[u8]> { Some(*self) }
}
#[cfg(test)]
mod tests {
use std::prelude::v1::*;
use super::*;
#[test]
fn str_input_not_lossy() {
let (text, lossy) = "hello".as_utf8_lossy();
assert_eq!(text, "hello");
assert!(!lossy);
}
#[test]
fn bytes_valid_utf8_not_lossy() {
let (text, lossy) = b"hello".as_utf8_lossy();
assert_eq!(text, "hello");
assert!(!lossy);
}
#[test]
fn bytes_invalid_utf8_is_lossy() {
let (text, lossy) = b"\xFF\xFE".as_utf8_lossy();
assert!(text.contains('\u{FFFD}'));
assert!(lossy);
}
#[test]
fn bytes_big5_0x5c_second_byte() {
let (text, lossy) = b"\xB3\x5C".as_utf8_lossy();
assert!(lossy);
assert!(text.contains('\u{FFFD}')); assert!(text.contains('\\')); }
}