fidl_next_codec/wire/string/
optional.rs1use core::fmt;
6use core::mem::MaybeUninit;
7use core::str::from_utf8;
8
9use munge::munge;
10
11use crate::{
12 Constrained, Decode, DecodeError, Decoder, EncodeError, EncodeOption, Encoder, FromWireOption,
13 FromWireOptionRef, IntoNatural, Slot, ValidationError, Wire, wire,
14};
15
16#[repr(transparent)]
18pub struct OptionalString<'de> {
19 vec: wire::OptionalVector<'de, u8>,
20}
21
22unsafe impl Wire for OptionalString<'static> {
23 type Narrowed<'de> = OptionalString<'de>;
24
25 #[inline]
26 fn zero_padding(out: &mut MaybeUninit<Self>) {
27 munge!(let Self { vec } = out);
28 wire::OptionalVector::<u8>::zero_padding(vec);
29 }
30}
31
32impl OptionalString<'_> {
33 #[inline]
35 pub fn encode_present(out: &mut MaybeUninit<Self>, len: u64) {
36 munge!(let Self { vec } = out);
37 wire::OptionalVector::encode_present(vec, len);
38 }
39
40 #[inline]
42 pub fn encode_absent(out: &mut MaybeUninit<Self>) {
43 munge!(let Self { vec } = out);
44 wire::OptionalVector::encode_absent(vec);
45 }
46
47 #[inline]
49 pub fn is_some(&self) -> bool {
50 self.vec.is_some()
51 }
52
53 #[inline]
55 pub fn is_none(&self) -> bool {
56 self.vec.is_none()
57 }
58
59 #[inline]
61 pub fn as_ref(&self) -> Option<&wire::String<'_>> {
62 self.vec.as_ref().map(|vec| unsafe { &*(vec as *const wire::Vector<'_, u8>).cast() })
63 }
64
65 fn validate_max_len(slot: Slot<'_, Self>, limit: u64) -> Result<(), ValidationError> {
67 munge!(let Self { vec } = slot);
68 match wire::OptionalVector::validate_max_len(vec, limit) {
69 Ok(()) => Ok(()),
70 Err(ValidationError::VectorTooLong { count, limit }) => {
71 Err(ValidationError::StringTooLong { count, limit })
72 }
73 Err(e) => Err(e),
74 }
75 }
76}
77
78impl Constrained for OptionalString<'_> {
79 type Constraint = u64;
80
81 fn validate(slot: Slot<'_, Self>, constraint: Self::Constraint) -> Result<(), ValidationError> {
82 Self::validate_max_len(slot, constraint)
83 }
84}
85
86impl fmt::Debug for OptionalString<'_> {
87 #[inline]
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 self.as_ref().fmt(f)
90 }
91}
92
93impl<T> PartialEq<Option<T>> for OptionalString<'_>
94where
95 for<'de> wire::String<'de>: PartialEq<T>,
96{
97 fn eq(&self, other: &Option<T>) -> bool {
98 match (self.as_ref(), other.as_ref()) {
99 (Some(lhs), Some(rhs)) => lhs == rhs,
100 (None, None) => true,
101 _ => false,
102 }
103 }
104}
105
106unsafe impl<'de, D: Decoder<'de> + ?Sized> Decode<D> for OptionalString<'de> {
107 #[inline]
108 fn decode(slot: Slot<'_, Self>, decoder: &mut D, constraint: u64) -> Result<(), DecodeError> {
109 munge!(let Self { mut vec } = slot);
110
111 let result = unsafe { wire::OptionalVector::decode_raw(vec.as_mut(), decoder, constraint) };
112 match result {
113 Ok(()) => (),
114 Err(DecodeError::Validation(ValidationError::VectorTooLong { count, limit })) => {
115 return Err(DecodeError::Validation(ValidationError::StringTooLong {
116 count,
117 limit,
118 }));
119 }
120 Err(e) => return Err(e),
121 }
122 let vec = unsafe { vec.deref_unchecked() };
123 if let Some(bytes) = vec.as_ref() {
124 if !bytes.as_slice().is_ascii() {
126 let _ = from_utf8(bytes)?;
129 }
130 }
131
132 Ok(())
133 }
134}
135
136unsafe impl<E: Encoder + ?Sized> EncodeOption<OptionalString<'static>, E> for String {
137 #[inline]
138 fn encode_option(
139 this: Option<Self>,
140 encoder: &mut E,
141 out: &mut MaybeUninit<OptionalString<'static>>,
142 constraint: u64,
143 ) -> Result<(), EncodeError> {
144 <&str>::encode_option(this.as_deref(), encoder, out, constraint)
145 }
146}
147
148unsafe impl<E: Encoder + ?Sized> EncodeOption<OptionalString<'static>, E> for &String {
149 #[inline]
150 fn encode_option(
151 this: Option<Self>,
152 encoder: &mut E,
153 out: &mut MaybeUninit<OptionalString<'static>>,
154 constraint: u64,
155 ) -> Result<(), EncodeError> {
156 <&str>::encode_option(this.map(String::as_str), encoder, out, constraint)
157 }
158}
159
160unsafe impl<E: Encoder + ?Sized> EncodeOption<OptionalString<'static>, E> for &str {
161 #[inline]
162 fn encode_option(
163 this: Option<Self>,
164 encoder: &mut E,
165 out: &mut MaybeUninit<OptionalString<'static>>,
166 _constraint: u64,
167 ) -> Result<(), EncodeError> {
168 if let Some(string) = this {
169 encoder.write(string.as_bytes());
170 OptionalString::encode_present(out, string.len() as u64);
171 } else {
172 OptionalString::encode_absent(out);
173 }
174 Ok(())
175 }
176}
177
178impl FromWireOption<OptionalString<'_>> for String {
179 #[inline]
180 fn from_wire_option(wire: OptionalString<'_>) -> Option<Self> {
181 Vec::from_wire_option(wire.vec).map(|vec| unsafe { String::from_utf8_unchecked(vec) })
182 }
183}
184
185impl IntoNatural for OptionalString<'_> {
186 type Natural = Option<String>;
187}
188
189impl FromWireOptionRef<OptionalString<'_>> for String {
190 #[inline]
191 fn from_wire_option_ref(wire: &OptionalString<'_>) -> Option<Self> {
192 Vec::from_wire_option_ref(&wire.vec).map(|vec| unsafe { String::from_utf8_unchecked(vec) })
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use crate::{DecoderExt as _, EncoderExt as _, chunks, wire};
199
200 #[test]
201 fn decode_optional_string() {
202 assert_eq!(
203 chunks![
204 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
205 0x00, 0x00,
206 ]
207 .as_mut_slice()
208 .decode_with_constraint::<wire::OptionalString<'_>>(1000)
209 .unwrap(),
210 None::<&str>,
211 );
212 }
213
214 #[test]
215 fn encode_optional_string() {
216 assert_eq!(
217 Vec::encode_with_constraint(None::<String>, 1000).unwrap(),
218 chunks![
219 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
220 0x00, 0x00,
221 ],
222 );
223 }
224}