Created
April 12, 2022 11:48
-
-
Save sibu-github/6ea23742ccd6f691de149cb7f829f57a to your computer and use it in GitHub Desktop.
URL Parse
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// URL Parser | |
#![allow(dead_code, unused_imports)] | |
use std::io; | |
use std::num::ParseIntError; | |
use std::str::FromStr; | |
#[derive(Debug, PartialEq, Eq)] | |
enum CustomError { | |
ParseError, | |
MalformedURLError, | |
} | |
impl From<ParseIntError> for CustomError { | |
fn from(_: ParseIntError) -> Self { | |
Self::ParseError | |
} | |
} | |
#[derive(Debug)] | |
#[allow(dead_code)] | |
struct URL { | |
scheme: String, | |
host: String, | |
port: Option<u32>, | |
paths: Vec<String>, | |
query_params: Vec<(String, String)>, | |
} | |
impl FromStr for URL { | |
type Err = CustomError; | |
fn from_str(value: &str) -> Result<Self, Self::Err> { | |
let value = value.trim(); | |
let idx_scheme_sep = value.find("://").ok_or(CustomError::MalformedURLError)?; | |
let scheme = &value[..idx_scheme_sep]; | |
let value = &value[idx_scheme_sep + 3..]; | |
let idx_qp_sep = value.find("?"); | |
let (host_and_paths, qparams) = match idx_qp_sep { | |
Some(i) => (&value[..i], &value[i + 1..]), | |
None => (value, ""), | |
}; | |
let splitted = host_and_paths.split('/').collect::<Vec<_>>(); | |
if splitted.len() < 1 { | |
return Err(CustomError::MalformedURLError); | |
} | |
let host = splitted[0]; | |
let idx_port_sep = host.find(":"); | |
let (host, port) = match idx_port_sep { | |
None => (host, None), | |
Some(i) => { | |
let h = &host[..i]; | |
let p = &host[i + 1..]; | |
let p = p.parse::<u32>()?; | |
(h, Some(p)) | |
} | |
}; | |
let paths = splitted | |
.iter() | |
.skip(1) | |
.map(|s| s.to_string()) | |
.collect::<Vec<_>>(); | |
let query_params = { | |
qparams | |
.split("&") | |
.map(|s| { | |
if let Some(idx) = s.find("=") { | |
let s1 = &s[..idx]; | |
let s2 = &s[idx + 1..]; | |
(s1.to_owned(), s2.to_owned()) | |
} else { | |
(s.to_string(), "".to_string()) | |
} | |
}) | |
.collect::<Vec<_>>() | |
}; | |
Ok(URL { | |
scheme: String::from(scheme), | |
host: String::from(host), | |
port, | |
paths, | |
query_params, | |
}) | |
} | |
} | |
fn main() { | |
println!("Please enter the URL to be parsed"); | |
// let mut input = String::new(); | |
// io::stdin().read_line(&mut input).unwrap(); | |
// hardcode the input for rust playground | |
let input = "https://play.rust-lang.org/"; | |
let u = input.trim().parse::<URL>(); | |
match u { | |
Ok(u) => { | |
println!("Scheme = {}, host = {}", u.scheme, u.host); | |
} | |
Err(e) => { | |
println!("Not able to parse URL: {:?}", e); | |
} | |
} | |
} | |
#[cfg(test)] | |
mod test { | |
use super::*; | |
#[test] | |
fn case_1() { | |
let url = "https://www.google.com?q=Elon+Musk"; | |
let result = url.parse::<URL>(); | |
assert!(result.is_ok()); | |
let u = result.unwrap(); | |
assert_eq!(u.host, "www.google.com".to_owned()); | |
assert_eq!(u.port, None); | |
assert_eq!(u.scheme, "https".to_owned()); | |
assert_eq!(u.paths, Vec::<String>::new()); | |
let query_params = vec![("q".to_owned(), "Elon+Musk".to_owned())]; | |
assert_eq!(u.query_params, query_params); | |
} | |
#[test] | |
fn case_2_without_scheme() { | |
let url = "play.rust-lang.com"; | |
let result = url.parse::<URL>(); | |
assert!(result.is_err()); | |
assert_eq!(result.err(), Some(CustomError::MalformedURLError)); | |
} | |
#[test] | |
fn case_3_with_port() { | |
let url = "http://18.203.21.33:8080/api/users?id=372"; | |
let result = url.parse::<URL>(); | |
assert!(result.is_ok()); | |
let u = result.unwrap(); | |
assert_eq!(u.scheme, "http".to_owned()); | |
assert_eq!(u.host, "18.203.21.33".to_string()); | |
assert_eq!(u.port, Some(8080)); | |
let paths = vec!["api".to_string(), "users".to_string()]; | |
let query_params = vec![("id".to_string(), "372".to_string())]; | |
assert_eq!(u.paths, paths); | |
assert_eq!(u.query_params, query_params); | |
} | |
#[test] | |
fn case_4_multiple_qp() { | |
let url = "https://www.youtube.com/watch?v=Dutvai4El0w&t=0s"; | |
let result = url.parse::<URL>(); | |
assert!(result.is_ok()); | |
let u = result.unwrap(); | |
assert_eq!(u.scheme, "https".to_owned()); | |
assert_eq!(u.host, "www.youtube.com".to_string()); | |
assert_eq!(u.port, None); | |
let paths = vec!["watch".to_string()]; | |
let query_params = vec![ | |
("v".to_string(), "Dutvai4El0w".to_string()), | |
("t".to_string(), "0s".to_string()), | |
]; | |
assert_eq!(u.paths, paths); | |
assert_eq!(u.query_params, query_params); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment