Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document finished features #126

Merged
merged 9 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ jobs:
cargo check --workspace
cargo build --workspace
cargo test --all-targets
cargo test --doc

- name: Show SCCache stats
if: always() && !cancelled()
Expand Down
4 changes: 0 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ suspicious = { level = "deny", priority = -1 }
# Personal Preferences
module_name_repetitions = "allow"

# FIXME: Strict
missing_panics_doc = "allow"
missing_errors_doc = "allow"

[profile.dev.package]
insta.opt-level = 3
similar.opt-level = 3
Expand Down
4 changes: 2 additions & 2 deletions benches/matchit_criterion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ fn benchmark(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("matchit benchmarks");

group.bench_function("matchit benchmarks/wayfind", |bencher| {
let mut router = wayfind::router::Router::new();
let mut router = wayfind::Router::new();
for route in routes!(brackets) {
router.insert(route, true).unwrap();
}

bencher.iter(|| {
for route in black_box(paths()) {
let path = wayfind::path::Path::new(route).unwrap();
let path = wayfind::Path::new(route).unwrap();
let output = black_box(router.search(black_box(&path)).unwrap());
let _parameters: Vec<(&str, &str)> =
black_box(output.parameters.iter().map(|p| (p.key, p.value)).collect());
Expand Down
4 changes: 2 additions & 2 deletions benches/matchit_divan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ fn main() {

#[divan::bench(name = "wayfind")]
fn wayfind(bencher: divan::Bencher) {
let mut router = wayfind::router::Router::new();
let mut router = wayfind::Router::new();
for route in routes!(brackets) {
router.insert(route, true).unwrap();
}

bencher.bench(|| {
for route in black_box(paths()) {
let path = wayfind::path::Path::new(route).unwrap();
let path = wayfind::Path::new(route).unwrap();
let output = black_box(router.search(black_box(&path)).unwrap());
let _parameters: Vec<(&str, &str)> =
black_box(output.parameters.iter().map(|p| (p.key, p.value)).collect());
Expand Down
4 changes: 2 additions & 2 deletions benches/path_tree_criterion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ fn benchmark(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("path-tree benchmarks");

group.bench_function("path-tree benchmarks/wayfind", |bencher| {
let mut router = wayfind::router::Router::new();
let mut router = wayfind::Router::new();
for (index, route) in routes!(brackets).iter().enumerate() {
router.insert(route, index).unwrap();
}

bencher.iter(|| {
for route in black_box(paths()) {
let path = wayfind::path::Path::new(route).unwrap();
let path = wayfind::Path::new(route).unwrap();
let output = black_box(router.search(black_box(&path)).unwrap());
let _parameters: Vec<(&str, &str)> =
black_box(output.parameters.iter().map(|p| (p.key, p.value)).collect());
Expand Down
4 changes: 2 additions & 2 deletions benches/path_tree_divan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ fn main() {

#[divan::bench(name = "wayfind")]
fn wayfind(bencher: divan::Bencher) {
let mut router = wayfind::router::Router::new();
let mut router = wayfind::Router::new();
for (index, route) in routes!(brackets).iter().enumerate() {
router.insert(route, index).unwrap();
}

bencher.bench(|| {
for route in black_box(paths()) {
let path = wayfind::path::Path::new(route).unwrap();
let path = wayfind::Path::new(route).unwrap();
let output = black_box(router.search(black_box(&path)).unwrap());
let _parameters: Vec<(&str, &str)> =
black_box(output.parameters.iter().map(|p| (p.key, p.value)).collect());
Expand Down
2 changes: 1 addition & 1 deletion examples/axum-fork/src/routing/path_router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use axum_core::response::IntoResponse;
use std::{borrow::Cow, collections::HashMap, convert::Infallible, fmt, sync::Arc};
use tower_layer::Layer;
use tower_service::Service;
use wayfind::{errors::insert::InsertError, node::search::Match, path::Path, router::Router};
use wayfind::{errors::InsertError, Match, Path, Router};

use super::{
future::RouteFuture, not_found::NotFound, strip_prefix::StripPrefix, url_params, Endpoint,
Expand Down
2 changes: 1 addition & 1 deletion examples/axum-fork/src/routing/url_params.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::util::PercentDecodedStr;
use http::Extensions;
use std::sync::Arc;
use wayfind::node::search::Parameter;
use wayfind::Parameter;

#[derive(Clone)]
pub(crate) enum UrlParams {
Expand Down
2 changes: 1 addition & 1 deletion examples/hyper/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use std::{
sync::Arc,
};
use tokio::{net::TcpListener, task::JoinSet};
use wayfind::{node::search::Parameter, path::Path, router::Router};
use wayfind::{Parameter, Path, Router};

type BoxFuture<'a> = Pin<
Box<
Expand Down
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
})
sccache
cargo-insta
cargo-watch

# Benchmarking
cargo-codspeed
Expand Down
2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/insert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
let mut router = wayfind::router::Router::new();
let mut router = wayfind::Router::new();
if let Ok(route) = std::str::from_utf8(data) {
let _ = router.insert(route, true);
}
Expand Down
28 changes: 27 additions & 1 deletion src/constraints.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
use std::net::{Ipv4Addr, Ipv6Addr};

pub trait Constraint {
/// A constraint that can be used for custom path routing logic.
///
/// Must be a stateless, static check.
/// In the future, we may support stateful constraints.
///
/// Constraints can be registered within a [`Router`](crate::Router) via the [`constraint`](crate::Router::constraint) function.
///
/// # Example
///
/// ```rust
/// use wayfind::Constraint;
///
/// struct HelloConstraint;
/// impl Constraint for HelloConstraint {
/// const NAME: &'static str = "hello";
///
/// fn check(segment: &str) -> bool {
/// segment == "hello"
/// }
/// }
/// ```
pub trait Constraint: Send + Sync {
/// The name of the constraint.
///
/// Must be unique within a given router.
/// Try and avoid generic constraint names like `id`.
const NAME: &'static str;

/// Checks if a given segment matches this constraint.
fn check(segment: &str) -> bool;
}

Expand Down
4 changes: 3 additions & 1 deletion src/decode.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::errors::decode::DecodeError;
use std::borrow::Cow;

pub(crate) fn percent_decode(input: &[u8]) -> Result<Cow<[u8]>, DecodeError> {
/// Try and percent-decode input bytes.
/// Does not do any sort of normalization, simply decodes hex characters.
pub fn percent_decode(input: &[u8]) -> Result<Cow<[u8]>, DecodeError> {
if !input.contains(&b'%') {
return Ok(Cow::Borrowed(input));
}
Expand Down
23 changes: 18 additions & 5 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
pub mod constraint;
pub mod decode;
pub mod delete;
pub mod insert;
pub mod route;
//! Error types for [`wayfind`](crate).
//!
//! All errors contain a user-friendly display method.

pub(crate) mod constraint;
pub use constraint::ConstraintError;

pub(crate) mod decode;
pub use decode::DecodeError;

pub(crate) mod delete;
pub use delete::DeleteError;

pub(crate) mod insert;
pub use insert::InsertError;

pub(crate) mod route;
pub use route::RouteError;
33 changes: 33 additions & 0 deletions src/errors/constraint.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,43 @@
use std::{error::Error, fmt::Display};

/// Errors relating to constraints.
#[derive(Debug, PartialEq, Eq)]
pub enum ConstraintError {
/// Constraint name is already in use.
///
/// # Examples
///
/// ```rust
/// use wayfind::errors::ConstraintError;
///
/// let error = ConstraintError::DuplicateName {
/// name: "my_constraint",
/// existing_type: "my_crate::constraints::A",
/// new_type: "my_crate::constraints::B",
/// };
///
/// let display = "
/// duplicate constraint name
///
/// The constraint name 'my_constraint' is already in use:
/// - existing constraint type: 'my_crate::constraints::A'
/// - new constraint type: 'my_crate::constraints::B'
///
/// help: each constraint must have a unique name
///
/// try:
/// - Check if you have accidentally added the same constraint twice
/// - Ensure different constraints have different names
/// ";
///
/// assert_eq!(error.to_string(), display.trim());
/// ```
DuplicateName {
/// The name of the constraint.
name: &'static str,
/// The [`type_name`](std::any::type_name) of the already existing constraint.
existing_type: &'static str,
/// The [`type_name`](std::any::type_name) of the attempted new constraint.
new_type: &'static str,
},
}
Expand Down
29 changes: 29 additions & 0 deletions src/errors/decode.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
use std::{error::Error, fmt::Display};

/// Errors relating to percent-decoding failures.
#[derive(Debug, PartialEq, Eq)]
pub enum DecodeError {
/// Invalid percent-encoding sequence encountered.
///
/// # Examples
///
/// ```rust
/// use wayfind::errors::DecodeError;
///
/// let error = DecodeError::InvalidEncoding {
/// input: "/hello%GGworld".to_string(),
/// position: 6,
/// character: [b'%', b'G', b'G'],
/// };
///
/// let display = "
/// invalid percent-encoding
///
/// Input: /hello%GGworld
/// ^^^
///
/// Expected: '%' followed by two hexadecimal digits (a-F, 0-9)
/// Found: '%GG'
/// ";
///
/// assert_eq!(error.to_string(), display.trim());
/// ```
InvalidEncoding {
/// The unaltered input string.
input: String,
/// The position in the input where the invalid encoding was found.
position: usize,
/// The invalid character sequence.
character: [u8; 3],
},
}
Expand Down
29 changes: 28 additions & 1 deletion src/errors/delete.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
use super::route::RouteError;
use std::{error::Error, fmt::Display};

/// Errors relating to attempting to delete a route from a [`Router`](crate::Router).
#[derive(Debug, PartialEq, Eq)]
pub enum DeleteError {
/// A [`RouteError`] that occurred during the delete.
RouteError(RouteError),
NotFound { path: String },

/// Path to be deleted was not found in the router.
///
/// # Examples
///
/// ```rust
/// use wayfind::errors::DeleteError;
///
/// let error = DeleteError::NotFound {
/// path: "/not_found".to_string(),
/// };
///
/// let display = "
/// not found
///
/// Path: /not_found
///
/// The specified path does not exist in the router
/// ";
///
/// assert_eq!(error.to_string(), display.trim());
/// ```
NotFound {
/// The path that was not found in the router.
path: String,
},
}

impl Error for DeleteError {}
Expand Down
Loading