sqlx_xugu/options/
parse.rs1use 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", ¤t_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}