qubit_codec_misc/
form_urlencoded_codec.rs1use qubit_codec::Codec;
11use qubit_codec::DecodeFailure;
12use qubit_codec::ValueDecoder;
13use qubit_codec::ValueEncoder;
14use qubit_utils::nonzero;
15
16use crate::MiscCodecError;
17use crate::MiscCodecResult;
18use crate::internal::ParseError;
19use crate::internal::PercentEncodingMode;
20use crate::misc_codec_error::map_misc_decode_failure_with_consumed;
21use crate::percent_codec::percent_decode_byte;
22use crate::percent_codec::percent_decode_bytes;
23use crate::percent_codec::percent_encode_byte;
24use crate::percent_codec::percent_encode_bytes;
25use crate::percent_codec::percent_encode_len;
26use crate::percent_codec::percent_invalid_consumed;
27
28#[must_use]
34#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
35pub struct FormUrlencodedCodec;
36
37impl FormUrlencodedCodec {
38 #[inline]
43 pub fn new() -> Self {
44 Self
45 }
46
47 #[inline]
55 #[must_use]
56 pub fn encode(&self, text: &str) -> String {
57 percent_encode_bytes(
58 text.as_bytes(),
59 PercentEncodingMode::FormUrlencoded,
60 )
61 }
62
63 #[inline]
75 pub fn decode(&self, text: &str) -> MiscCodecResult<String> {
76 String::from_utf8(percent_decode_bytes(text, true)?)
77 .map_err(MiscCodecError::from)
78 }
79}
80
81impl ValueEncoder<str> for FormUrlencodedCodec {
82 type Error = MiscCodecError;
83 type Output = String;
84
85 #[inline]
87 fn encode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
88 Ok(FormUrlencodedCodec::encode(self, input))
89 }
90}
91
92impl ValueDecoder<str> for FormUrlencodedCodec {
93 type Error = MiscCodecError;
94 type Output = String;
95
96 #[inline]
98 fn decode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
99 FormUrlencodedCodec::decode(self, input)
100 }
101}
102
103impl Codec for FormUrlencodedCodec {
104 type Value = u8;
105 type Unit = u8;
106 type DecodeError = MiscCodecError;
107 type EncodeError = MiscCodecError;
108
109 const MIN_UNITS_PER_VALUE: usize = 1;
110 const MAX_ENCODE_UNITS_PER_VALUE: usize = 3;
111
112 const MAX_DECODE_UNITS_PER_VALUE: usize = 3;
113
114 #[inline(always)]
116 fn encode_len(&self, value: &u8) -> usize {
117 percent_encode_len(*value, PercentEncodingMode::FormUrlencoded)
118 }
119
120 #[inline]
126 unsafe fn decode(
127 &mut self,
128 input: &[u8],
129 input_index: usize,
130 ) -> Result<(u8, core::num::NonZeroUsize), DecodeFailure<Self::DecodeError>>
131 {
132 debug_assert!(input_index < input.len());
133
134 let (value, consumed) = percent_decode_byte(input, input_index, true)
135 .map_err(|error| {
136 ParseError::into_decode_failure_with_consumed(error, nonzero(3))
137 })?;
138 debug_assert!(consumed > 0);
139 let consumed = nonzero(consumed);
142 Ok((value, consumed))
143 }
144
145 #[inline]
151 unsafe fn encode(
152 &mut self,
153 value: &u8,
154 output: &mut [u8],
155 output_index: usize,
156 ) -> Result<usize, Self::EncodeError> {
157 let required =
158 percent_encode_len(*value, PercentEncodingMode::FormUrlencoded);
159 debug_assert!(output_index + required <= output.len());
160
161 let written = percent_encode_byte(
162 *value,
163 output,
164 output_index,
165 PercentEncodingMode::FormUrlencoded,
166 );
167 debug_assert_eq!(written, required);
168 Ok(required)
169 }
170
171 #[inline]
177 unsafe fn decode_eof(
178 &mut self,
179 input: &[u8],
180 input_index: usize,
181 ) -> Result<(u8, core::num::NonZeroUsize), DecodeFailure<Self::DecodeError>>
182 {
183 debug_assert!(input_index < input.len());
184
185 let (value, consumed) = percent_decode_byte(input, input_index, true)
186 .map_err(|error| match error {
187 ParseError::Incomplete { .. } => {
188 map_misc_decode_failure_with_consumed(
189 MiscCodecError::InvalidEscape {
190 index: input_index,
191 escape: String::from_utf8_lossy(&input[input_index..])
192 .into_owned(),
193 reason: "expected two hexadecimal digits".to_owned(),
194 },
195 percent_invalid_consumed(input, input_index),
196 )
197 }
198 ParseError::Invalid(error) => {
199 map_misc_decode_failure_with_consumed(
200 error,
201 percent_invalid_consumed(input, input_index),
202 )
203 }
204 })?;
205 debug_assert!(consumed > 0);
206 let consumed = nonzero(consumed);
207 Ok((value, consumed))
208 }
209}