Skip to content

Commit

Permalink
Extend path search struct to work as request builder.
Browse files Browse the repository at this point in the history
  • Loading branch information
CathalMullan committed Nov 15, 2024
1 parent 560de66 commit 987f1bc
Show file tree
Hide file tree
Showing 22 changed files with 417 additions and 340 deletions.
74 changes: 48 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Dynamic parameters are greedy in nature, similar to a regex `.*`, and will attem

```rust
use std::error::Error;
use wayfind::{Path, Router, RoutableBuilder};
use wayfind::{Router, RoutableBuilder, RequestBuilder};

fn main() -> Result<(), Box<dyn Error>> {
let mut router = Router::new();
Expand All @@ -58,14 +58,18 @@ fn main() -> Result<(), Box<dyn Error>> {
.build()?;
router.insert(&route, 2)?;

let path = Path::new("/users/123")?;
let search = router.search(&path)?.unwrap();
let request = RequestBuilder::new()
.path("/users/123")
.build()?;
let search = router.search(&request)?.unwrap();
assert_eq!(*search.data, 1);
assert_eq!(search.route, "/users/{id}");
assert_eq!(search.parameters[0], ("id", "123"));

let path = Path::new("/users/123/files/my.document.pdf")?;
let search = router.search(&path)?.unwrap();
let request = RequestBuilder::new()
.path("/users/123/files/my.document.pdf")
.build()?;
let search = router.search(&request)?.unwrap();
assert_eq!(*search.data, 2);
assert_eq!(search.route, "/users/{id}/files/{filename}.{extension}");
assert_eq!(search.parameters[0], ("id", "123"));
Expand All @@ -91,7 +95,7 @@ Like dynamic parameters, wildcard parameters are also greedy in nature.

```rust
use std::error::Error;
use wayfind::{Path, Router, RoutableBuilder};
use wayfind::{Router, RoutableBuilder, RequestBuilder};

fn main() -> Result<(), Box<dyn Error>> {
let mut router = Router::new();
Expand All @@ -106,14 +110,18 @@ fn main() -> Result<(), Box<dyn Error>> {
.build()?;
router.insert(&route, 2)?;

let path = Path::new("/files/documents/reports/annual.pdf/delete")?;
let search = router.search(&path)?.unwrap();
let request = RequestBuilder::new()
.path("/files/documents/reports/annual.pdf/delete")
.build()?;
let search = router.search(&request)?.unwrap();
assert_eq!(*search.data, 1);
assert_eq!(search.route, "/files/{*slug}/delete");
assert_eq!(search.parameters[0], ("slug", "documents/reports/annual.pdf"));

let path = Path::new("/any/other/path")?;
let search = router.search(&path)?.unwrap();
let request = RequestBuilder::new()
.path("/any/other/path")
.build()?;
let search = router.search(&request)?.unwrap();
assert_eq!(*search.data, 2);
assert_eq!(search.route, "/{*catch_all}");
assert_eq!(search.parameters[0], ("catch_all", "any/other/path"));
Expand Down Expand Up @@ -144,7 +152,7 @@ There is a small overhead to using optional groups, due to `Arc` usage internall

```rust
use std::error::Error;
use wayfind::{Path, Router, RoutableBuilder};
use wayfind::{Router, RoutableBuilder, RequestBuilder};

fn main() -> Result<(), Box<dyn Error>> {
let mut router = Router::new();
Expand All @@ -159,30 +167,38 @@ fn main() -> Result<(), Box<dyn Error>> {
.build()?;
router.insert(&route, 2)?;

let path = Path::new("/users")?;
let search = router.search(&path)?.unwrap();
let request = RequestBuilder::new()
.path("/users")
.build()?;
let search = router.search(&request)?.unwrap();
assert_eq!(*search.data, 1);
assert_eq!(search.route, "/users(/{id})");
assert_eq!(search.expanded, Some("/users"));

let path = Path::new("/users/123")?;
let search = router.search(&path)?.unwrap();
let request = RequestBuilder::new()
.path("/users/123")
.build()?;
let search = router.search(&request)?.unwrap();
assert_eq!(*search.data, 1);
assert_eq!(search.route, "/users(/{id})");
assert_eq!(search.expanded, Some("/users/{id}"));
assert_eq!(search.parameters[0], ("id", "123"));

let path = Path::new("/files/documents/folder/report.pdf")?;
let search = router.search(&path)?.unwrap();
let request = RequestBuilder::new()
.path("/files/documents/folder/report.pdf")
.build()?;
let search = router.search(&request)?.unwrap();
assert_eq!(*search.data, 2);
assert_eq!(search.route, "/files/{*slug}/{file}(.{extension})");
assert_eq!(search.expanded, Some("/files/{*slug}/{file}.{extension}"));
assert_eq!(search.parameters[0], ("slug", "documents/folder"));
assert_eq!(search.parameters[1], ("file", "report"));
assert_eq!(search.parameters[2], ("extension", "pdf"));

let path = Path::new("/files/documents/folder/readme")?;
let search = router.search(&path)?.unwrap();
let request = RequestBuilder::new()
.path("/files/documents/folder/readme")
.build()?;
let search = router.search(&request)?.unwrap();
assert_eq!(*search.data, 2);
assert_eq!(search.route, "/files/{*slug}/{file}(.{extension})");
assert_eq!(search.expanded, Some("/files/{*slug}/{file}"));
Expand Down Expand Up @@ -244,7 +260,7 @@ Curently, these can't be disabled.

```rust
use std::error::Error;
use wayfind::{Constraint, Path, Router, RoutableBuilder};
use wayfind::{Constraint, Router, RoutableBuilder, RequestBuilder};

struct NamespaceConstraint;
impl Constraint for NamespaceConstraint {
Expand Down Expand Up @@ -273,21 +289,27 @@ fn main() -> Result<(), Box<dyn Error>> {
.build()?;
router.insert(&route, 2)?;

let path = Path::new("/v2")?;
let search = router.search(&path)?.unwrap();
let request = RequestBuilder::new()
.path("/v2")
.build()?;
let search = router.search(&request)?.unwrap();
assert_eq!(*search.data, 1);
assert_eq!(search.route, "/v2");

let path = Path::new("/v2/my-org/my-repo/blobs/sha256:1234567890")?;
let search = router.search(&path)?.unwrap();
let request = RequestBuilder::new()
.path("/v2/my-org/my-repo/blobs/sha256:1234567890")
.build()?;
let search = router.search(&request)?.unwrap();
assert_eq!(*search.data, 2);
assert_eq!(search.route, "/v2/{*name:namespace}/blobs/{type}:{digest}");
assert_eq!(search.parameters[0], ("name", "my-org/my-repo"));
assert_eq!(search.parameters[1], ("type", "sha256"));
assert_eq!(search.parameters[2], ("digest", "1234567890"));

let path = Path::new("/v2/invalid repo/blobs/uploads")?;
assert!(router.search(&path)?.is_none());
let request = RequestBuilder::new()
.path("/v2/invalid repo/blobs/uploads")
.build()?;
assert!(router.search(&request)?.is_none());

Ok(())
}
Expand Down
7 changes: 5 additions & 2 deletions benches/matchit_criterion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ fn matchit_benchmark(criterion: &mut Criterion) {

bencher.iter(|| {
for route in black_box(paths()) {
let path = wayfind::Path::new(route).unwrap();
let output = black_box(router.search(black_box(&path)).unwrap().unwrap());
let request = wayfind::RequestBuilder::new()
.path(route)
.build()
.unwrap();
let output = black_box(router.search(black_box(&request)).unwrap().unwrap());
let _parameters: Vec<(&str, &str)> =
black_box(output.parameters.iter().map(|p| (p.0, p.1)).collect());
}
Expand Down
7 changes: 5 additions & 2 deletions benches/matchit_divan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ fn wayfind(bencher: divan::Bencher<'_, '_>) {

bencher.bench(|| {
for route in black_box(paths()) {
let path = wayfind::Path::new(route).unwrap();
let output = black_box(router.search(black_box(&path)).unwrap().unwrap());
let request = wayfind::RequestBuilder::new()
.path(route)
.build()
.unwrap();
let output = black_box(router.search(black_box(&request)).unwrap().unwrap());
let _parameters: Vec<(&str, &str)> =
black_box(output.parameters.iter().map(|p| (p.0, p.1)).collect());
}
Expand Down
7 changes: 5 additions & 2 deletions benches/path_tree_criterion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ fn path_tree_benchmark(criterion: &mut Criterion) {

bencher.iter(|| {
for route in black_box(paths()) {
let path = wayfind::Path::new(route).unwrap();
let output = black_box(router.search(black_box(&path)).unwrap().unwrap());
let request = wayfind::RequestBuilder::new()
.path(route)
.build()
.unwrap();
let output = black_box(router.search(black_box(&request)).unwrap().unwrap());
let _parameters: Vec<(&str, &str)> =
black_box(output.parameters.iter().map(|p| (p.0, p.1)).collect());
}
Expand Down
7 changes: 5 additions & 2 deletions benches/path_tree_divan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ fn wayfind(bencher: divan::Bencher<'_, '_>) {

bencher.bench(|| {
for route in black_box(paths()) {
let path = wayfind::Path::new(route).unwrap();
let output = black_box(router.search(black_box(&path)).unwrap().unwrap());
let request = wayfind::RequestBuilder::new()
.path(route)
.build()
.unwrap();
let output = black_box(router.search(black_box(&request)).unwrap().unwrap());
let _parameters: Vec<(&str, &str)> =
black_box(output.parameters.iter().map(|p| (p.0, p.1)).collect());
}
Expand Down
2 changes: 1 addition & 1 deletion examples/oci/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl<'r> AppRouter<'r> {
let method = req.method();
let path = req.uri().path().to_owned();

let Ok(path) = wayfind::Path::new(&path) else {
let Ok(path) = wayfind::RequestBuilder::new().path(&path).build() else {
return Response::builder()
.status(StatusCode::NOT_FOUND)
.body(Full::new(Bytes::from("Not Found")))
Expand Down
10 changes: 6 additions & 4 deletions fuzz/fuzz_targets/e2e.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
#![no_main]

use libfuzzer_sys::fuzz_target;
use wayfind::{Path, RoutableBuilder, Router};
use wayfind::{RoutableBuilder, Router, RequestBuilder};

fuzz_target!(|data: &[u8]| {
let mut router = Router::new();
if let Ok(route) = std::str::from_utf8(data) {
let routable = RoutableBuilder::new().route(route).build().unwrap();
let _ = router.insert(&routable, true);
if let Ok(path) = Path::new(route) {
if let Ok(route) = RoutableBuilder::new().route(route).build() {
let _ = router.insert(&route, true);
}

if let Ok(path) = RequestBuilder::new().path(route).build() {
let _ = router.search(&path);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ pub use encoding::EncodingError;
pub(crate) mod insert;
pub use insert::InsertError;

pub(crate) mod path;
pub use path::PathError;
pub(crate) mod request;
pub use request::RequestError;

pub(crate) mod routable;
pub use routable::RoutableError;
Expand Down
25 changes: 0 additions & 25 deletions src/errors/path.rs

This file was deleted.

50 changes: 50 additions & 0 deletions src/errors/request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use super::EncodingError;
use core::{error::Error, fmt::Display};

/// Errors that can occur when creating a [`Request`](`crate::Request`).
#[derive(Debug, PartialEq, Eq)]
pub enum RequestError {
/// A [`EncodingError`] that occurred during the decoding.
EncodingError(EncodingError),

/// The path was not provided when building the [`Request`](`crate::Request`).
///
/// # Examples
///
/// ```rust
/// use wayfind::errors::RequestError;
///
/// let error = RequestError::MissingPath;
///
/// let display = "
/// missing path
///
/// A path must be provided when building a Request
/// ";
///
/// assert_eq!(error.to_string(), display.trim());
/// ```
MissingPath,
}

impl Error for RequestError {}

impl Display for RequestError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::EncodingError(error) => error.fmt(f),
Self::MissingPath => write!(
f,
r#"missing path
A path must be provided when building a Request"#
),
}
}
}

impl From<EncodingError> for RequestError {
fn from(error: EncodingError) -> Self {
Self::EncodingError(error)
}
}
1 change: 1 addition & 0 deletions src/errors/routable.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::EncodingError;
use core::{error::Error, fmt::Display};

// FIXME: Rename to route after moving existing route error to pathroute?
/// Errors that can occur when creating a [`Routable`](`crate::Routable`).
#[derive(Debug, PartialEq, Eq)]
pub enum RoutableError {
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ pub mod errors;

pub(crate) mod node;

pub(crate) mod path;
pub use path::Path;

pub(crate) mod parser;

pub(crate) mod routable;
pub use routable::{Routable, RoutableBuilder};

pub(crate) mod request;
pub use request::{Request, RequestBuilder};

pub(crate) mod router;
pub use router::{Match, Parameters, Router};

Expand Down
Loading

0 comments on commit 987f1bc

Please sign in to comment.