Skip to content

Instantly share code, notes, and snippets.

@sibu-github
Created April 12, 2022 11:48
Show Gist options
  • Save sibu-github/6ea23742ccd6f691de149cb7f829f57a to your computer and use it in GitHub Desktop.
Save sibu-github/6ea23742ccd6f691de149cb7f829f57a to your computer and use it in GitHub Desktop.
URL Parse
// 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