dictproto/
url.rs

1use crate::{Database, Strategy};
2use std::convert::From;
3use std::error::Error;
4use std::fmt::{Display, Formatter};
5use std::str::FromStr;
6use url::{ParseError, Url};
7
8#[derive(Debug)]
9pub enum DICTUrlError {
10    ParseError(ParseError),
11    UnknownAccess(String),
12    MissingParameters,
13    MissingHost,
14    Unsupported(&'static str),
15}
16
17impl Display for DICTUrlError {
18    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
19        write!(
20            f,
21            "{}",
22            match self {
23                Self::ParseError(_) => "Parse error",
24                Self::Unsupported(_) => "Unsuported",
25                Self::UnknownAccess(_) => "Unknown access method",
26                Self::MissingParameters => "Missing parameters",
27                Self::MissingHost => "Missing host",
28            }
29        )
30    }
31}
32
33impl From<ParseError> for DICTUrlError {
34    fn from(inner: ParseError) -> Self {
35        DICTUrlError::ParseError(inner)
36    }
37}
38
39impl Error for DICTUrlError {}
40
41pub enum DICTUrlAccess {
42    AccessOnly,
43    Define(String, Database, Option<usize>),
44    Match(String, Database, Strategy, Option<usize>),
45}
46
47impl FromStr for DICTUrlAccess {
48    type Err = DICTUrlError;
49    fn from_str(s: &str) -> Result<Self, Self::Err> {
50        let path = if let Some(p) = s.strip_prefix("/") {
51            p
52        } else {
53            return Ok(DICTUrlAccess::AccessOnly);
54        };
55
56        let mut parts = path.split(':');
57
58        match parts.next() {
59            None | Some("") => Ok(DICTUrlAccess::AccessOnly),
60            Some("d") => {
61                let word = match parts.next() {
62                    Some(w) if !w.is_empty() => w.to_string(),
63                    _ => {
64                        return Err(DICTUrlError::MissingParameters);
65                    }
66                };
67
68                let db = match parts.next() {
69                    Some(d) if !d.is_empty() => Database::from(d.to_string()),
70                    _ => Database::default(),
71                };
72
73                let nr = if let Some(maybe_n) = parts.next() {
74                    if let Ok(n) = maybe_n.parse::<usize>() {
75                        Some(n)
76                    } else {
77                        Some(0)
78                    }
79                } else {
80                    None
81                };
82
83                Ok(DICTUrlAccess::Define(word, db, nr))
84            }
85            Some("m") => {
86                let word = match parts.next() {
87                    Some(w) if !w.is_empty() => w.to_string(),
88                    _ => {
89                        return Err(DICTUrlError::MissingParameters);
90                    }
91                };
92
93                let db = match parts.next() {
94                    Some(d) if !d.is_empty() => Database::from(d.to_string()),
95                    _ => Database::default(),
96                };
97
98                let strat = match parts.next() {
99                    Some(s) if !s.is_empty() => Strategy::from(s.to_string()),
100                    _ => Strategy::default(),
101                };
102
103                let nr = if let Some(maybe_n) = parts.next() {
104                    if let Ok(n) = maybe_n.parse::<usize>() {
105                        Some(n)
106                    } else {
107                        Some(0)
108                    }
109                } else {
110                    None
111                };
112
113                Ok(DICTUrlAccess::Match(word, db, strat, nr))
114            }
115            Some(s) => Err(DICTUrlError::UnknownAccess(s.to_string())),
116        }
117    }
118}
119
120pub struct DICTUrl {
121    pub host: String,
122    pub port: u16,
123    pub access_method: DICTUrlAccess,
124}
125
126impl DICTUrl {
127    pub fn new(src: &str) -> Result<Self, DICTUrlError> {
128        let raw_url = Url::parse(src)?;
129
130        if !raw_url.username().is_empty() {
131            return Err(DICTUrlError::Unsupported("Auth part is not supported"));
132        }
133
134        let host: String = raw_url
135            .host_str()
136            .ok_or(DICTUrlError::MissingHost)?
137            .to_string();
138        let port: u16 = raw_url.port().or(Some(2628)).unwrap();
139        let access_method = DICTUrlAccess::from_str(raw_url.path())?;
140
141        Ok(DICTUrl {
142            host,
143            port,
144            access_method,
145        })
146    }
147}
148
149#[cfg(test)]
150mod test {
151    use super::*;
152
153    #[test]
154    fn basic_parsing() {
155        let url = DICTUrl::new("dict://dict.org/d:shortcake:").unwrap();
156
157        assert_eq!(url.host, "dict.org");
158        assert_eq!(url.port, 2628);
159
160        if let DICTUrlAccess::Define(word, _, _) = url.access_method {
161            assert_eq!(word, String::from("shortcake"));
162        } else {
163            panic!("Did not return correct access method");
164        }
165    }
166}