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]
48#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
49pub struct FormUrlencodedCodec;
50
51impl FormUrlencodedCodec {
52 #[inline]
57 pub fn new() -> Self {
58 Self
59 }
60
61 #[inline]
69 #[must_use]
70 pub fn encode(&self, text: &str) -> String {
71 percent_encode_bytes(text.as_bytes(), PercentEncodingMode::FormUrlencoded)
72 }
73
74 #[inline]
86 pub fn decode(&self, text: &str) -> MiscCodecResult<String> {
87 String::from_utf8(percent_decode_bytes(text, true)?).map_err(MiscCodecError::from)
88 }
89}
90
91impl ValueEncoder<str> for FormUrlencodedCodec {
92 type Error = MiscCodecError;
93 type Output = String;
94
95 #[inline]
97 fn encode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
98 Ok(FormUrlencodedCodec::encode(self, input))
99 }
100}
101
102impl ValueDecoder<str> for FormUrlencodedCodec {
103 type Error = MiscCodecError;
104 type Output = String;
105
106 #[inline]
108 fn decode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
109 FormUrlencodedCodec::decode(self, input)
110 }
111}
112
113impl Codec for FormUrlencodedCodec {
114 type Value = u8;
115 type Unit = u8;
116 type DecodeError = MiscCodecError;
117 type EncodeError = MiscCodecError;
118
119 const MIN_UNITS_PER_VALUE: usize = 1;
120 const MAX_ENCODE_UNITS_PER_VALUE: usize = 3;
121
122 const MAX_DECODE_UNITS_PER_VALUE: usize = 3;
123
124 #[inline]
126 fn encode_len(&self, value: &u8) -> usize {
127 percent_encode_len(*value, PercentEncodingMode::FormUrlencoded)
128 }
129
130 #[inline]
136 unsafe fn decode(
137 &mut self,
138 input: &[u8],
139 input_index: usize,
140 ) -> Result<(u8, core::num::NonZeroUsize), DecodeFailure<Self::DecodeError>> {
141 debug_assert!(input_index < input.len());
142
143 let (value, consumed) = percent_decode_byte(input, input_index, true).map_err(|error| {
144 ParseError::into_decode_failure_with_consumed(error, percent_invalid_consumed(input, input_index))
145 })?;
146 debug_assert!(consumed > 0);
147 let consumed = nonzero(consumed);
150 Ok((value, consumed))
151 }
152
153 #[inline]
159 unsafe fn encode(
160 &mut self,
161 value: &u8,
162 output: &mut [u8],
163 output_index: usize,
164 ) -> Result<usize, Self::EncodeError> {
165 let required = percent_encode_len(*value, PercentEncodingMode::FormUrlencoded);
166 debug_assert!(output_index + required <= output.len());
167
168 let written = percent_encode_byte(*value, output, output_index, PercentEncodingMode::FormUrlencoded);
169 debug_assert_eq!(written, required);
170 Ok(required)
171 }
172
173 #[inline]
179 unsafe fn decode_eof(
180 &mut self,
181 input: &[u8],
182 input_index: usize,
183 ) -> Result<(u8, core::num::NonZeroUsize), DecodeFailure<Self::DecodeError>> {
184 debug_assert!(input_index < input.len());
185
186 let (value, consumed) = percent_decode_byte(input, input_index, true).map_err(|error| match error {
187 ParseError::Incomplete { .. } => map_misc_decode_failure_with_consumed(
188 MiscCodecError::InvalidEscape {
189 index: input_index,
190 escape: String::from_utf8_lossy(&input[input_index..]).into_owned(),
191 reason: "expected two hexadecimal digits".to_owned(),
192 },
193 percent_invalid_consumed(input, input_index),
194 ),
195 ParseError::Invalid(error) => {
196 map_misc_decode_failure_with_consumed(error, percent_invalid_consumed(input, input_index))
197 }
198 })?;
199 debug_assert!(consumed > 0);
200 let consumed = nonzero(consumed);
201 Ok((value, consumed))
202 }
203}