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

WIP: experimental arena #4

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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 Cargo.lock

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

9 changes: 7 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ edition = "2018"
license = "MIT"
description = "A simple (incomplete) URI parser"


# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nom = "5.1.1"

[dependencies.bumpalo]
version = "3.2.1"
features = ["collections"]

[dev-dependencies]
criterion = "0.3"

[[bench]]
name = "uri_parse"
harness = false

[profile.release]
debug = true
# [profile.release]
# debug = true
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ In the case of duplicated query string tags the last one wins:
- [x] Ports
- [x] Split up into multiple files
- [x] Domains with .
- [x] Rendering of URIs and Authority with fmt::Display
- [ ] Parsing IPv4, IPv6
- [ ] Parsing fragments
- [ ] Percent encoding and decoding
Expand Down
18 changes: 16 additions & 2 deletions benches/uri_parse.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use auris::parsers::uri;
use auris::URI;
use criterion::{criterion_group, criterion_main, Criterion, Throughput};

fn criterion_benchmark(c: &mut Criterion) {
Expand All @@ -15,5 +14,20 @@ fn criterion_benchmark(c: &mut Criterion) {
});
}

criterion_group!(benches, criterion_benchmark);
fn bench_f(c: &mut Criterion) {
let mut group = c.benchmark_group("My own parser ffuu");

let string = "foo://user:[email protected]";
group.bench_function("parsers::uri", |b| {
b.iter(|| auris::parsers::f(string));
});
}

criterion_group! {
name = benches;
config = Criterion::default();
targets =
criterion_benchmark,
bench_f
}
criterion_main!(benches);
134 changes: 120 additions & 14 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,39 @@
//! - Uses only safe features in rust.
//! - `rfc2396` & `rfc3986` compliant (incomplete)
//!
//!
//! ## Parses structure:
//!
//! ```notrust
//! foo://example.com:8042/over/there?name=ferret#nose
//! \_/ \______________/\_________/ \_________/ \__/
//! | | | | |
//! scheme authority path query fragment
//! ```
//!
//! # Usage
//!
//! ```
//! use auris::parsers;
//! use auris::URI;
//!
//! "postgres://user:password@host".parse::<URI<String>>();
//!
//! parsers::uri("https://crates.io/crates/auris");
//! "https://crates.io/crates/auris".parse::<URI<String>>();
//! ```
//!
//! ## Query strings
//!
//! We also parse query strings into HashMaps:
//!
//! ```
//! # use auris::URI;
//! "postgres://user:[email protected]/db?replication=true".parse::<URI<String>>();
//! ```
//!
//! In the case of duplicated query string tags the last one wins:
//! ```
//! # use auris::URI;
//! "scheme://host/path?a=1&a=2".parse::<URI<String>>();
//! ```
extern crate nom;
use std::str;
Expand All @@ -21,18 +48,47 @@ use std::str::FromStr;

pub mod parsers;

pub enum AurisParseErrorKind {
#[derive(Debug)]
pub enum ParseError {
Failed,
}

pub struct ParseError {
kind: AurisParseErrorKind,
#[derive(Debug)]
pub struct AurisErr {
kind: ParseError,
}

impl fmt::Display for ParseError {
impl fmt::Display for AurisErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.kind {
AurisParseErrorKind::Failed => write!(f, "Parsing failed"),
ParseError::Failed => write!(f, "Parsing failed"),
}
}
}

/// Make impossible authentication states unrepresentable
#[derive(Debug, PartialEq, Eq)]
pub enum UserInfo<T> {
User(T),
UserAndPassword(T, T),
}

impl UserInfo<&str> {
fn to_owned(&self) -> UserInfo<String> {
match self {
UserInfo::User(d) => UserInfo::User((*d).to_string()),
UserInfo::UserAndPassword(u, p) => {
UserInfo::UserAndPassword((*u).to_string(), (*p).to_string())
}
}
}
}

impl fmt::Display for UserInfo<String> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UserInfo::User(user) => write!(f, "{}", user),
UserInfo::UserAndPassword(user, password) => write!(f, "{}:{}", user, password),
}
}
}
Expand All @@ -45,22 +101,52 @@ where
{
//TODO(bradford): IPV6, IPV4, DNS enum
pub host: T,
pub username: Option<T>,
pub password: Option<T>,
pub userinfo: Option<UserInfo<T>>,
pub port: Option<u16>,
}

impl Authority<&str> {
fn to_owned(&self) -> Authority<String> {
Authority {
host: self.host.to_string(),
username: self.username.map(|u| u.to_string()),
password: self.password.map(|p| p.to_string()),
userinfo: self.userinfo.as_ref().map(|u| u.to_owned()),
port: self.port,
}
}
}

/// Converts the URI struct back to a string
///
/// # Examples
/// ```
/// use auris::{Authority, UserInfo};
///
/// assert_eq!("a:[email protected]:443",
/// format!("{}", Authority {
/// host: "bob.com".to_string(),
/// userinfo: Some(UserInfo::UserAndPassword("a".to_string(), "b".to_string())),
/// port: Some(443),
/// }));
/// ```
impl fmt::Display for Authority<String> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut formatted = String::new();
// using a match as this feels cleaner than a map
let userinfo_string = match self.userinfo.as_ref() {
Some(userinfo) => format!("{}@", userinfo),
None => String::new(),
};
formatted.push_str(&userinfo_string);
formatted.push_str(&self.host);
let port_string = match self.port.as_ref() {
Some(port) => format!(":{}", port),
None => String::new(),
};
formatted.push_str(&port_string);
write!(f, "{}", formatted)
}
}

/// URI is the whole URI object
///
/// # Examples
Expand Down Expand Up @@ -102,17 +188,37 @@ impl URI<&str> {
}

impl FromStr for URI<String> {
type Err = ParseError;
type Err = AurisErr;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match parsers::uri(s) {
Ok((_, obj)) => Ok(obj.to_owned()),
Err(_) => Err(ParseError {
kind: AurisParseErrorKind::Failed,
Err(_) => Err(AurisErr {
kind: ParseError::Failed,
}),
}
}
}
/// Converts the URI struct back to a string
///
/// # Examples
/// ```
/// use auris::URI;
///
/// let parsed = "http://bob.com".parse::<URI<String>>().unwrap();
///
/// assert_eq!("http://bob.com",
/// format!("{}", parsed));
/// ```
impl fmt::Display for URI<String> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut formatted = String::new();
formatted.push_str(&self.scheme);
formatted.push_str("://");
formatted.push_str(&format!("{}", self.authority));
write!(f, "{}", formatted)
}
}

// The host name of an URL.
pub enum Host<S = String> {
Expand Down
Loading