sqlx_xugu/options/
parse.rs

1use crate::XuguConnectOptions;
2use sqlx_core::percent_encoding::{percent_decode_str, utf8_percent_encode, NON_ALPHANUMERIC};
3use sqlx_core::{Error, Url};
4use std::str::FromStr;
5
6fn parse_bool(s: &str, default: bool) -> bool {
7    match s {
8        "true" | "on" | "1" | "t" | "T" => true,
9        "false" | "off" | "0" | "f" | "F" => false,
10        _ => default,
11    }
12}
13
14fn bool2url(b: bool) -> &'static str {
15    if b {
16        "on"
17    } else {
18        "off"
19    }
20}
21
22impl XuguConnectOptions {
23    pub(crate) fn parse_from_url(url: &Url) -> Result<Self, Error> {
24        let mut options = Self::new();
25
26        if let Some(host) = url.host_str() {
27            options = options.host(host);
28        }
29
30        if let Some(port) = url.port() {
31            options = options.port(port);
32        }
33
34        let user = url.username();
35        if !user.is_empty() {
36            options = options.user(
37                &percent_decode_str(user)
38                    .decode_utf8()
39                    .map_err(Error::config)?,
40            );
41        }
42
43        if let Some(password) = url.password() {
44            options = options.password(
45                &percent_decode_str(password)
46                    .decode_utf8()
47                    .map_err(Error::config)?,
48            );
49        }
50
51        let path = url.path().trim_start_matches('/');
52        if !path.is_empty() {
53            options = options.database(
54                &percent_decode_str(path)
55                    .decode_utf8()
56                    .map_err(Error::config)?,
57            );
58        }
59
60        for (key, value) in url.query_pairs().into_iter() {
61            if value.is_empty() {
62                continue;
63            }
64            match &*key {
65                "user" => options = options.user(&value),
66                "password" => options = options.password(&value),
67                "version" => options = options.version(value.parse().unwrap()),
68                "return_schema" => options = options.return_schema(parse_bool(&value, true)),
69                "return_rowid" => options = options.return_rowid(parse_bool(&value, true)),
70                "encryptor" => options = options.encryptor(&value),
71                "charset" => options = options.charset(&value),
72                "timezone" | "time_zone" | "time-zone" => {
73                    options = options.timezone(Some(value.replace("GMT ", "GMT+")));
74                }
75                "iso_level" => options = options.iso_level(&value),
76                "lock_timeout" => options = options.lock_timeout(&value),
77                "lob_ret" => options = options.lob_ret(&value),
78                "identity_mode" => options = options.identity_mode(&value),
79                "keyword_filter" => options = options.keyword_filter(&value),
80                "disable_binlog" => options = options.disable_binlog(&value),
81                "auto_commit" => options = options.auto_commit(parse_bool(&value, true)),
82                "current_schema" | "schemaon" => options = options.current_schema(&value),
83                "compatible_mode" => options = options.compatible_mode(&value),
84                "useSSL" | "usessl" | "use_ssl" => {
85                    options = options.use_ssl(parse_bool(&value, false))
86                }
87                "ssl" if value.eq("ssl") => options = options.use_ssl(true),
88
89                _ => {}
90            }
91        }
92
93        Ok(options)
94    }
95
96    pub(crate) fn build_url(&self) -> Url {
97        let mut url = Url::parse(&format!("xugu://{}@{}:{}", self.user, self.host, self.port))
98            .expect("BUG: generated un-parseable URL");
99
100        let password = utf8_percent_encode(&self.password, NON_ALPHANUMERIC).to_string();
101        let _ = url.set_password(Some(&password));
102
103        url.set_path(&self.database);
104
105        if let Some(version) = self.version {
106            url.query_pairs_mut()
107                .append_pair("version", version.to_string().as_str());
108        }
109
110        url.query_pairs_mut()
111            .append_pair("return_schema", bool2url(self.return_schema));
112        url.query_pairs_mut()
113            .append_pair("return_rowid", bool2url(self.return_rowid));
114        if let Some(encryptor) = &self.encryptor {
115            url.query_pairs_mut().append_pair("encryptor", &encryptor);
116        }
117        url.query_pairs_mut().append_pair("charset", &self.charset);
118        if let Some(time_zone) = &self.time_zone {
119            url.query_pairs_mut().append_pair("time_zone", &time_zone);
120        }
121        if let Some(iso_level) = &self.iso_level {
122            url.query_pairs_mut().append_pair("iso_level", &iso_level);
123        }
124        if let Some(lock_timeout) = &self.lock_timeout {
125            url.query_pairs_mut()
126                .append_pair("lock_timeout", &lock_timeout);
127        }
128        if let Some(lob_ret) = &self.lob_ret {
129            url.query_pairs_mut().append_pair("lob_ret", &lob_ret);
130        }
131        if let Some(identity_mode) = &self.identity_mode {
132            url.query_pairs_mut()
133                .append_pair("identity_mode", &identity_mode);
134        }
135        if let Some(keyword_filter) = &self.keyword_filter {
136            url.query_pairs_mut()
137                .append_pair("keyword_filter", &keyword_filter);
138        }
139        if let Some(disable_binlog) = &self.disable_binlog {
140            url.query_pairs_mut()
141                .append_pair("disable_binlog", &disable_binlog);
142        }
143        url.query_pairs_mut()
144            .append_pair("auto_commit", bool2url(self.auto_commit));
145        if let Some(current_schema) = &self.current_schema {
146            url.query_pairs_mut()
147                .append_pair("current_schema", &current_schema);
148        }
149        if let Some(compatible_mode) = &self.compatible_mode {
150            url.query_pairs_mut()
151                .append_pair("compatible_mode", &compatible_mode);
152        }
153        url.query_pairs_mut()
154            .append_pair("use_ssl", bool2url(self.use_ssl));
155
156        url
157    }
158}
159
160impl FromStr for XuguConnectOptions {
161    type Err = Error;
162
163    fn from_str(s: &str) -> Result<Self, Error> {
164        let url: Url = s.parse().map_err(Error::config)?;
165        Self::parse_from_url(&url)
166    }
167}