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]
45#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
46pub struct FormUrlencodedCodec;
47
48impl FormUrlencodedCodec {
49 #[inline]
54 pub fn new() -> Self {
55 Self
56 }
57
58 #[inline]
66 #[must_use]
67 pub fn encode(&self, text: &str) -> String {
68 percent_encode_bytes(text.as_bytes(), PercentEncodingMode::FormUrlencoded)
69 }
70
71 #[inline]
83 pub fn decode(&self, text: &str) -> MiscCodecResult<String> {
84 String::from_utf8(percent_decode_bytes(text, true)?).map_err(MiscCodecError::from)
85 }
86}
87
88impl ValueEncoder<str> for FormUrlencodedCodec {
89 type Error = MiscCodecError;
90 type Output = String;
91
92 #[inline]
94 fn encode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
95 Ok(FormUrlencodedCodec::encode(self, input))
96 }
97}
98
99impl ValueDecoder<str> for FormUrlencodedCodec {
100 type Error = MiscCodecError;
101 type Output = String;
102
103 #[inline]
105 fn decode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
106 FormUrlencodedCodec::decode(self, input)
107 }
108}
109
110impl Codec for FormUrlencodedCodec {
111 type Value = u8;
112 type Unit = u8;
113 type DecodeError = MiscCodecError;
114 type EncodeError = MiscCodecError;
115
116 const MIN_UNITS_PER_VALUE: usize = 1;
117 const MAX_ENCODE_UNITS_PER_VALUE: usize = 3;
118
119 const MAX_DECODE_UNITS_PER_VALUE: usize = 3;
120
121 #[inline(always)]
123 fn encode_len(&self, value: &u8) -> usize {
124 percent_encode_len(*value, PercentEncodingMode::FormUrlencoded)
125 }
126
127 #[inline]
133 unsafe fn decode(
134 &mut self,
135 input: &[u8],
136 input_index: usize,
137 ) -> Result<(u8, core::num::NonZeroUsize), DecodeFailure<Self::DecodeError>> {
138 debug_assert!(input_index < input.len());
139
140 let (value, consumed) = percent_decode_byte(input, input_index, true)
141 .map_err(|error| ParseError::into_decode_failure_with_consumed(error, nonzero(3)))?;
142 debug_assert!(consumed > 0);
143 let consumed = nonzero(consumed);
146 Ok((value, consumed))
147 }
148
149 #[inline]
155 unsafe fn encode(
156 &mut self,
157 value: &u8,
158 output: &mut [u8],
159 output_index: usize,
160 ) -> Result<usize, Self::EncodeError> {
161 let required = percent_encode_len(*value, PercentEncodingMode::FormUrlencoded);
162 debug_assert!(output_index + required <= output.len());
163
164 let written = percent_encode_byte(*value, output, output_index, PercentEncodingMode::FormUrlencoded);
165 debug_assert_eq!(written, required);
166 Ok(required)
167 }
168
169 #[inline]
175 unsafe fn decode_eof(
176 &mut self,
177 input: &[u8],
178 input_index: usize,
179 ) -> Result<(u8, core::num::NonZeroUsize), DecodeFailure<Self::DecodeError>> {
180 debug_assert!(input_index < input.len());
181
182 let (value, consumed) = percent_decode_byte(input, input_index, true).map_err(|error| match error {
183 ParseError::Incomplete { .. } => map_misc_decode_failure_with_consumed(
184 MiscCodecError::InvalidEscape {
185 index: input_index,
186 escape: String::from_utf8_lossy(&input[input_index..]).into_owned(),
187 reason: "expected two hexadecimal digits".to_owned(),
188 },
189 percent_invalid_consumed(input, input_index),
190 ),
191 ParseError::Invalid(error) => {
192 map_misc_decode_failure_with_consumed(error, percent_invalid_consumed(input, input_index))
193 }
194 })?;
195 debug_assert!(consumed > 0);
196 let consumed = nonzero(consumed);
197 Ok((value, consumed))
198 }
199}