1use alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec};
4use core::{
5 convert::Infallible,
6 fmt,
7 str::{self, FromStr},
8};
9use encoding::{Decode, Encode, Error, Reader, Writer};
10
11#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
26pub struct Comment(Box<[u8]>);
27
28impl AsRef<[u8]> for Comment {
29 fn as_ref(&self) -> &[u8] {
30 self.as_bytes()
31 }
32}
33
34impl AsRef<str> for Comment {
35 fn as_ref(&self) -> &str {
36 self.as_str_lossy()
37 }
38}
39
40impl Decode for Comment {
41 type Error = Error;
42
43 fn decode(reader: &mut impl Reader) -> encoding::Result<Self> {
44 Vec::<u8>::decode(reader).map(Into::into)
45 }
46}
47
48impl Encode for Comment {
49 fn encoded_len(&self) -> Result<usize, Error> {
50 self.0.encoded_len()
51 }
52
53 fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
54 self.0.encode(writer)
55 }
56}
57
58impl FromStr for Comment {
59 type Err = Infallible;
60
61 fn from_str(s: &str) -> Result<Comment, Infallible> {
62 Ok(s.into())
63 }
64}
65
66impl From<&str> for Comment {
67 fn from(s: &str) -> Comment {
68 s.to_owned().into()
69 }
70}
71
72impl From<String> for Comment {
73 fn from(s: String) -> Self {
74 s.into_bytes().into()
75 }
76}
77
78impl From<&[u8]> for Comment {
79 fn from(bytes: &[u8]) -> Comment {
80 bytes.to_owned().into()
81 }
82}
83
84impl From<Vec<u8>> for Comment {
85 fn from(vec: Vec<u8>) -> Self {
86 Self(vec.into_boxed_slice())
87 }
88}
89
90impl From<Comment> for Vec<u8> {
91 fn from(comment: Comment) -> Vec<u8> {
92 comment.0.into()
93 }
94}
95
96impl TryFrom<Comment> for String {
97 type Error = Error;
98
99 fn try_from(comment: Comment) -> Result<String, Error> {
100 comment.as_str().map(ToOwned::to_owned)
101 }
102}
103
104impl fmt::Display for Comment {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 f.write_str(self.as_str_lossy())
107 }
108}
109
110impl Comment {
111 pub fn as_bytes(&self) -> &[u8] {
113 &self.0
114 }
115
116 pub fn as_str(&self) -> Result<&str, Error> {
118 Ok(str::from_utf8(&self.0)?)
119 }
120
121 #[cfg(feature = "alloc")]
126 pub fn as_str_lossy(&self) -> &str {
127 for i in (1..=self.len()).rev() {
128 if let Ok(s) = str::from_utf8(&self.0[..i]) {
129 return s;
130 }
131 }
132
133 ""
134 }
135
136 pub fn is_empty(&self) -> bool {
138 self.0.is_empty()
139 }
140
141 pub fn len(&self) -> usize {
143 self.0.len()
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::Comment;
150
151 #[test]
152 fn as_str_lossy_ignores_non_utf8_data() {
153 const EXAMPLE: &[u8] = b"hello world\xc3\x28";
154
155 let comment = Comment::from(EXAMPLE);
156 assert_eq!(comment.as_str_lossy(), "hello world");
157 }
158}