bson/
utf8_lossy.rs

1use std::ops::{Deref, DerefMut};
2
3// One could imagine passthrough Borrow impls; however, it turns out that can't be made to work
4// because of the existing base library impl of Borrow<T> for T will conflict despite that not
5// actually being possible to construct (https://github.com/rust-lang/rust/issues/50237).  So,
6// sadly, Borrow impls for HumanReadable are deliberately omitted :(
7
8/// Wrapper type for lossily decoding embedded strings with invalid UTF-8 sequences.
9///
10/// A [`RawDocument`](crate::RawDocument) or [`RawDocumentBuf`](crate::RawDocumentBuf) can be
11/// converted into a `Utf8Lossy<Document>` via `TryFrom`; any invalid UTF-8 sequences contained in
12/// strings in the source buffer will be replaced with the Unicode replacement character.
13///
14/// If the `serde` feature is enabled, this type will also cause the same lossy decoding to apply
15/// to any strings contained in a wrapped deserializable type when deserializing from BSON bytes.
16/// This wrapper has no effect on serialization behavior.
17#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
18#[repr(transparent)]
19pub struct Utf8Lossy<T>(pub T);
20
21impl<T: std::fmt::Display> std::fmt::Display for Utf8Lossy<T> {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        self.0.fmt(f)
24    }
25}
26
27impl<T> From<T> for Utf8Lossy<T> {
28    fn from(value: T) -> Self {
29        Self(value)
30    }
31}
32
33impl<T> Deref for Utf8Lossy<T> {
34    type Target = T;
35
36    fn deref(&self) -> &Self::Target {
37        &self.0
38    }
39}
40
41impl<T> DerefMut for Utf8Lossy<T> {
42    fn deref_mut(&mut self) -> &mut Self::Target {
43        &mut self.0
44    }
45}
46
47impl<T, R> AsRef<R> for Utf8Lossy<T>
48where
49    R: ?Sized,
50    <Utf8Lossy<T> as Deref>::Target: AsRef<R>,
51{
52    fn as_ref(&self) -> &R {
53        self.deref().as_ref()
54    }
55}
56
57impl<T, R: ?Sized> AsMut<R> for Utf8Lossy<T>
58where
59    <Utf8Lossy<T> as Deref>::Target: AsMut<R>,
60{
61    fn as_mut(&mut self) -> &mut R {
62        self.deref_mut().as_mut()
63    }
64}
65
66#[cfg(feature = "serde")]
67pub(crate) const UTF8_LOSSY_NEWTYPE: &str = "$__bson_private_utf8_lossy";
68
69#[cfg(feature = "serde")]
70impl<T: serde::Serialize> serde::Serialize for Utf8Lossy<T> {
71    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
72    where
73        S: serde::Serializer,
74    {
75        self.0.serialize(serializer)
76    }
77}
78
79#[cfg(feature = "serde")]
80impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for Utf8Lossy<T> {
81    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
82    where
83        D: serde::Deserializer<'de>,
84    {
85        struct V<T>(std::marker::PhantomData<fn() -> T>);
86        impl<'de, T: serde::Deserialize<'de>> serde::de::Visitor<'de> for V<T> {
87            type Value = Utf8Lossy<T>;
88            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
89                formatter.write_str("Utf8Lossy wrapper")
90            }
91            fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
92            where
93                D: serde::Deserializer<'de>,
94            {
95                T::deserialize(deserializer).map(Utf8Lossy)
96            }
97        }
98        deserializer.deserialize_newtype_struct(UTF8_LOSSY_NEWTYPE, V(std::marker::PhantomData))
99    }
100}