qubit_codec_misc/
form_urlencoded_codec.rs1use crate::percent_codec::{
11 percent_decode_byte,
12 percent_decode_bytes,
13 percent_encode_byte,
14 percent_encode_bytes,
15 percent_encode_len,
16 percent_invalid_consumed,
17};
18use crate::{
19 MiscCodecError,
20 MiscCodecResult,
21 internal::{
22 ParseError,
23 PercentEncodingMode,
24 },
25 misc_codec_error::map_misc_decode_failure_with_consumed,
26};
27use qubit_codec::{
28 Codec,
29 ValueDecoder,
30 ValueEncoder,
31};
32
33#[must_use]
39#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
40pub struct FormUrlencodedCodec;
41
42impl FormUrlencodedCodec {
43 #[inline]
48 pub fn new() -> Self {
49 Self
50 }
51
52 #[inline]
60 #[must_use]
61 pub fn encode(&self, text: &str) -> String {
62 percent_encode_bytes(
63 text.as_bytes(),
64 PercentEncodingMode::FormUrlencoded,
65 )
66 }
67
68 #[inline]
80 pub fn decode(&self, text: &str) -> MiscCodecResult<String> {
81 String::from_utf8(percent_decode_bytes(text, true)?)
82 .map_err(MiscCodecError::from)
83 }
84}
85
86impl ValueEncoder<str> for FormUrlencodedCodec {
87 type Error = MiscCodecError;
88 type Output = String;
89
90 #[inline]
92 fn encode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
93 Ok(FormUrlencodedCodec::encode(self, input))
94 }
95}
96
97impl ValueDecoder<str> for FormUrlencodedCodec {
98 type Error = MiscCodecError;
99 type Output = String;
100
101 #[inline]
103 fn decode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
104 FormUrlencodedCodec::decode(self, input)
105 }
106}
107
108impl Codec for FormUrlencodedCodec {
109 type Value = u8;
110 type Unit = u8;
111 type DecodeError = MiscCodecError;
112 type EncodeError = MiscCodecError;
113
114 const MIN_UNITS_PER_VALUE: usize = 1;
115 const MAX_ENCODE_UNITS_PER_VALUE: usize = 3;
116
117 const MAX_DECODE_UNITS_PER_VALUE: usize = 3;
118
119 #[inline(always)]
121 fn encode_len(&self, value: &u8) -> usize {
122 percent_encode_len(*value, PercentEncodingMode::FormUrlencoded)
123 }
124
125 #[inline]
131 unsafe fn decode(
132 &mut self,
133 input: &[u8],
134 input_index: usize,
135 ) -> Result<
136 (u8, core::num::NonZeroUsize),
137 qubit_codec::DecodeFailure<Self::DecodeError>,
138 > {
139 debug_assert!(input_index < input.len());
140
141 let (value, consumed) = percent_decode_byte(input, input_index, true)
142 .map_err(|error| {
143 ParseError::into_decode_failure_with_consumed(
144 error,
145 qubit_codec::nz!(3),
146 )
147 })?;
148 debug_assert!(consumed > 0);
149 let consumed = qubit_codec::nz!(consumed);
152 Ok((value, consumed))
153 }
154
155 #[inline]
161 unsafe fn encode(
162 &mut self,
163 value: &u8,
164 output: &mut [u8],
165 output_index: usize,
166 ) -> Result<usize, Self::EncodeError> {
167 let required =
168 percent_encode_len(*value, PercentEncodingMode::FormUrlencoded);
169 debug_assert!(output_index + required <= output.len());
170
171 let written = percent_encode_byte(
172 *value,
173 output,
174 output_index,
175 PercentEncodingMode::FormUrlencoded,
176 );
177 debug_assert_eq!(written, required);
178 Ok(required)
179 }
180
181 #[inline]
187 unsafe fn decode_eof(
188 &mut self,
189 input: &[u8],
190 input_index: usize,
191 ) -> Result<
192 (u8, core::num::NonZeroUsize),
193 qubit_codec::DecodeFailure<Self::DecodeError>,
194 > {
195 debug_assert!(input_index < input.len());
196
197 let (value, consumed) = percent_decode_byte(input, input_index, true)
198 .map_err(|error| match error {
199 ParseError::Incomplete { .. } => {
200 map_misc_decode_failure_with_consumed(
201 MiscCodecError::InvalidEscape {
202 index: input_index,
203 escape: String::from_utf8_lossy(&input[input_index..])
204 .into_owned(),
205 reason: "expected two hexadecimal digits".to_owned(),
206 },
207 percent_invalid_consumed(input, input_index),
208 )
209 }
210 ParseError::Invalid(error) => {
211 map_misc_decode_failure_with_consumed(
212 error,
213 percent_invalid_consumed(input, input_index),
214 )
215 }
216 })?;
217 debug_assert!(consumed > 0);
218 let consumed = qubit_codec::nz!(consumed);
219 Ok((value, consumed))
220 }
221}