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(text.as_bytes(), PercentEncodingMode::FormUrlencoded)
58 }
59
60 #[inline]
72 pub fn decode(&self, text: &str) -> MiscCodecResult<String> {
73 String::from_utf8(percent_decode_bytes(text, true)?).map_err(MiscCodecError::from)
74 }
75}
76
77impl ValueEncoder<str> for FormUrlencodedCodec {
78 type Error = MiscCodecError;
79 type Output = String;
80
81 #[inline]
83 fn encode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
84 Ok(FormUrlencodedCodec::encode(self, input))
85 }
86}
87
88impl ValueDecoder<str> for FormUrlencodedCodec {
89 type Error = MiscCodecError;
90 type Output = String;
91
92 #[inline]
94 fn decode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
95 FormUrlencodedCodec::decode(self, input)
96 }
97}
98
99impl Codec for FormUrlencodedCodec {
100 type Value = u8;
101 type Unit = u8;
102 type DecodeError = MiscCodecError;
103 type EncodeError = MiscCodecError;
104
105 const MIN_UNITS_PER_VALUE: usize = 1;
106 const MAX_ENCODE_UNITS_PER_VALUE: usize = 3;
107
108 const MAX_DECODE_UNITS_PER_VALUE: usize = 3;
109
110 #[inline(always)]
112 fn encode_len(&self, value: &u8) -> usize {
113 percent_encode_len(*value, PercentEncodingMode::FormUrlencoded)
114 }
115
116 #[inline]
122 unsafe fn decode(
123 &mut self,
124 input: &[u8],
125 input_index: usize,
126 ) -> Result<(u8, core::num::NonZeroUsize), DecodeFailure<Self::DecodeError>> {
127 debug_assert!(input_index < input.len());
128
129 let (value, consumed) = percent_decode_byte(input, input_index, true)
130 .map_err(|error| ParseError::into_decode_failure_with_consumed(error, nonzero(3)))?;
131 debug_assert!(consumed > 0);
132 let consumed = nonzero(consumed);
135 Ok((value, consumed))
136 }
137
138 #[inline]
144 unsafe fn encode(
145 &mut self,
146 value: &u8,
147 output: &mut [u8],
148 output_index: usize,
149 ) -> Result<usize, Self::EncodeError> {
150 let required = percent_encode_len(*value, PercentEncodingMode::FormUrlencoded);
151 debug_assert!(output_index + required <= output.len());
152
153 let written = percent_encode_byte(
154 *value,
155 output,
156 output_index,
157 PercentEncodingMode::FormUrlencoded,
158 );
159 debug_assert_eq!(written, required);
160 Ok(required)
161 }
162
163 #[inline]
169 unsafe fn decode_eof(
170 &mut self,
171 input: &[u8],
172 input_index: usize,
173 ) -> Result<(u8, core::num::NonZeroUsize), DecodeFailure<Self::DecodeError>> {
174 debug_assert!(input_index < input.len());
175
176 let (value, consumed) =
177 percent_decode_byte(input, input_index, true).map_err(|error| match error {
178 ParseError::Incomplete { .. } => map_misc_decode_failure_with_consumed(
179 MiscCodecError::InvalidEscape {
180 index: input_index,
181 escape: String::from_utf8_lossy(&input[input_index..]).into_owned(),
182 reason: "expected two hexadecimal digits".to_owned(),
183 },
184 percent_invalid_consumed(input, input_index),
185 ),
186 ParseError::Invalid(error) => map_misc_decode_failure_with_consumed(
187 error,
188 percent_invalid_consumed(input, input_index),
189 ),
190 })?;
191 debug_assert!(consumed > 0);
192 let consumed = nonzero(consumed);
193 Ok((value, consumed))
194 }
195}