Skip to content

Commit

Permalink
Manually implement decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
CathalMullan committed Aug 17, 2024
1 parent 7646cf7 commit 73398c3
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 17 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,6 @@ categories.workspace = true
[lints]
workspace = true

[dependencies]
percent-encoding = "2.3"

[dev-dependencies]
# Snapshots
# NOTE: Keep in sync with `cargo-insta` Nix package.
Expand Down
39 changes: 39 additions & 0 deletions src/decode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use crate::errors::decode::DecodeError;

pub(crate) fn percent_decode(input: &[u8]) -> Result<Vec<u8>, DecodeError> {
if !input.contains(&b'%') {
return Ok(input.to_vec());
}

let mut output = Vec::with_capacity(input.len());

let mut i = 0;
let len = input.len();

while i < len {
if input[i] == b'%' && i + 2 < len {
let a = input[i + 1];
let b = input[i + 2];
output.push(decode_hex(a, b)?);
i += 3;
} else {
output.push(input[i]);
i += 1;
}
}

Ok(output)
}

#[allow(clippy::cast_possible_truncation)]
fn decode_hex(a: u8, b: u8) -> Result<u8, DecodeError> {
let a = (a as char)
.to_digit(16)
.ok_or(DecodeError::InvalidEncoding)?;

let b = (b as char)
.to_digit(16)
.ok_or(DecodeError::InvalidEncoding)?;

Ok((a as u8) << 4 | (b as u8))
}
1 change: 1 addition & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod constraint;
pub mod decode;
pub mod delete;
pub mod insert;
pub mod route;
Expand Down
24 changes: 24 additions & 0 deletions src/errors/decode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use std::{error::Error, fmt::Display, str::Utf8Error};

#[derive(Debug, PartialEq, Eq)]
pub enum DecodeError {
Utf8Error(Utf8Error),
InvalidEncoding,
}

impl Error for DecodeError {}

impl Display for DecodeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Utf8Error(error) => error.fmt(f),
Self::InvalidEncoding => write!(f, "Invalid Encoding"),
}
}
}

impl From<Utf8Error> for DecodeError {
fn from(error: Utf8Error) -> Self {
Self::Utf8Error(error)
}
}
10 changes: 10 additions & 0 deletions src/errors/search.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::{error::Error, fmt::Display, str::Utf8Error};

use super::decode::DecodeError;

#[derive(Debug, PartialEq, Eq)]
pub enum SearchError {
Utf8Error(Utf8Error),
DecodeError(DecodeError),
}

impl Error for SearchError {}
Expand All @@ -11,6 +14,7 @@ impl Display for SearchError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Utf8Error(error) => error.fmt(f),
Self::DecodeError(error) => error.fmt(f),
}
}
}
Expand All @@ -20,3 +24,9 @@ impl From<Utf8Error> for SearchError {
Self::Utf8Error(error)
}
}

impl From<DecodeError> for SearchError {
fn from(error: DecodeError) -> Self {
Self::DecodeError(error)
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod constraints;
pub mod decode;
pub mod errors;
pub mod node;
pub mod parts;
Expand Down
26 changes: 13 additions & 13 deletions src/router.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::{
constraints::Constraint,
decode::percent_decode,
errors::{
constraint::ConstraintError, delete::DeleteError, insert::InsertError, search::SearchError,
},
node::{Node, NodeData, NodeKind},
parts::{Part, Parts},
};
use percent_encoding::percent_decode_str;
use std::{
collections::{hash_map::Entry, HashMap},
fmt::Display,
Expand Down Expand Up @@ -143,16 +143,16 @@ impl<T> Router<T> {
}

pub fn search<'a>(&'a self, path: &str) -> Result<Option<Match<'a, T>>, SearchError> {
let decoded_path = if self.percent_encoding {
percent_decode_str(path).decode_utf8()?.into_owned()
let path_bytes = if self.percent_encoding {
&percent_decode(path.as_bytes())?
} else {
path.to_string()
path.as_bytes()
};

let mut parameters = vec![];
let Some(node) =
self.root
.search(decoded_path.as_bytes(), &mut parameters, &self.constraints)
let Some(node) = self
.root
.search(path_bytes, &mut parameters, &self.constraints)
else {
return Ok(None);
};
Expand All @@ -164,16 +164,16 @@ impl<T> Router<T> {
let parameters = parameters
.into_iter()
.map(|raw| {
let key = std::str::from_utf8(raw.key)?.to_string();
let value = if self.percent_encoding {
percent_decode_str(std::str::from_utf8(raw.value)?)
.decode_utf8()?
.into_owned()
&percent_decode(raw.value)?
} else {
std::str::from_utf8(raw.value)?.to_string()
raw.value
};

Ok(Parameter { key, value })
Ok(Parameter {
key: std::str::from_utf8(raw.key)?.to_string(),
value: std::str::from_utf8(value)?.to_string(),
})
})
.collect::<Result<Vec<_>, SearchError>>()?;

Expand Down

0 comments on commit 73398c3

Please sign in to comment.