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 "statement-cache-capacity"
89 | "statement_cache_capacity"
90 | "max-prepare-num"
91 | "max_prepare_num" => {
92 options =
93 options.statement_cache_capacity(value.parse().map_err(Error::config)?);
94 }
95
96 _ => {}
97 }
98 }
99
100 Ok(options)
101 }
102
103 pub(crate) fn build_url(&self) -> Url {
104 let mut url = Url::parse(&format!("xugu://{}@{}:{}", self.user, self.host, self.port))
105 .expect("BUG: generated un-parseable URL");
106
107 let password = utf8_percent_encode(&self.password, NON_ALPHANUMERIC).to_string();
108 let _ = url.set_password(Some(&password));
109
110 url.set_path(&self.database);
111
112 if let Some(version) = self.version {
113 url.query_pairs_mut()
114 .append_pair("version", version.to_string().as_str());
115 }
116
117 url.query_pairs_mut()
118 .append_pair("return_schema", bool2url(self.return_schema));
119 url.query_pairs_mut()
120 .append_pair("return_rowid", bool2url(self.return_rowid));
121 if let Some(encryptor) = &self.encryptor {
122 url.query_pairs_mut().append_pair("encryptor", &encryptor);
123 }
124 url.query_pairs_mut().append_pair("charset", &self.charset);
125 if let Some(time_zone) = &self.time_zone {
126 url.query_pairs_mut().append_pair("time_zone", &time_zone);
127 }
128 if let Some(iso_level) = &self.iso_level {
129 url.query_pairs_mut().append_pair("iso_level", &iso_level);
130 }
131 if let Some(lock_timeout) = &self.lock_timeout {
132 url.query_pairs_mut()
133 .append_pair("lock_timeout", &lock_timeout);
134 }
135 if let Some(lob_ret) = &self.lob_ret {
136 url.query_pairs_mut().append_pair("lob_ret", &lob_ret);
137 }
138 if let Some(identity_mode) = &self.identity_mode {
139 url.query_pairs_mut()
140 .append_pair("identity_mode", &identity_mode);
141 }
142 if let Some(keyword_filter) = &self.keyword_filter {
143 url.query_pairs_mut()
144 .append_pair("keyword_filter", &keyword_filter);
145 }
146 if let Some(disable_binlog) = &self.disable_binlog {
147 url.query_pairs_mut()
148 .append_pair("disable_binlog", &disable_binlog);
149 }
150 url.query_pairs_mut()
151 .append_pair("auto_commit", bool2url(self.auto_commit));
152 if let Some(current_schema) = &self.current_schema {
153 url.query_pairs_mut()
154 .append_pair("current_schema", ¤t_schema);
155 }
156 if let Some(compatible_mode) = &self.compatible_mode {
157 url.query_pairs_mut()
158 .append_pair("compatible_mode", &compatible_mode);
159 }
160 url.query_pairs_mut()
161 .append_pair("use_ssl", bool2url(self.use_ssl));
162
163 url.query_pairs_mut().append_pair(
164 "statement-cache-capacity",
165 &self.statement_cache_capacity.to_string(),
166 );
167
168 url
169 }
170}
171
172impl FromStr for XuguConnectOptions {
173 type Err = Error;
174
175 fn from_str(s: &str) -> Result<Self, Error> {
176 let url: Url = s.parse().map_err(Error::config)?;
177 Self::parse_from_url(&url)
178 }
179}