use crate::percent_codec::{
percent_decode_byte,
percent_decode_bytes,
percent_encode_byte,
percent_encode_bytes,
percent_encode_len,
percent_invalid_consumed,
};
use crate::{
MiscCodecError,
MiscCodecResult,
internal::{
ParseError,
PercentEncodingMode,
},
misc_codec_error::map_misc_decode_failure_with_consumed,
};
use qubit_codec::{
Codec,
ValueDecoder,
ValueEncoder,
};
#[must_use]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct FormUrlencodedCodec;
impl FormUrlencodedCodec {
#[inline]
pub fn new() -> Self {
Self
}
#[inline]
#[must_use]
pub fn encode(&self, text: &str) -> String {
percent_encode_bytes(
text.as_bytes(),
PercentEncodingMode::FormUrlencoded,
)
}
#[inline]
pub fn decode(&self, text: &str) -> MiscCodecResult<String> {
String::from_utf8(percent_decode_bytes(text, true)?)
.map_err(MiscCodecError::from)
}
}
impl ValueEncoder<str> for FormUrlencodedCodec {
type Error = MiscCodecError;
type Output = String;
#[inline]
fn encode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
Ok(FormUrlencodedCodec::encode(self, input))
}
}
impl ValueDecoder<str> for FormUrlencodedCodec {
type Error = MiscCodecError;
type Output = String;
#[inline]
fn decode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
FormUrlencodedCodec::decode(self, input)
}
}
impl Codec for FormUrlencodedCodec {
type Value = u8;
type Unit = u8;
type DecodeError = MiscCodecError;
type EncodeError = MiscCodecError;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 3;
const MAX_DECODE_UNITS_PER_VALUE: usize = 3;
#[inline(always)]
fn encode_len(&self, value: &u8) -> usize {
percent_encode_len(*value, PercentEncodingMode::FormUrlencoded)
}
#[inline]
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<
(u8, core::num::NonZeroUsize),
qubit_codec::DecodeFailure<Self::DecodeError>,
> {
debug_assert!(input_index < input.len());
let (value, consumed) = percent_decode_byte(input, input_index, true)
.map_err(|error| {
ParseError::into_decode_failure_with_consumed(
error,
qubit_codec::nz!(3),
)
})?;
debug_assert!(consumed > 0);
let consumed = qubit_codec::nz!(consumed);
Ok((value, consumed))
}
#[inline]
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
let required =
percent_encode_len(*value, PercentEncodingMode::FormUrlencoded);
debug_assert!(output_index + required <= output.len());
let written = percent_encode_byte(
*value,
output,
output_index,
PercentEncodingMode::FormUrlencoded,
);
debug_assert_eq!(written, required);
Ok(required)
}
#[inline]
unsafe fn decode_eof(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<
(u8, core::num::NonZeroUsize),
qubit_codec::DecodeFailure<Self::DecodeError>,
> {
debug_assert!(input_index < input.len());
let (value, consumed) = percent_decode_byte(input, input_index, true)
.map_err(|error| match error {
ParseError::Incomplete { .. } => {
map_misc_decode_failure_with_consumed(
MiscCodecError::InvalidEscape {
index: input_index,
escape: String::from_utf8_lossy(&input[input_index..])
.into_owned(),
reason: "expected two hexadecimal digits".to_owned(),
},
percent_invalid_consumed(input, input_index),
)
}
ParseError::Invalid(error) => {
map_misc_decode_failure_with_consumed(
error,
percent_invalid_consumed(input, input_index),
)
}
})?;
debug_assert!(consumed > 0);
let consumed = qubit_codec::nz!(consumed);
Ok((value, consumed))
}
}