alith_client/components/grammar/
basic_url.rs

1use super::{Grammar, GrammarError, GrammarSetterTrait};
2use std::{cell::RefCell, str::FromStr};
3use url::Url;
4
5#[derive(Clone, Default, PartialEq)]
6pub struct BasicUrlGrammar {
7    pub stop_word_done: Option<String>,
8    pub stop_word_no_result: Option<String>,
9    grammar_string: RefCell<Option<String>>,
10}
11
12impl BasicUrlGrammar {
13    pub fn new() -> Self {
14        Self {
15            stop_word_done: None,
16            stop_word_no_result: None,
17            grammar_string: RefCell::new(None),
18        }
19    }
20}
21
22impl BasicUrlGrammar {
23    #[inline]
24    pub fn wrap(self) -> Grammar {
25        Grammar::BasicUrl(self)
26    }
27
28    pub fn grammar_string(&self) -> String {
29        let mut grammar_string = self.grammar_string.borrow_mut();
30        if grammar_string.is_none() {
31            *grammar_string = Some(url_grammar(&self.stop_word_done, &self.stop_word_no_result));
32        }
33        grammar_string.as_ref().unwrap().clone()
34    }
35
36    #[inline]
37    pub fn validate_clean(&self, content: &str) -> Result<String, GrammarError> {
38        url_validate_clean(content)
39    }
40
41    #[inline]
42    pub fn grammar_parse(&self, content: &str) -> Result<Url, GrammarError> {
43        url_parse(content)
44    }
45}
46
47impl GrammarSetterTrait for BasicUrlGrammar {
48    fn stop_word_done_mut(&mut self) -> &mut Option<String> {
49        &mut self.stop_word_done
50    }
51
52    fn stop_word_no_result_mut(&mut self) -> &mut Option<String> {
53        &mut self.stop_word_no_result
54    }
55}
56
57pub fn url_grammar<T: AsRef<str>>(
58    stop_word_done: &Option<T>,
59    stop_word_no_result: &Option<T>,
60) -> String {
61    match (stop_word_done, stop_word_no_result) {
62        (Some(stop_word_done), Some(stop_word_no_result)) => format!(
63            "root ::= \" \" ( url | \"{}\" ) \" {}\"\n{URL_GRAMMAR}",
64            stop_word_no_result.as_ref(),
65            stop_word_done.as_ref()
66        ),
67        (None, Some(stop_word_no_result)) => {
68            format!(
69                "root ::= \" \" ( url | \"{}\" )\n{URL_GRAMMAR}",
70                stop_word_no_result.as_ref()
71            )
72        }
73        (Some(stop_word_done), None) => {
74            format!(
75                "root ::= \" \" url \" {}\"\n{URL_GRAMMAR}",
76                stop_word_done.as_ref()
77            )
78        }
79        (None, None) => format!("root ::= \" \" url\n{URL_GRAMMAR}"),
80    }
81}
82
83pub const URL_GRAMMAR: &str = r##"url ::= scheme "://" authority path{0,}
84scheme ::= "https" | "http"
85authority ::= host (":" port){0,1}
86host ::= domain | ipv4address
87domain ::= subdomains tld
88subdomains ::= (label "."){1,3}
89label ::= [a-zA-Z0-9] [a-zA-Z0-9-]{0,62}
90tld ::= [a-zA-Z]{2,63}
91ipv4address ::= dec-octet "." dec-octet "." dec-octet "." dec-octet
92dec-octet ::= [0-9] | [1-9][0-9] | "1"[0-9][0-9] | "2"[0-4][0-9] | "25"[0-5]
93port ::= [0-9]{1,5}
94path ::= ("/" segment)
95segment ::= pchar{0,}
96pchar ::= unreserved | pct-encoded | sub-delims | ":" | "@"
97unreserved ::= [a-zA-Z0-9-._~]
98pct-encoded ::= "%" hexdig hexdig
99hexdig ::= [0-9a-fA-F]
100sub-delims ::= [!$&'()*+,;=]
101"##;
102
103pub fn url_validate_clean(content: &str) -> Result<String, GrammarError> {
104    if let Ok(url) = url_parse(content) {
105        Ok(url.to_string())
106    } else {
107        Err(GrammarError::ParseValueError {
108            content: content.to_string(),
109            parse_type: "Url".to_string(),
110        })
111    }
112}
113
114pub fn url_parse(content: &str) -> Result<Url, GrammarError> {
115    if let Ok(url) = Url::from_str(content.trim()) {
116        Ok(url)
117    } else {
118        Err(GrammarError::ParseValueError {
119            content: content.to_string(),
120            parse_type: "Url".to_string(),
121        })
122    }
123}