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