Skip to content

Commit

Permalink
bump: v0.7.0
Browse files Browse the repository at this point in the history
  • Loading branch information
fundon committed Oct 4, 2022
1 parent dcf20be commit 0480642
Show file tree
Hide file tree
Showing 6 changed files with 428 additions and 458 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "path-tree"
version = "0.6.0"
version = "0.7.0"
authors = ["Fangdun Tsai <[email protected]>"]
description = "path-tree is a lightweight high performance HTTP request router for Rust"
homepage = "https://github.com/viz-rs/path-tree"
Expand All @@ -20,7 +20,7 @@ include = [
]

[dependencies]
smallvec = "1.9.0"
smallvec = "1.10.0"

[dev-dependencies]
actix-router = "0.5"
Expand Down
76 changes: 36 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,32 +108,30 @@ tree.insert("/:org/:repo/:page", 11);
tree.insert("/:org/:repo/*", 12);
tree.insert("/api/+", 13);

let r = tree.find("/").unwrap();
assert_eq!(r.value, &0);
assert_eq!(r.params(), vec![]);
let (h, p) = tree.find("/").unwrap();
assert_eq!(h, &0);
assert_eq!(p.params(), vec![]);

let r = tree.find("/login").unwrap();
assert_eq!(r.value, &1);
assert_eq!(r.params(), vec![]);
let (h, p) = tree.find("/login").unwrap();
assert_eq!(h, &1);
assert_eq!(p.params(), vec![]);

let r = tree.find("/settings/admin").unwrap();
assert_eq!(r.value, &4);
assert_eq!(r.params(), vec![("page", "admin")]);
let (h, p) = tree.find("/settings/admin").unwrap();
assert_eq!(h, &4);
assert_eq!(p.params(), vec![("page", "admin")]);

let r = tree.find("/viz-rs").unwrap();
assert_eq!(r.value, &5);
assert_eq!(r.params(), vec![("user", "viz-rs")]);
let (h, p) = tree.find("/viz-rs").unwrap();
assert_eq!(h, &5);
assert_eq!(p.params(), vec![("user", "viz-rs")]);

let r = tree.find("/viz-rs/path-tree").unwrap();
assert_eq!(r.value, &6);
assert_eq!(r.params(), vec![("user", "viz-rs"), ("repo", "path-tree")]);
let (h, p) = tree.find("/viz-rs/path-tree").unwrap();
assert_eq!(h, &6);
assert_eq!(p.params(), vec![("user", "viz-rs"), ("repo", "path-tree")]);

assert_eq!(tree.url_for(*r.id, &["viz-rs", "viz"]).unwrap(), "/viz-rs/viz");

let r = tree.find("/rust-lang/rust-analyzer/releases/download/2022-09-12/rust-analyzer-aarch64-apple-darwin.gz").unwrap();
assert_eq!(r.value, &8);
let (h, p) = tree.find("/rust-lang/rust-analyzer/releases/download/2022-09-12/rust-analyzer-aarch64-apple-darwin.gz").unwrap();
assert_eq!(h, &8);
assert_eq!(
r.params(),
p.params(),
vec![
("org", "rust-lang"),
("repo", "rust-analyzer"),
Expand All @@ -143,10 +141,10 @@ assert_eq!(
]
);

let r = tree.find("/rust-lang/rust-analyzer/tags/2022-09-12").unwrap();
assert_eq!(r.value, &9);
let (h, p) = tree.find("/rust-lang/rust-analyzer/tags/2022-09-12").unwrap();
assert_eq!(h, &9);
assert_eq!(
r.params(),
p.params(),
vec![
("org", "rust-lang"),
("repo", "rust-analyzer"),
Expand All @@ -156,10 +154,10 @@ assert_eq!(
]
);

let r = tree.find("/rust-lang/rust-analyzer/actions/ci:bench").unwrap();
assert_eq!(r.value, &10);
let (h, p) = tree.find("/rust-lang/rust-analyzer/actions/ci:bench").unwrap();
assert_eq!(h, &10);
assert_eq!(
r.params(),
p.params(),
vec![
("org", "rust-lang"),
("repo", "rust-analyzer"),
Expand All @@ -168,23 +166,21 @@ assert_eq!(
]
);

let r = tree.find("/rust-lang/rust-analyzer/stargazers").unwrap();
assert_eq!(r.value, &11);
assert_eq!(r.params(), vec![("org", "rust-lang"), ("repo", "rust-analyzer"), ("page", "stargazers")]);

let r = tree.find("/rust-lang/rust-analyzer/stargazers/404").unwrap();
assert_eq!(r.value, &12);
assert_eq!(r.params(), vec![("org", "rust-lang"), ("repo", "rust-analyzer"), ("*1", "stargazers/404")]);
let (h, p) = tree.find("/rust-lang/rust-analyzer/stargazers").unwrap();
assert_eq!(h, &11);
assert_eq!(p.params(), vec![("org", "rust-lang"), ("repo", "rust-analyzer"), ("page", "stargazers")]);

let r = tree.find("/public/js/main.js").unwrap();
assert_eq!(r.value, &7);
assert_eq!(r.params(), vec![("any", "js/main.js")]);
let (h, p) = tree.find("/rust-lang/rust-analyzer/stargazers/404").unwrap();
assert_eq!(h, &12);
assert_eq!(p.params(), vec![("org", "rust-lang"), ("repo", "rust-analyzer"), ("*1", "stargazers/404")]);

let r = tree.find("/api/v1").unwrap();
assert_eq!(r.value, &13);
assert_eq!(r.params(), vec![("+1", "v1")]);
let (h, p) = tree.find("/public/js/main.js").unwrap();
assert_eq!(h, &7);
assert_eq!(p.params(), vec![("any", "js/main.js")]);

assert_eq!(tree.url_for(*r.id, &["repos/viz-rs"]).unwrap(), "/api/repos/viz-rs");
let (h, p) = tree.find("/api/v1").unwrap();
assert_eq!(h, &13);
assert_eq!(p.params(), vec![("+1", "v1")]);
```

Hyper hello example can be found [here](examples/hello.rs).
Expand Down
2 changes: 1 addition & 1 deletion benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ fn bench_path_find(c: &mut Criterion) {
b.iter(|| {
for (i, r) in ROUTES_URLS.iter().enumerate() {
let n = tree.find(r).unwrap();
assert_eq!(*n.value, i);
assert_eq!(*n.0, i);
}
})
})
Expand Down
4 changes: 2 additions & 2 deletions examples/hello.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {

async move {
Ok::<_, Infallible>(match router.find(&path) {
Some(route) => {
Some((handler, route)) => {
let p = route
.params()
.iter()
.map(|p| (p.0.to_string(), p.1.to_string()))
.collect::<Params>();
req.extensions_mut().insert(p);
route.value.call(req).await
handler.call(req).await
}
None => Response::builder()
.status(StatusCode::NOT_FOUND)
Expand Down
145 changes: 60 additions & 85 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,30 +57,30 @@
//! tree.insert("/:org/:repo/*", 12);
//! tree.insert("/api/+", 13);
//!
//! let r = tree.find("/").unwrap();
//! assert_eq!(r.value, &0);
//! assert_eq!(r.params(), vec![]);
//! let (h, p) = tree.find("/").unwrap();
//! assert_eq!(h, &0);
//! assert_eq!(p.params(), vec![]);
//!
//! let r = tree.find("/login").unwrap();
//! assert_eq!(r.value, &1);
//! assert_eq!(r.params(), vec![]);
//! let (h, p) = tree.find("/login").unwrap();
//! assert_eq!(h, &1);
//! assert_eq!(p.params(), vec![]);
//!
//! let r = tree.find("/settings/admin").unwrap();
//! assert_eq!(r.value, &4);
//! assert_eq!(r.params(), vec![("page", "admin")]);
//! let (h, p) = tree.find("/settings/admin").unwrap();
//! assert_eq!(h, &4);
//! assert_eq!(p.params(), vec![("page", "admin")]);
//!
//! let r = tree.find("/viz-rs").unwrap();
//! assert_eq!(r.value, &5);
//! assert_eq!(r.params(), vec![("user", "viz-rs")]);
//! let (h, p) = tree.find("/viz-rs").unwrap();
//! assert_eq!(h, &5);
//! assert_eq!(p.params(), vec![("user", "viz-rs")]);
//!
//! let r = tree.find("/viz-rs/path-tree").unwrap();
//! assert_eq!(r.value, &6);
//! assert_eq!(r.params(), vec![("user", "viz-rs"), ("repo", "path-tree")]);
//! let (h, p) = tree.find("/viz-rs/path-tree").unwrap();
//! assert_eq!(h, &6);
//! assert_eq!(p.params(), vec![("user", "viz-rs"), ("repo", "path-tree")]);
//!
//! let r = tree.find("/rust-lang/rust-analyzer/releases/download/2022-09-12/rust-analyzer-aarch64-apple-darwin.gz").unwrap();
//! assert_eq!(r.value, &8);
//! let (h, p) = tree.find("/rust-lang/rust-analyzer/releases/download/2022-09-12/rust-analyzer-aarch64-apple-darwin.gz").unwrap();
//! assert_eq!(h, &8);
//! assert_eq!(
//! r.params(),
//! p.params(),
//! vec![
//! ("org", "rust-lang"),
//! ("repo", "rust-analyzer"),
Expand All @@ -90,10 +90,10 @@
//! ]
//! );
//!
//! let r = tree.find("/rust-lang/rust-analyzer/tags/2022-09-12").unwrap();
//! assert_eq!(r.value, &9);
//! let (h, p) = tree.find("/rust-lang/rust-analyzer/tags/2022-09-12").unwrap();
//! assert_eq!(h, &9);
//! assert_eq!(
//! r.params(),
//! p.params(),
//! vec![
//! ("org", "rust-lang"),
//! ("repo", "rust-analyzer"),
Expand All @@ -103,10 +103,10 @@
//! ]
//! );
//!
//! let r = tree.find("/rust-lang/rust-analyzer/actions/ci:bench").unwrap();
//! assert_eq!(r.value, &10);
//! let (h, p) = tree.find("/rust-lang/rust-analyzer/actions/ci:bench").unwrap();
//! assert_eq!(h, &10);
//! assert_eq!(
//! r.params(),
//! p.params(),
//! vec![
//! ("org", "rust-lang"),
//! ("repo", "rust-analyzer"),
Expand All @@ -115,21 +115,21 @@
//! ]
//! );
//!
//! let r = tree.find("/rust-lang/rust-analyzer/stargazers").unwrap();
//! assert_eq!(r.value, &11);
//! assert_eq!(r.params(), vec![("org", "rust-lang"), ("repo", "rust-analyzer"), ("page", "stargazers")]);
//! let (h, p) = tree.find("/rust-lang/rust-analyzer/stargazers").unwrap();
//! assert_eq!(h, &11);
//! assert_eq!(p.params(), vec![("org", "rust-lang"), ("repo", "rust-analyzer"), ("page", "stargazers")]);
//!
//! let r = tree.find("/rust-lang/rust-analyzer/stargazers/404").unwrap();
//! assert_eq!(r.value, &12);
//! assert_eq!(r.params(), vec![("org", "rust-lang"), ("repo", "rust-analyzer"), ("*1", "stargazers/404")]);
//! let (h, p) = tree.find("/rust-lang/rust-analyzer/stargazers/404").unwrap();
//! assert_eq!(h, &12);
//! assert_eq!(p.params(), vec![("org", "rust-lang"), ("repo", "rust-analyzer"), ("*1", "stargazers/404")]);
//!
//! let r = tree.find("/public/js/main.js").unwrap();
//! assert_eq!(r.value, &7);
//! assert_eq!(r.params(), vec![("any", "js/main.js")]);
//! let (h, p) = tree.find("/public/js/main.js").unwrap();
//! assert_eq!(h, &7);
//! assert_eq!(p.params(), vec![("any", "js/main.js")]);
//!
//! let r = tree.find("/api/v1").unwrap();
//! assert_eq!(r.value, &13);
//! assert_eq!(r.params(), vec![("+1", "v1")]);
//! let (h, p) = tree.find("/api/v1").unwrap();
//! assert_eq!(h, &13);
//! assert_eq!(p.params(), vec![("+1", "v1")]);
//! ```
#![no_std]
Expand All @@ -142,12 +142,7 @@ use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::{
iter::{Copied, FilterMap, Zip},
marker::PhantomData,
slice::Iter,
str::from_utf8,
};
use core::str::from_utf8;

use smallvec::SmallVec;

Expand Down Expand Up @@ -212,21 +207,23 @@ impl<T> PathTree<T> {
}

/// Returns the [Path] by the given path.
pub fn find<'b>(&self, path: &'b str) -> Option<Path<'_, 'b, T>> {
pub fn find<'a, 'b>(&'a self, path: &'b str) -> Option<(&T, Path<'a, 'b>)> {
let bytes = path.as_bytes();
self.node.find(bytes).and_then(|(id, ranges)| {
self.get_route(*id).map(|(value, pieces)| {
Path {
id,
(
value,
pieces,
// opt!
raws: ranges
.into_iter()
.filter_map(|r| from_utf8(&bytes[r]).ok())
.rev()
.collect(),
}
Path {
id,
pieces,
// opt!
raws: ranges
.into_iter()
.filter_map(|r| from_utf8(&bytes[r]).ok())
.rev()
.collect(),
},
)
})
})
}
Expand Down Expand Up @@ -260,15 +257,14 @@ impl<T> PathTree<T> {
}

/// Matched route path infomation.
#[derive(Debug, PartialEq, Eq)]
pub struct Path<'a, 'b, T> {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Path<'a, 'b> {
pub id: &'a usize,
pub value: &'a T,
pub pieces: &'a [Piece],
pub raws: SmallVec<[&'b str; 4]>,
}

impl<'a, 'b, T> Path<'a, 'b, T> {
impl<'a, 'b> Path<'a, 'b> {
/// Gets current path pattern.
pub fn pattern(&self) -> String {
let mut bytes = Vec::new();
Expand Down Expand Up @@ -312,14 +308,14 @@ impl<'a, 'b, T> Path<'a, 'b, T> {
}

/// Returns the parameters of the current path.
pub fn params(&self) -> Vec<(&'a str, &'b str)> {
pub fn params(&self) -> Vec<(&str, &str)> {
self.params_iter().collect()
}

/// Returns the parameters iterator of the current path.
pub fn params_iter<'p>(&'p self) -> ParamsIter<'p, 'a, 'b, T> {
pub fn params_iter(&self) -> impl Iterator<Item = (&str, &str)> {
#[inline]
fn piece_filter(piece: &Piece) -> Option<&'_ str> {
fn piece_filter(piece: &Piece) -> Option<&str> {
match piece {
Piece::String(_) => None,
Piece::Parameter(p, _) => from_utf8(match p {
Expand All @@ -329,30 +325,9 @@ impl<'a, 'b, T> Path<'a, 'b, T> {
}
}

ParamsIter {
iter: self
.pieces
.iter()
.filter_map(piece_filter as fn(piece: &'a Piece) -> Option<&'a str>)
.zip(self.raws.iter().copied()),
_t: PhantomData,
}
}
}

type FilterIter<'a> = FilterMap<Iter<'a, Piece>, fn(piece: &'a Piece) -> Option<&'a str>>;

/// A Parameters Iterator.
pub struct ParamsIter<'p, 'a, 'b, T> {
iter: Zip<FilterIter<'a>, Copied<Iter<'p, &'b str>>>,
_t: PhantomData<T>,
}

impl<'p, 'a, 'b, T> Iterator for ParamsIter<'p, 'a, 'b, T> {
type Item = (&'a str, &'b str);

#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
self.pieces
.iter()
.filter_map(piece_filter)
.zip(self.raws.iter().copied())
}
}
Loading

0 comments on commit 0480642

Please sign in to comment.