Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • codecraft/webserver
  • sorcaMriete/webserver
  • opnonmicgo/webserver
  • loposuezo/webserver
  • diufeYmike/webserver
  • contbuspecmi/webserver
  • mogamuboun/webserver
  • glabalwelre/webserver
8 results
Show changes
Commits on Source (5)
Showing
with 168 additions and 164 deletions
...@@ -20,7 +20,6 @@ git clone https://edugit.org/codecraft/webserver ...@@ -20,7 +20,6 @@ git clone https://edugit.org/codecraft/webserver
``` ```
cd webserver cd webserver
git checkout new
cd site cd site
cargo run cargo run
``` ```
......
/target
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "codegen"
version = "0.1.0"
[package]
name = "codegen"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
/target
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "html"
version = "0.1.0"
[package]
name = "html"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
[package] [package]
name = "http" name = "http"
version = "0.1.0" version = "1.0.0"
edition = "2021" edition = "2021"
[features] [features]
default = []
secure = [] secure = []
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
......
use std::{io, path::PathBuf}; use std::{io, path::PathBuf, ops::Deref};
use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader, AsyncWrite, AsyncRead}; use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader, AsyncWrite, AsyncRead};
...@@ -8,8 +8,7 @@ use crate::{handling::{ ...@@ -8,8 +8,7 @@ use crate::{handling::{
request::Request, request::Request,
response::{Outcome, Response, ResponseBody, Status}, response::{Outcome, Response, ResponseBody, Status},
routes::{Body, Data}, routes::{Body, Data},
}, utils::urlencoded::UrlEncodeData}; }, setup::MountPoint};
use crate::setup::MountPoint;
/// The Maximal size of the Body of an HTTP-Message in bytes /// The Maximal size of the Body of an HTTP-Message in bytes
static MAX_HTTP_MESSAGE_SIZE: u16 = 4196; static MAX_HTTP_MESSAGE_SIZE: u16 = 4196;
...@@ -25,7 +24,7 @@ static MAX_HTTP_MESSAGE_SIZE: u16 = 4196; ...@@ -25,7 +24,7 @@ static MAX_HTTP_MESSAGE_SIZE: u16 = 4196;
/// ///
/// # Panics /// # Panics
/// No Panics /// No Panics
pub async fn handle_connection<T: AsyncRead + AsyncWrite + std::marker::Unpin>(mut stream: T, mountpoints: Vec<MountPoint<'_>>) { pub async fn handle_connection<T: AsyncRead + AsyncWrite + std::marker::Unpin>(mut stream: T, mountpoints: Vec<MountPoint>) {
let mut buf_reader = BufReader::new(&mut stream); let mut buf_reader = BufReader::new(&mut stream);
let mut http_request: Vec<String> = Vec::with_capacity(10); let mut http_request: Vec<String> = Vec::with_capacity(10);
loop { loop {
...@@ -49,7 +48,7 @@ pub async fn handle_connection<T: AsyncRead + AsyncWrite + std::marker::Unpin>(m ...@@ -49,7 +48,7 @@ pub async fn handle_connection<T: AsyncRead + AsyncWrite + std::marker::Unpin>(m
let mut request = Request { let mut request = Request {
uri: if let Some(uri) = &request_status_line.split(' ').nth(1) { uri: if let Some(uri) = &request_status_line.split(' ').nth(1) {
if let Ok(uri) = UrlEncodeData::from_encoded(uri) { if let Ok(uri) = uri.deref().try_into() {
uri uri
} else { } else {
eprintln!("\x1b[31mAborting due to invalid uri\x1b[0m"); eprintln!("\x1b[31mAborting due to invalid uri\x1b[0m");
...@@ -132,29 +131,19 @@ pub async fn handle_connection<T: AsyncRead + AsyncWrite + std::marker::Unpin>(m ...@@ -132,29 +131,19 @@ pub async fn handle_connection<T: AsyncRead + AsyncWrite + std::marker::Unpin>(m
let mut handled_response: Option<Outcome<Response, Status, Data>> = None; let mut handled_response: Option<Outcome<Response, Status, Data>> = None;
for mountpoint in mountpoints { for mountpoint in mountpoints {
if request.uri.raw_string().is_none() { if !mountpoint.compare_with_uri(&mut request.uri) {
return;
}
if !request.uri.raw_string().unwrap().starts_with(mountpoint.mountpoint) {
continue; continue;
} }
let mounted_request_uri = request.uri.raw_string().unwrap().strip_prefix(mountpoint.mountpoint).unwrap();
for route in mountpoint.routes { for route in mountpoint.routes {
if (route.method != request.method) if (route.method != request.method)
&& ((route.method != Method::Get) && (request.method == Method::Head)) && ((route.method != Method::Get) && (request.method == Method::Head))
{ {
continue; continue;
} }
if !route.compare_uri(mounted_request_uri) { if !route.uri.compare_uri(&request.uri) {
continue; continue;
} }
handled_response = Some((route.handler)( handled_response = Some((route.handler)(request.clone(), data.clone()));
Request {
uri: UrlEncodeData::from_raw(mounted_request_uri),
..request.clone()
},
data.clone(),
));
if let Some(Outcome::Forward(_)) = handled_response { if let Some(Outcome::Forward(_)) = handled_response {
continue; continue;
......
...@@ -2,7 +2,7 @@ use std::{collections::HashMap, error::Error, fmt::Display}; ...@@ -2,7 +2,7 @@ use std::{collections::HashMap, error::Error, fmt::Display};
use crate::{ use crate::{
handling::methods::Method, handling::methods::Method,
utils::{mime::Mime, urlencoded::UrlEncodeData}, utils::{mime::Mime, url_utils::Uri},
}; };
type HeaderMap = Vec<String>; type HeaderMap = Vec<String>;
...@@ -12,7 +12,7 @@ type HeaderMap = Vec<String>; ...@@ -12,7 +12,7 @@ type HeaderMap = Vec<String>;
#[derive(Clone)] #[derive(Clone)]
pub struct Request { pub struct Request {
/// The requested Uri /// The requested Uri
pub uri: UrlEncodeData, pub uri: Uri,
/// All headers of the request that haven't been parsed /// All headers of the request that haven't been parsed
pub headers: HeaderMap, pub headers: HeaderMap,
/// The methods Request represented with the [Method] /// The methods Request represented with the [Method]
......
...@@ -66,10 +66,15 @@ impl Request { ...@@ -66,10 +66,15 @@ impl Request {
&'a self, &'a self,
keys: &'a [&str], keys: &'a [&str],
) -> Result<HashMap<&str, Result<&str, ParseFormError>>, ParseFormError> { ) -> Result<HashMap<&str, Result<&str, ParseFormError>>, ParseFormError> {
let Some(uri) = self.uri.raw_string() else { let data = if let Some(val) = self
return Err(ParseFormError { error: ParseErrors::BadData }); .uri
}; .parts()
let data = if let Some(val) = uri.split_once('?') { .last()
.unwrap()
.raw_string()
.unwrap()
.split_once('?')
{
val val
} else { } else {
return Err(ParseFormError { return Err(ParseFormError {
...@@ -249,17 +254,14 @@ mod test { ...@@ -249,17 +254,14 @@ mod test {
request::{datatypes::ParseErrors, ParseFormError}, request::{datatypes::ParseErrors, ParseFormError},
routes::Data, routes::Data,
}, },
utils::{ utils::mime::Mime::{ApplicationXWwwFormUrlencoded, MultipartFormData},
mime::Mime::{ApplicationXWwwFormUrlencoded, MultipartFormData},
urlencoded::UrlEncodeData,
},
}; };
use super::Request; use super::Request;
#[test] #[test]
fn try_get_test() { fn try_get_test() {
let request = Request { let request = Request {
uri: UrlEncodeData::from_encoded("/form?name=Name&age=Age").unwrap(), uri: "/form?name=Name&age=Age".try_into().unwrap(),
headers: vec![], headers: vec![],
method: Method::Get, method: Method::Get,
cookies: None, cookies: None,
...@@ -270,7 +272,7 @@ mod test { ...@@ -270,7 +272,7 @@ mod test {
assert_eq!(&"Age", right.get("age").unwrap().as_ref().unwrap()); assert_eq!(&"Age", right.get("age").unwrap().as_ref().unwrap());
let wrong_request = Request { let wrong_request = Request {
uri: UrlEncodeData::from_encoded("/form").unwrap(), uri: "/form".try_into().unwrap(),
..request.clone() ..request.clone()
}; };
assert_eq!( assert_eq!(
...@@ -281,7 +283,7 @@ mod test { ...@@ -281,7 +283,7 @@ mod test {
); );
let bad_data = Request { let bad_data = Request {
uri: UrlEncodeData::from_encoded("/form?age=").unwrap(), uri: "/form?age=".try_into().unwrap(),
..request.clone() ..request.clone()
}; };
let wrong = bad_data.get_get_form_keys(&["name", "age"]).unwrap(); let wrong = bad_data.get_get_form_keys(&["name", "age"]).unwrap();
...@@ -297,7 +299,7 @@ mod test { ...@@ -297,7 +299,7 @@ mod test {
#[test] #[test]
fn try_post_text() { fn try_post_text() {
let req = Request { let req = Request {
uri: UrlEncodeData::from_encoded("").unwrap(), uri: "".try_into().unwrap(),
headers: vec!["Content-Type: application/x-www-form-urlencoded".to_string()], headers: vec!["Content-Type: application/x-www-form-urlencoded".to_string()],
method: Method::Post, method: Method::Post,
cookies: None, cookies: None,
...@@ -317,7 +319,7 @@ mod test { ...@@ -317,7 +319,7 @@ mod test {
map.get("message1").unwrap().as_ref().unwrap() map.get("message1").unwrap().as_ref().unwrap()
); );
let req = Request { let req = Request {
uri: UrlEncodeData::from_encoded("").unwrap(), uri: "".try_into().unwrap(),
headers: vec!["Content-Type: multipart/form-data; boundary=\"boundary\"".to_string()], headers: vec!["Content-Type: multipart/form-data; boundary=\"boundary\"".to_string()],
method: Method::Post, method: Method::Post,
cookies: None, cookies: None,
......
...@@ -69,15 +69,16 @@ impl Request { ...@@ -69,15 +69,16 @@ impl Request {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{ use crate::{
handling::{methods::Method, request::Request}, handling::{methods::Method, request::Request},
utils::{mime::Mime, urlencoded::UrlEncodeData}, utils::mime::Mime,
}; };
#[test] #[test]
pub fn test_mime_parse_from_header_vec() { pub fn test_mime_parse_from_header_vec() {
let mut request = Request { let mut request = Request {
uri: UrlEncodeData::from_raw("thing"), uri: "thing".try_into().unwrap(),
headers: vec![ headers: vec![
"GET / 23".to_string(), "GET / 23".to_string(),
"SDF:LKJSD:F".to_string(), "SDF:LKJSD:F".to_string(),
...@@ -90,7 +91,7 @@ mod test { ...@@ -90,7 +91,7 @@ mod test {
}; };
let mut wrong = Request { let mut wrong = Request {
uri: UrlEncodeData::from_raw("thing"), uri: "thing".try_into().unwrap(),
headers: vec![ headers: vec![
"GET / 23".to_string(), "GET / 23".to_string(),
"SDF:LKJSD:F".to_string(), "SDF:LKJSD:F".to_string(),
......
...@@ -4,7 +4,7 @@ use crate::{ ...@@ -4,7 +4,7 @@ use crate::{
request::{MediaType, Request}, request::{MediaType, Request},
response::{Outcome, Response, Status}, response::{Outcome, Response, Status},
}, },
utils::mime::Mime, utils::{mime::Mime, url_utils::RawUri},
}; };
/// A RouteBuilder struct /// A RouteBuilder struct
...@@ -39,8 +39,8 @@ pub struct RoutBuilder { ...@@ -39,8 +39,8 @@ pub struct RoutBuilder {
} }
/// A struct to define Routes on the Server /// A struct to define Routes on the Server
#[derive(Clone, Copy)] #[derive(Clone)]
pub struct Route<'a> { pub struct Route {
/// An optional name of the route /// An optional name of the route
pub name: Option<&'static str>, pub name: Option<&'static str>,
/// The [Method] via which the route is accesable /// The [Method] via which the route is accesable
...@@ -55,7 +55,7 @@ pub struct Route<'a> { ...@@ -55,7 +55,7 @@ pub struct Route<'a> {
/// // All Information after this sequence is irrelvent /// // All Information after this sequence is irrelvent
/// // Matches: /a, /a/b/c ... /// // Matches: /a, /a/b/c ...
/// ``` /// ```
pub uri: Uri<'a>, pub uri: RawUri,
/// The Handler function for this route, which gets called when the request need the route. /// The Handler function for this route, which gets called when the request need the route.
/// Inputs to the function are an [Request] and the [Data] which represents the body of the /// Inputs to the function are an [Request] and the [Data] which represents the body of the
/// [Request]. The Outcome is expected to be an [Outcome], which is a [Response], A [Status] if /// [Request]. The Outcome is expected to be an [Outcome], which is a [Response], A [Status] if
...@@ -69,7 +69,7 @@ pub struct Route<'a> { ...@@ -69,7 +69,7 @@ pub struct Route<'a> {
pub format: Option<MediaType>, pub format: Option<MediaType>,
} }
impl Route<'_> { impl Route {
/// generates a Route from a Routebuilder /// generates a Route from a Routebuilder
//TODO: ranking //TODO: ranking
pub fn from(routeinfo: RoutBuilder) -> Self { pub fn from(routeinfo: RoutBuilder) -> Self {
...@@ -77,39 +77,18 @@ impl Route<'_> { ...@@ -77,39 +77,18 @@ impl Route<'_> {
Route { Route {
name: routeinfo.name, name: routeinfo.name,
method: routeinfo.method, method: routeinfo.method,
uri: routeinfo.path, uri: routeinfo
.path
.try_into()
.unwrap_or_else(|_| panic!("Incorrect RawUri for path {}", routeinfo.path)),
handler: routeinfo.handler, handler: routeinfo.handler,
rank, rank,
format: routeinfo.format, format: routeinfo.format,
} }
} }
/// Matches a [Request] Uri with a [Route] Uri. Respecting special cases like `?` and `<a..>`
pub fn compare_uri(&self, uri: Uri) -> bool {
let mut iter_comp_str = uri.split('/');
for true_str in self.uri.split('/') {
let comp_str = if let Some(str) = iter_comp_str.next() {
str
} else {
return false;
};
if (true_str.starts_with('<') && true_str.ends_with("..>"))
|| (comp_str.starts_with(true_str) && comp_str.contains('?'))
{
return true;
}
if true_str.starts_with('<') && true_str.ends_with('>') {
continue;
}
if true_str != comp_str {
return false;
}
}
true
}
} }
/// Alias for using a &'a str for Uri /// Alias for using a &'a str for Uri
pub type Uri<'a> = &'a str;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
/// A basic Body type for respones /// A basic Body type for respones
......
...@@ -11,7 +11,7 @@ use tokio_native_tls::{native_tls::{Identity, self}, TlsAcceptor}; ...@@ -11,7 +11,7 @@ use tokio_native_tls::{native_tls::{Identity, self}, TlsAcceptor};
use crate::{ use crate::{
handlers::handler::handle_connection, handlers::handler::handle_connection,
handling::routes::{Route, Uri}, handling::routes::Route, utils::url_utils::Uri,
}; };
#[cfg(feature = "secure")] #[cfg(feature = "secure")]
use crate::handling::response::{Response, Status}; use crate::handling::response::{Response, Status};
...@@ -19,19 +19,19 @@ use crate::handling::response::{Response, Status}; ...@@ -19,19 +19,19 @@ use crate::handling::response::{Response, Status};
#[derive(Clone)] #[derive(Clone)]
/// Represnts a [MountPoint] that can be mounted in the config /// Represnts a [MountPoint] that can be mounted in the config
pub struct MountPoint<'a> { pub struct MountPoint {
/// The prefix of the [MountPoint] /// The prefix of the [MountPoint]
pub mountpoint: Uri<'a>, pub mountpoint: Uri,
/// All Routes mounted on the [MountPoint]. The Routes are all prefixed by the mountpoints /// All Routes mounted on the [MountPoint]. The Routes are all prefixed by the mountpoints
/// mountpoint /// mountpoint
pub routes: Vec<Route<'a>>, pub routes: Vec<Route>,
} }
/// A server configuration that is run /// A server configuration that is run
pub struct Config { pub struct Config {
/// Contains an Optional [`Vec<MountPoint>`]. which contains all [MountPoint]s on the Server. /// Contains an Optional [`Vec<MountPoint>`]. which contains all [MountPoint]s on the Server.
/// Which contain all the [Route]s /// Which contain all the [Route]s
mountpoints: Option<Vec<MountPoint<'static>>>, mountpoints: Option<Vec<MountPoint>>,
/// Contains a [tokio::net::TcpListener] that is bound for the server /// Contains a [tokio::net::TcpListener] that is bound for the server
address: TcpListener, address: TcpListener,
#[cfg(feature = "secure")] #[cfg(feature = "secure")]
...@@ -40,13 +40,14 @@ pub struct Config { ...@@ -40,13 +40,14 @@ pub struct Config {
tls_acceptor: TlsAcceptor, tls_acceptor: TlsAcceptor,
} }
impl<'a> Config { impl<'a> Config {
/// Utility that checks if the given mointpoint is already taken. takes in the uri of the to be /// Utility that checks if the given mointpoint is already taken. takes in the uri of the to be
/// mounted mountpoint /// mounted mountpoint
fn check_mountpoint_taken(&self, to_insert: Uri) -> bool { fn check_mountpoint_taken(&self, to_insert: &Uri) -> bool {
if let Some(to_check) = &self.mountpoints { if let Some(to_check) = &self.mountpoints {
for i in to_check.iter() { for i in to_check.iter() {
if i.mountpoint == to_insert { if i.mountpoint == *to_insert {
return true; // Found a duplicate &str return true; // Found a duplicate &str
} }
} }
...@@ -55,13 +56,13 @@ impl<'a> Config { ...@@ -55,13 +56,13 @@ impl<'a> Config {
} }
/// mounts a [MountPoint] on the [Config] takes in a blank [MountPoint] and the [Route]s to be /// mounts a [MountPoint] on the [Config] takes in a blank [MountPoint] and the [Route]s to be
/// mounted, mounts them and inserts the new [MountPoint]. Also sorts by rank. /// mounted, mounts them and inserts the new [MountPoint]. Also sorts by rank.
pub fn mount(mut self, mountpoint: Uri<'static>, mut routes: Vec<Route<'static>>) -> Self { pub fn mount(mut self, mountpoint: Uri, mut routes: Vec<Route>) -> Self {
if self.check_mountpoint_taken(mountpoint) { if self.check_mountpoint_taken(&mountpoint) {
eprintln!("\x1b[31mTrying to reassign a mountpoint, mountpoint `{mountpoint}` already taken.\x1b[0m"); eprintln!("\x1b[31mTrying to reassign a mountpoint, mountpoint `{}` already taken.\x1b[0m", mountpoint);
return self; return self;
} }
routes.sort_by(|a, b| a.rank.cmp(&b.rank)); routes.sort_by(|a, b| a.rank.cmp(&b.rank));
let mut mount_message = format!(" >> \x1b[35m{}\x1b[0m\n", mountpoint); let mut mount_message = format!(" >> \x1b[35m{}\x1b[0m\n", mountpoint.to_pretty_print_string());
for (index, route) in routes.iter().enumerate() { for (index, route) in routes.iter().enumerate() {
let indent_sign = match index { let indent_sign = match index {
i if i == routes.len() - 1 => "└─", i if i == routes.len() - 1 => "└─",
...@@ -69,11 +70,10 @@ impl<'a> Config { ...@@ -69,11 +70,10 @@ impl<'a> Config {
}; };
mount_message += &format!( mount_message += &format!(
" \x1b[35m{indent_sign}\x1b[0m \x1b[36m(\x1b[0m{}\x1b[36m)\x1b[0m \x1b[32m{}\x1b[0m \x1b[34;4m{}\x1b[24m{}\x1b[0m\n", " \x1b[35m{indent_sign}\x1b[0m \x1b[36m(\x1b[0m{}\x1b[36m)\x1b[0m \x1b[32m{}\x1b[0m {}\n",
route.name.unwrap_or(""), route.name.unwrap_or(""),
route.method, route.method,
mountpoint, route.uri.to_pretty_print_string(&mountpoint)
route.uri
) )
} }
......
pub mod mime; pub mod mime;
pub mod url_utils; pub mod url_utils;
pub mod urlencoded; pub mod urlencoded;
pub mod vec_utils;
...@@ -5,16 +5,17 @@ use crate::utils::urlencoded::UrlEncodeData; ...@@ -5,16 +5,17 @@ use crate::utils::urlencoded::UrlEncodeData;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Uri { pub struct Uri {
pub(super) parts: Vec<UrlEncodeData>, pub(super) parts: Vec<UrlEncodeData>,
pub raw: String,
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct RawUri { pub struct RawUri {
pub(super) raw_string: String, pub(super) raw_string: String,
pub(super) infinte_end: bool, pub(super) infinte_end: bool,
pub(super) parts: Vec<RawUriElement>, pub(super) parts: Vec<RawUriElement>,
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub enum RawUriElement { pub enum RawUriElement {
Variable, Variable,
Name(UrlEncodeData), Name(UrlEncodeData),
......
use std::str::FromStr; use std::str::FromStr;
use crate::utils::{url_utils::datatypes::RawUriElement, urlencoded::UrlEncodeData}; use crate::{
setup::MountPoint,
utils::{
url_utils::datatypes::RawUriElement,
urlencoded::{EnCodable, UrlEncodeData},
vec_utils::remove_n,
},
};
use super::datatypes::{ParseUriError, RawUri, Uri, UriError}; use super::datatypes::{ParseUriError, RawUri, Uri};
impl MountPoint {
pub fn compare_with_uri(&self, uri: &mut Uri) -> bool {
if !uri.raw.starts_with(&self.mountpoint.raw) {
return false;
}
let to_remove_after_finish = self.mountpoint.parts.len();
remove_n(uri.mut_parts(), to_remove_after_finish);
true
}
}
impl Uri { impl Uri {
pub fn new(parts: Vec<&str>) -> Self { pub fn new(parts: Vec<&str>) -> Self {
Self { Self {
raw: "/".to_owned()
+ &parts
.iter()
.map(|part| part.encode())
.collect::<Vec<String>>()
.join("/"),
parts: parts.into_iter().map(UrlEncodeData::from_raw).collect(), parts: parts.into_iter().map(UrlEncodeData::from_raw).collect(),
} }
} }
pub fn parts(&self) -> &[UrlEncodeData] {
self.parts.as_slice()
}
pub fn mut_parts(&mut self) -> &mut Vec<UrlEncodeData> {
self.parts.as_mut()
}
pub fn compare(&self, uri: &Uri) -> bool {
let mut uri_iter = uri.parts.iter();
for part in self.parts.iter() {
let Some(part_uri) = uri_iter.next() else {
return false;
};
if part != part_uri {
return false;
}
}
true
}
pub(crate) fn to_pretty_print_string(&self) -> String {
if self.parts.is_empty() {
"/".to_string()
} else {
let url = self
.parts
.iter()
.map(|part| part.encoded())
.collect::<Vec<_>>();
"/".to_string() + &url.join("/")
}
}
} }
impl RawUri { impl RawUri {
...@@ -17,7 +71,7 @@ impl RawUri { ...@@ -17,7 +71,7 @@ impl RawUri {
let mut result = Self { let mut result = Self {
infinte_end: false, infinte_end: false,
parts: Vec::with_capacity(parts.len()), parts: Vec::with_capacity(parts.len()),
raw_string: "/".to_owned() + &parts.join("/"), raw_string: parts.join("/"),
}; };
for part in parts { for part in parts {
if part.starts_with('<') && part.ends_with("..>") { if part.starts_with('<') && part.ends_with("..>") {
...@@ -34,15 +88,15 @@ impl RawUri { ...@@ -34,15 +88,15 @@ impl RawUri {
} }
result result
} }
pub fn compare_uri(self, uri: Uri) -> bool { pub fn compare_uri(&self, uri: &Uri) -> bool {
let mut iter_comp = uri.parts.iter(); let mut iter_comp = uri.parts.iter();
let mut counter = 0; if uri.parts().len() != self.parts.len() && !self.infinte_end {
return false;
}
for element in self.parts.iter() { for element in self.parts.iter() {
counter += 1;
let Some(compare_element) = iter_comp.next() else { let Some(compare_element) = iter_comp.next() else {
return false; return false;
}; };
if *element == RawUriElement::Variable { if *element == RawUriElement::Variable {
continue; continue;
} }
...@@ -54,11 +108,19 @@ impl RawUri { ...@@ -54,11 +108,19 @@ impl RawUri {
return false; return false;
} }
} }
if counter > self.parts.len() && !self.infinte_end { if uri.parts.len() > self.parts.len() && self.infinte_end {
return false; return true;
} }
true true
} }
pub(crate) fn to_pretty_print_string(&self, is_after: &Uri) -> String {
let is_after = is_after.to_pretty_print_string();
if is_after == "/" {
format!("\x1b[34;4m/\x1b[24m{}\x1b[0m", self)
} else {
format!("\x1b[34;4m{is_after}/\x1b[24m{self}\x1b[0m")
}
}
} }
impl std::fmt::Display for RawUri { impl std::fmt::Display for RawUri {
...@@ -69,12 +131,16 @@ impl std::fmt::Display for RawUri { ...@@ -69,12 +131,16 @@ impl std::fmt::Display for RawUri {
impl std::fmt::Display for Uri { impl std::fmt::Display for Uri {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let url = self if self.parts.is_empty() {
.parts write!(f, "/")
.iter() } else {
.map(|part| part.encoded()) let url = self
.collect::<Vec<_>>(); .parts
write!(f, "/{}", url.join("/")) .iter()
.map(|part| part.encoded())
.collect::<Vec<_>>();
write!(f, "{}", url.join("/"))
}
} }
} }
...@@ -93,19 +159,21 @@ impl FromStr for Uri { ...@@ -93,19 +159,21 @@ impl FromStr for Uri {
UrlEncodeData::from_raw(sub) UrlEncodeData::from_raw(sub)
}); });
} }
Ok(Self { parts: result }) Ok(Self {
parts: result,
raw: s.to_string(),
})
} }
} }
impl FromStr for RawUri { impl FromStr for RawUri {
type Err = UriError; type Err = ParseUriError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts = s.split('/').collect::<Vec<&str>>(); let parts = s.split('/').collect::<Vec<&str>>();
let mut result = Self { let mut result = Self {
infinte_end: false, infinte_end: false,
parts: Vec::new(), parts: Vec::new(),
raw_string: parts.join("/"), raw_string: s.to_string(),
}; };
for part in parts { for part in parts {
if part.is_empty() { if part.is_empty() {
...@@ -127,6 +195,21 @@ impl FromStr for RawUri { ...@@ -127,6 +195,21 @@ impl FromStr for RawUri {
} }
} }
impl TryFrom<&str> for Uri {
type Error = ParseUriError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::from_str(value)
}
}
impl TryFrom<&str> for RawUri {
type Error = ParseUriError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::from_str(value)
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::utils::{ use crate::utils::{
...@@ -139,7 +222,7 @@ mod test { ...@@ -139,7 +222,7 @@ mod test {
#[test] #[test]
fn uri_to_string() { fn uri_to_string() {
assert_eq!("/a/%20", Uri::new(vec!["a", " "]).to_string()); assert_eq!("a/%20", Uri::new(vec!["a", " "]).to_string());
} }
#[test] #[test]
......
pub(crate) fn remove_n<T>(vec: &mut Vec<T>, n: usize) {
if n > vec.len() {
return;
}
for i in 0..n {
vec.remove(i);
}
}