1use std::error::Error;
2
3use pest::Parser;
4use pest_derive::Parser;
5
6#[derive(Parser)]
7#[grammar = "url.pest"]
8struct UrlParser;
9
10#[derive(Debug)]
11pub struct ParsedUrl {
12 pub scheme: String,
13 pub host: String,
14 pub port: Option<u16>,
15 pub path: String,
16 pub query: Option<String>,
17 pub fragment: Option<String>,
18 pub file: Option<String>,
19}
20
21impl ParsedUrl {
22 fn new() -> Self {
23 ParsedUrl {
24 scheme: String::new(),
25 host: String::new(),
26 port: None,
27 path: String::new(),
28 query: None,
29 fragment: None,
30 file: None,
31 }
32 }
33}
34
35pub fn parse_url(url_string: &str) -> Result<ParsedUrl, Box<dyn Error>> {
36 let pairs = match UrlParser::parse(Rule::url, url_string) {
37 Ok(pairs) => pairs,
38 Err(e) => return Err(format!("Error parsing URL: {}", e).into()),
39 };
40
41 let mut parsed_url = ParsedUrl::new();
42
43 for pair in pairs.into_iter() {
44 for pair_inner in pair.into_inner() {
45 match pair_inner.as_rule() {
46 Rule::scheme => parsed_url.scheme = pair_inner.as_str().to_string(),
47 Rule::host => parsed_url.host = pair_inner.as_str().to_string(),
48 Rule::port => parsed_url.port = Some(pair_inner.as_str().parse().unwrap()),
49 Rule::path => parsed_url.path = pair_inner.as_str().to_string(),
50 Rule::query => parsed_url.query = Some(pair_inner.as_str().to_string()),
51 Rule::fragment => parsed_url.fragment = Some(pair_inner.as_str().to_string()),
52 Rule::file => parsed_url.file = Some(pair_inner.as_str().to_string()),
53 _ => {
54 eprintln!("Unexpected rule: {:?}", pair_inner.as_rule());
55 }
56 }
57 }
58 }
59
60 Ok(parsed_url)
61}