mdsn/
lib.rs

1//! M-DSN: A Multi-address DSN(Data Source Name) parser.
2//!
3//! M-DSN support two kind of DSN format:
4//!
5//! 1. `<driver>[+<protocol>]://<username>:<password>@<addresses>/<database>?<params>`
6//! 2. `<driver>[+<protocol>]://<username>:<password>@<fragment>?<params>`
7//! 3. `<driver>://<username>:<password>@<protocol>(<addresses>)/<database>?<params>`
8//!
9//! All the items will be parsed into struct [Dsn](crate::Dsn).
10//!
11//! ## Parser
12//!
13//! ```rust
14//! use mdsn::Dsn;
15//! use std::str::FromStr;
16//!
17//! # fn main() -> Result<(), mdsn::DsnError> {
18//! // The two styles are equivalent.
19//! let dsn = Dsn::from_str("taos://root:taosdata@host1:6030,host2:6030/db")?;
20//! let dsn: Dsn = "taos://root:taosdata@host1:6030,host2:6030/db".parse()?;
21//!
22//! assert_eq!(dsn.driver, "taos");
23//! assert_eq!(dsn.username.unwrap(), "root");
24//! assert_eq!(dsn.password.unwrap(), "taosdata");
25//! assert_eq!(dsn.subject.unwrap(), "db");
26//! assert_eq!(dsn.addresses.len(), 2);
27//! assert_eq!(dsn.addresses, vec![
28//!     mdsn::Address::new("host1", 6030),
29//!     mdsn::Address::new("host2", 6030),
30//! ]);
31//! # Ok(())
32//! # }
33//! ```
34//!
35//! ## DSN Examples
36//!
37//! A DSN for [TDengine](https://taosdata.com) driver [taos](https://docs.rs/taos).
38//!
39//! ```dsn
40//! taos://root:taosdata@localhost:6030/db?timezone=Asia/Shanghai&asyncLog=1
41//! ```
42//!
43//! With multi-address:
44//!
45//! ```dsn
46//! taos://root:taosdata@host1:6030,host2:6030/db?timezone=Asia/Shanghai
47//! ```
48//!
49//! A DSN for unix socket:
50//!
51//! ```dsn
52//! unix:///path/to/unix.sock?param1=value
53//! ```
54//!
55//! A DSN for postgresql with url-encoded socket directory path.
56//!
57//! ```dsn
58//! postgresql://%2Fvar%2Flib%2Fpostgresql/db
59//! ```
60//!
61//! A DSN for sqlite db file, note that you must use prefix `./` for a relative path file.
62//!
63//! ```dsn
64//! sqlite://./file.db
65//! ```
66//!
67use std::borrow::Cow;
68use std::collections::BTreeMap;
69use std::fmt::Display;
70use std::num::ParseIntError;
71use std::str::FromStr;
72use std::string::FromUtf8Error;
73
74use itertools::Itertools;
75#[cfg(feature = "pest")]
76use pest::Parser;
77#[cfg(feature = "pest")]
78use pest_derive::Parser;
79use regex::Regex;
80use thiserror::Error;
81use urlencoding::encode;
82
83impl Dsn {
84    pub fn from_regex(input: &str) -> Result<Self, DsnError> {
85        lazy_static::lazy_static! {
86            static ref RE: Regex = Regex::new(r"(?x)
87                (?P<driver>[\w.-]+)(\+(?P<protocol>[^@/?\#]+))?: # abc
88                (
89                    # url-like dsn
90                    //((?P<username>[\w\s\-_%.]+)?(:(?P<password>[^@/?\#]+))?@)? # for authorization
91                        (((?P<protocol2>[\w\s.-]+)\()?
92                            (?P<addr>[\w\-_%.:]*(:\d{0,5})?(,[\w\-:_.]*(:\d{0,5})?)*)?  # for addresses
93                        \)?)?
94                        (/(?P<subject>[\w\s%$@.,/-]+)?)?                             # for subject
95                    | # or
96                    # path-like dsn
97                    (?P<path>([\\/.~]$|/\s*\w+[\w\s %$@*:.\\\-/ _\(\)\[\]{}()【】{}]*|[\.~\w\s]?[\w\s %$@*:.\\\-/ _\(\)\[\]{}()【】{}]+))
98                ) # abc
99                (\?(?P<params>(?s:.)*))?").unwrap();
100        }
101
102        let cap = RE
103            .captures(input)
104            .ok_or_else(|| DsnError::InvalidConnection(input.to_string()))?;
105
106        let driver = cap.name("driver").unwrap().as_str().to_string();
107        let protocol = cap.name("protocol").map(|m| m.as_str().to_string());
108        let protocol2 = cap.name("protocol2").map(|m| m.as_str().to_string());
109        let protocol = match (protocol, protocol2) {
110            (Some(_), Some(_)) => Err(DsnError::InvalidProtocol("".to_string()))?,
111            (Some(p), None) | (None, Some(p)) => Some(p),
112            _ => None,
113        };
114        let username = cap
115            .name("username")
116            .map(|m| {
117                let s = m.as_str();
118                urlencoding::decode(s)
119                    .map(|s| s.to_string())
120                    .map_err(|err| DsnError::InvalidPassword(s.to_string(), err))
121            })
122            .transpose()?;
123        let password = cap
124            .name("password")
125            .map(|m| {
126                let s = m.as_str();
127                urlencoding::decode(s)
128                    .map(|s| s.to_string())
129                    .map_err(|err| DsnError::InvalidPassword(s.to_string(), err))
130            })
131            .transpose()?;
132        let subject = cap.name("subject").map(|m| m.as_str().to_string());
133        let path = cap.name("path").map(|m| m.as_str().to_string());
134        let addresses = if let Some(addr) = cap.name("addr") {
135            let addr = addr.as_str();
136            if addr.is_empty() {
137                vec![]
138            } else {
139                let mut addrs = Vec::new();
140
141                for s in addr.split(',') {
142                    if s.is_empty() {
143                        continue;
144                    }
145                    if let Some((host, port)) = s.split_once(':') {
146                        let port = if port.is_empty() {
147                            0
148                        } else {
149                            port.parse::<u16>().map_err(|err| {
150                                DsnError::InvalidAddresses(s.to_string(), err.to_string())
151                            })?
152                        };
153                        addrs.push(Address::new(host, port));
154                    } else if s.contains('%') {
155                        addrs.push(Address::from_path(urlencoding::decode(s).unwrap()));
156                    } else {
157                        addrs.push(Address::from_host(s));
158                    }
159                }
160                addrs
161            }
162        } else {
163            vec![]
164        };
165        let mut params = BTreeMap::new();
166        if let Some(p) = cap.name("params") {
167            for p in p.as_str().split_terminator('&') {
168                if p.contains('=') {
169                    if let Some((k, v)) = p.split_once('=') {
170                        let k = urlencoding::decode(k)?;
171                        let v = urlencoding::decode(v)?;
172                        params.insert(k.to_string(), v.to_string());
173                    }
174                } else {
175                    let p = urlencoding::decode(p)?;
176                    params.insert(p.to_string(), "".to_string());
177                }
178            }
179        }
180        Ok(Dsn {
181            driver,
182            protocol,
183            username,
184            password,
185            addresses,
186            path,
187            subject,
188            params,
189        })
190    }
191
192    #[cfg(feature = "pest")]
193    pub fn from_pest(input: &str) -> Result<Self, DsnError> {
194        let dsn = DsnParser::parse(Rule::dsn, input)?.next().unwrap();
195
196        let mut to = Dsn::default();
197        for pair in dsn.into_inner() {
198            match pair.as_rule() {
199                Rule::scheme => {
200                    for inner in pair.into_inner() {
201                        match inner.as_rule() {
202                            Rule::driver => to.driver = inner.as_str().to_string(),
203                            Rule::protocol => to.protocol = Some(inner.as_str().to_string()),
204                            _ => unreachable!(),
205                        }
206                    }
207                }
208                Rule::SCHEME_IDENT => (),
209                Rule::username_with_password => {
210                    for inner in pair.into_inner() {
211                        match inner.as_rule() {
212                            Rule::username => to.username = Some(inner.as_str().to_string()),
213                            Rule::password => to.password = Some(inner.as_str().to_string()),
214                            _ => unreachable!(),
215                        }
216                    }
217                }
218                Rule::protocol_with_addresses => {
219                    for inner in pair.into_inner() {
220                        match inner.as_rule() {
221                            Rule::addresses => {
222                                for inner in inner.into_inner() {
223                                    match inner.as_rule() {
224                                        Rule::address => {
225                                            let mut addr = Address::default();
226                                            for inner in inner.into_inner() {
227                                                match inner.as_rule() {
228                                                    Rule::host => {
229                                                        addr.host = Some(inner.as_str().to_string())
230                                                    }
231                                                    Rule::port => {
232                                                        addr.port = Some(inner.as_str().parse()?)
233                                                    }
234                                                    Rule::path => {
235                                                        addr.path = Some(
236                                                            urlencoding::decode(inner.as_str())
237                                                                .expect("UTF-8")
238                                                                .to_string(),
239                                                        )
240                                                    }
241                                                    _ => unreachable!(),
242                                                }
243                                            }
244                                            to.addresses.push(addr);
245                                        }
246                                        _ => unreachable!(),
247                                    }
248                                }
249                            }
250                            Rule::protocol => to.protocol = Some(inner.as_str().to_string()),
251                            _ => unreachable!(),
252                        }
253                    }
254                }
255                Rule::database => {
256                    to.subject = Some(pair.as_str().to_string());
257                }
258                Rule::fragment => {
259                    to.path = Some(pair.as_str().to_string());
260                }
261                Rule::path_like => {
262                    to.path = Some(pair.as_str().to_string());
263                }
264                Rule::param => {
265                    let (mut name, mut value) = ("".to_string(), "".to_string());
266                    for inner in pair.into_inner() {
267                        match inner.as_rule() {
268                            Rule::name => name = inner.as_str().to_string(),
269                            Rule::value => value = inner.as_str().to_string(),
270                            _ => unreachable!(),
271                        }
272                    }
273                    to.params.insert(name, value);
274                }
275                Rule::EOI => {}
276                _ => unreachable!(),
277            }
278        }
279        Ok(to)
280    }
281}
282
283#[cfg(feature = "pest")]
284#[derive(Parser)]
285#[grammar = "dsn.pest"]
286struct DsnParser;
287
288/// Error caused by [pest](https://docs.rs/pest) DSN parser.
289#[derive(Debug, Error)]
290pub enum DsnError {
291    #[cfg(feature = "pest")]
292    #[error("{0}")]
293    ParseErr(#[from] pest::error::Error<Rule>),
294    #[error("unable to parse port from {0}")]
295    PortErr(#[from] ParseIntError),
296    #[error("invalid driver {0}")]
297    InvalidDriver(String),
298    #[error("invalid protocol {0}")]
299    InvalidProtocol(String),
300    #[error("invalid password {0}: {1}")]
301    InvalidPassword(String, FromUtf8Error),
302    #[error("invalid connection {0}")]
303    InvalidConnection(String),
304    #[error("invalid addresses: {0}, error: {1}")]
305    InvalidAddresses(String, String),
306    #[error("requires database: {0}")]
307    RequireDatabase(String),
308    #[error("requires parameter: {0}")]
309    RequireParam(String),
310    #[error("invalid parameter for {0}: {1}")]
311    InvalidParam(String, String),
312    #[error("non utf8 character: {0}")]
313    NonUtf8Character(#[from] FromUtf8Error),
314}
315
316/// A simple struct to represent a server address, with host:port or socket path.
317#[derive(Debug, Default, PartialEq, Eq, Clone)]
318pub struct Address {
319    /// Host or ip address of the server.
320    pub host: Option<String>,
321    /// Port to connect to the server.
322    pub port: Option<u16>,
323    /// Use unix socket path to connect.
324    pub path: Option<String>,
325}
326
327impl Address {
328    /// Construct server address with host and port.
329    #[inline]
330    pub fn new(host: impl Into<String>, port: u16) -> Self {
331        let host = host.into();
332        let host = if host.is_empty() { None } else { Some(host) };
333        Self {
334            host,
335            port: Some(port),
336            ..Default::default()
337        }
338    }
339    // ident: Ident,
340    /// Construct server address with host or ip address only.
341    #[inline]
342    pub fn from_host(host: impl Into<String>) -> Self {
343        let host = host.into();
344        let host = if host.is_empty() { None } else { Some(host) };
345        Self {
346            host,
347            ..Default::default()
348        }
349    }
350
351    /// Construct server address with unix socket path.
352    #[inline]
353    pub fn from_path(path: impl Into<String>) -> Self {
354        Self {
355            path: Some(path.into()),
356            ..Default::default()
357        }
358    }
359
360    #[inline]
361    pub fn is_empty(&self) -> bool {
362        self.host.is_none() && self.port.is_none() && self.path.is_none()
363    }
364}
365
366impl FromStr for Address {
367    type Err = DsnError;
368
369    fn from_str(s: &str) -> Result<Self, Self::Err> {
370        #[cfg(feature = "pest")]
371        {
372            let mut addr = Self::default();
373            if let Some(dsn) = DsnParser::parse(Rule::address, &s)?.next() {
374                for inner in dsn.into_inner() {
375                    match inner.as_rule() {
376                        Rule::host => addr.host = Some(inner.as_str().to_string()),
377                        Rule::port => addr.port = Some(inner.as_str().parse()?),
378                        Rule::path => {
379                            addr.path = Some(
380                                urlencoding::decode(inner.as_str())
381                                    .expect("UTF-8")
382                                    .to_string(),
383                            )
384                        }
385                        _ => unreachable!(),
386                    }
387                }
388            }
389            Ok(addr)
390        }
391        #[cfg(not(feature = "pest"))]
392        {
393            if s.is_empty() {
394                Ok(Self::default())
395            } else if let Some((host, port)) = s.split_once(':') {
396                Ok(Address::new(host, port.parse().unwrap()))
397            } else if s.contains('%') {
398                Ok(Address::from_path(urlencoding::decode(s).unwrap()))
399            } else {
400                Ok(Address::from_host(s))
401            }
402        }
403    }
404}
405
406impl Display for Address {
407    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
408        match (&self.host, self.port, &self.path) {
409            (Some(host), None, None) => write!(f, "{host}"),
410            (Some(host), Some(port), None) => write!(f, "{host}:{port}"),
411            (None, Some(port), None) => write!(f, ":{port}"),
412            (None, None, Some(path)) => write!(f, "{}", urlencoding::encode(path)),
413            (None, None, None) => Ok(()),
414            _ => unreachable!("path will be conflict with host/port"),
415        }
416    }
417}
418
419#[test]
420#[cfg(feature = "pest")]
421fn addr_parse() {
422    let s = "taosdata:6030";
423    let addr = Address::from_str(s).unwrap();
424    assert_eq!(addr.to_string(), s);
425    assert!(!addr.is_empty());
426
427    let s = "/var/lib/taos";
428    let addr = Address::from_str(&urlencoding::encode(s)).unwrap();
429    assert_eq!(addr.path.as_ref().unwrap(), s);
430    assert_eq!(addr.to_string(), urlencoding::encode(s));
431
432    assert_eq!(
433        Address::new("localhost", 6030).to_string(),
434        "localhost:6030"
435    );
436    assert_eq!(Address::from_host("localhost").to_string(), "localhost");
437    assert_eq!(
438        Address::from_path("/path/unix.sock").to_string(),
439        "%2Fpath%2Funix.sock"
440    );
441
442    let none = Address {
443        host: None,
444        port: None,
445        path: None,
446    };
447    assert!(none.is_empty());
448    assert_eq!(none.to_string(), "");
449}
450
451/// A DSN(**Data Source Name**) parser.
452#[derive(Debug, Default, PartialEq, Eq, Clone)]
453pub struct Dsn {
454    pub driver: String,
455    pub protocol: Option<String>,
456    pub username: Option<String>,
457    pub password: Option<String>,
458    pub addresses: Vec<Address>,
459    pub path: Option<String>,
460    pub subject: Option<String>,
461    pub params: BTreeMap<String, String>,
462}
463
464impl Dsn {
465    fn is_path_like(&self) -> bool {
466        self.username.is_none()
467            && self.password.is_none()
468            && self.addresses.is_empty()
469            && self.path.is_some()
470    }
471}
472
473pub trait IntoDsn {
474    fn into_dsn(self) -> Result<Dsn, DsnError>;
475}
476
477impl IntoDsn for &str {
478    fn into_dsn(self) -> Result<Dsn, DsnError> {
479        self.parse()
480    }
481}
482
483impl IntoDsn for String {
484    fn into_dsn(self) -> Result<Dsn, DsnError> {
485        self.as_str().into_dsn()
486    }
487}
488
489impl IntoDsn for &String {
490    fn into_dsn(self) -> Result<Dsn, DsnError> {
491        self.as_str().into_dsn()
492    }
493}
494
495impl IntoDsn for &Dsn {
496    fn into_dsn(self) -> Result<Dsn, DsnError> {
497        Ok(self.clone())
498    }
499}
500impl IntoDsn for Dsn {
501    fn into_dsn(self) -> Result<Dsn, DsnError> {
502        Ok(self)
503    }
504}
505
506impl Display for Dsn {
507    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
508        write!(f, "{}", self.driver)?;
509        if let Some(protocol) = &self.protocol {
510            write!(f, "+{protocol}")?;
511        }
512
513        if self.is_path_like() {
514            write!(f, ":")?;
515        } else {
516            write!(f, "://")?;
517        }
518
519        match (&self.username, &self.password) {
520            (Some(username), Some(password)) => {
521                write!(f, "{}:{}@", encode(username), encode(password))?
522            }
523            (Some(username), None) => write!(f, "{}@", encode(username))?,
524            (None, Some(password)) => write!(f, ":{}@", encode(password))?,
525            (None, None) => {}
526        }
527
528        if !self.addresses.is_empty() {
529            write!(
530                f,
531                "{}",
532                self.addresses.iter().map(ToString::to_string).join(",")
533            )?;
534        }
535        if let Some(database) = &self.subject {
536            write!(f, "/{database}")?;
537        } else if let Some(path) = &self.path {
538            write!(f, "{path}")?;
539        }
540
541        if !self.params.is_empty() {
542            fn percent_encode_or_not(v: &str) -> Cow<str> {
543                if v.contains(|c| c == '=' || c == '&' || c == '#' || c == '@') {
544                    urlencoding::encode(v)
545                } else {
546                    v.into()
547                }
548            }
549            write!(
550                f,
551                "?{}",
552                self.params
553                    .iter()
554                    .map(|(k, v)| format!(
555                        "{}={}",
556                        percent_encode_or_not(k),
557                        percent_encode_or_not(v)
558                    ))
559                    .join("&")
560            )?;
561        }
562        Ok(())
563    }
564}
565
566impl Dsn {
567    #[inline]
568    pub fn drain_params(&mut self) -> BTreeMap<String, String> {
569        let drained = self
570            .params
571            .iter()
572            .map(|(k, v)| (k.clone(), v.clone()))
573            .collect();
574        self.params.clear();
575        drained
576    }
577
578    #[inline]
579    pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) -> Option<String> {
580        self.params.insert(key.into(), value.into())
581    }
582
583    #[inline]
584    pub fn get(&self, key: impl AsRef<str>) -> Option<&String> {
585        self.params.get(key.as_ref())
586    }
587
588    #[inline]
589    pub fn remove(&mut self, key: impl AsRef<str>) -> Option<String> {
590        self.params.remove(key.as_ref())
591    }
592}
593
594impl TryFrom<&str> for Dsn {
595    type Error = DsnError;
596
597    fn try_from(value: &str) -> Result<Self, Self::Error> {
598        Dsn::from_str(value)
599    }
600}
601
602impl TryFrom<&String> for Dsn {
603    type Error = DsnError;
604
605    fn try_from(value: &String) -> Result<Self, Self::Error> {
606        Dsn::from_str(value)
607    }
608}
609
610impl TryFrom<String> for Dsn {
611    type Error = DsnError;
612
613    fn try_from(value: String) -> Result<Self, Self::Error> {
614        Dsn::from_str(&value)
615    }
616}
617
618impl TryFrom<&Dsn> for Dsn {
619    type Error = DsnError;
620
621    fn try_from(value: &Dsn) -> Result<Self, Self::Error> {
622        Ok(value.clone())
623    }
624}
625impl FromStr for Dsn {
626    type Err = DsnError;
627
628    fn from_str(s: &str) -> Result<Self, Self::Err> {
629        #[cfg(feature = "pest")]
630        return Self::from_pest(s);
631        #[cfg(not(feature = "pest"))]
632        return Self::from_regex(s);
633    }
634}
635
636#[cfg(test)]
637mod tests {
638    use super::*;
639
640    #[test]
641    fn test_into_dsn() {
642        let _ = "taos://".into_dsn().unwrap();
643        let _ = "taos://".to_string().into_dsn().unwrap();
644        let _ = (&"taos://".to_string()).into_dsn().unwrap();
645
646        let a: Dsn = "taos://".parse().unwrap();
647        let b = a.into_dsn().unwrap();
648        let c = (&b).into_dsn().unwrap();
649
650        let d: Dsn = (&c).try_into().unwrap();
651        let _: Dsn = (d).try_into().unwrap();
652        let _: Dsn = ("taos://").try_into().unwrap();
653        let _: Dsn = ("taos://".to_string()).try_into().unwrap();
654        let _: Dsn = (&"taos://:password@".parse::<Dsn>().unwrap().to_string())
655            .try_into()
656            .unwrap();
657    }
658
659    #[test]
660    fn test_methods() {
661        let mut dsn: Dsn = "taos://localhost:6030/test?debugFlag=135".parse().unwrap();
662
663        let flag = dsn.get("debugFlag").unwrap();
664        assert_eq!(flag, "135");
665
666        dsn.set("configDir", "/tmp/taos");
667        assert_eq!(dsn.get("configDir").unwrap(), "/tmp/taos");
668        let config = dsn.remove("configDir").unwrap();
669        assert_eq!(config, "/tmp/taos");
670
671        let params = dsn.drain_params();
672        assert!(dsn.params.is_empty());
673        assert!(params.contains_key("debugFlag"));
674        assert!(params.len() == 1);
675    }
676
677    #[test]
678    fn username_with_password() {
679        let s = "taos://";
680
681        let dsn = Dsn::from_str(s).unwrap();
682        assert_eq!(
683            dsn,
684            Dsn {
685                driver: "taos".to_string(),
686                ..Default::default()
687            }
688        );
689        assert_eq!(dsn.to_string(), s);
690
691        let s = "taos:///";
692
693        let dsn = Dsn::from_str(s).unwrap();
694        assert_eq!(
695            dsn,
696            Dsn {
697                driver: "taos".to_string(),
698                ..Default::default()
699            }
700        );
701        assert_eq!(dsn.to_string(), "taos://");
702
703        let s = "taos://root@";
704
705        let dsn = Dsn::from_str(s).unwrap();
706        assert_eq!(
707            dsn,
708            Dsn {
709                driver: "taos".to_string(),
710                username: Some("root".to_string()),
711                ..Default::default()
712            }
713        );
714        assert_eq!(dsn.to_string(), s);
715        let s = "taos://root:taosdata@";
716
717        let dsn = Dsn::from_str(s).unwrap();
718        assert_eq!(
719            dsn,
720            Dsn {
721                driver: "taos".to_string(),
722                username: Some("root".to_string()),
723                password: Some("taosdata".to_string()),
724                ..Default::default()
725            }
726        );
727        assert_eq!(dsn.to_string(), s);
728    }
729
730    #[test]
731    fn host_port_mix() {
732        let s = "taos://localhost";
733        let dsn = Dsn::from_str(s).unwrap();
734        assert_eq!(
735            dsn,
736            Dsn {
737                driver: "taos".to_string(),
738                addresses: vec![Address {
739                    host: Some("localhost".to_string()),
740                    ..Default::default()
741                }],
742                ..Default::default()
743            }
744        );
745        assert_eq!(dsn.to_string(), s);
746
747        let s = "taos://root@:6030";
748        let dsn = Dsn::from_str(s).unwrap();
749        assert_eq!(
750            dsn,
751            Dsn {
752                driver: "taos".to_string(),
753                username: Some("root".to_string()),
754                addresses: vec![Address {
755                    port: Some(6030),
756                    ..Default::default()
757                }],
758                ..Default::default()
759            }
760        );
761        assert_eq!(dsn.to_string(), s);
762
763        let s = "taos://root@localhost:6030";
764        let dsn = Dsn::from_str(s).unwrap();
765        assert_eq!(
766            dsn,
767            Dsn {
768                driver: "taos".to_string(),
769                username: Some("root".to_string()),
770                addresses: vec![Address {
771                    host: Some("localhost".to_string()),
772                    port: Some(6030),
773                    ..Default::default()
774                }],
775                ..Default::default()
776            }
777        );
778        assert_eq!(dsn.to_string(), s);
779    }
780    #[test]
781    fn username_with_host() {
782        let s = "taos://root@localhost";
783        let dsn = Dsn::from_str(s).unwrap();
784        assert_eq!(
785            dsn,
786            Dsn {
787                driver: "taos".to_string(),
788                username: Some("root".to_string()),
789                addresses: vec![Address {
790                    host: Some("localhost".to_string()),
791                    ..Default::default()
792                }],
793                ..Default::default()
794            }
795        );
796        assert_eq!(dsn.to_string(), s);
797
798        let s = "taos://root@:6030";
799        let dsn = Dsn::from_str(s).unwrap();
800        assert_eq!(
801            dsn,
802            Dsn {
803                driver: "taos".to_string(),
804                username: Some("root".to_string()),
805                addresses: vec![Address {
806                    port: Some(6030),
807                    ..Default::default()
808                }],
809                ..Default::default()
810            }
811        );
812        assert_eq!(dsn.to_string(), s);
813
814        let s = "taos://root@localhost:6030";
815        let dsn = Dsn::from_str(s).unwrap();
816        assert_eq!(
817            dsn,
818            Dsn {
819                driver: "taos".to_string(),
820                username: Some("root".to_string()),
821                addresses: vec![Address::new("localhost", 6030)],
822                ..Default::default()
823            }
824        );
825
826        let s = "taos://root:taosdata@localhost:6030";
827        let dsn = Dsn::from_str(s).unwrap();
828        assert_eq!(
829            dsn,
830            Dsn {
831                driver: "taos".to_string(),
832                username: Some("root".to_string()),
833                password: Some("taosdata".to_string()),
834                addresses: vec![Address::new("localhost", 6030)],
835                ..Default::default()
836            }
837        );
838        assert_eq!(dsn.to_string(), s);
839    }
840
841    #[test]
842    fn username_with_multi_addresses() {
843        let s = "taos://root@host1.domain:6030,host2.domain:6031";
844        let dsn = Dsn::from_str(s).unwrap();
845        assert_eq!(
846            dsn,
847            Dsn {
848                driver: "taos".to_string(),
849                username: Some("root".to_string()),
850                addresses: vec![
851                    Address::new("host1.domain", 6030),
852                    Address::new("host2.domain", 6031)
853                ],
854                ..Default::default()
855            }
856        );
857        assert_eq!(dsn.to_string(), s);
858
859        let s = "taos://root:taosdata@host1:6030,host2:6031";
860        let dsn = Dsn::from_str(s).unwrap();
861        assert_eq!(
862            dsn,
863            Dsn {
864                driver: "taos".to_string(),
865                username: Some("root".to_string()),
866                password: Some("taosdata".to_string()),
867                addresses: vec![Address::new("host1", 6030), Address::new("host2", 6031)],
868                ..Default::default()
869            }
870        );
871        assert_eq!(dsn.to_string(), s);
872    }
873
874    #[test]
875    fn db_only() {
876        let s = "taos:///db1";
877        let dsn = Dsn::from_str(s).unwrap();
878        assert_eq!(
879            dsn,
880            Dsn {
881                driver: "taos".to_string(),
882                subject: Some("db1".to_string()),
883                ..Default::default()
884            }
885        );
886        assert_eq!(dsn.to_string(), s);
887
888        let s = "taos:///db1";
889        let dsn = Dsn::from_str(s).unwrap();
890        assert_eq!(
891            dsn,
892            Dsn {
893                driver: "taos".to_string(),
894                subject: Some("db1".to_string()),
895                ..Default::default()
896            }
897        );
898        assert_eq!(dsn.to_string(), s);
899    }
900
901    #[test]
902    fn username_with_multi_addresses_database() {
903        let s = "taos://root@host1:6030,host2:6031/db1";
904        let dsn = Dsn::from_str(s).unwrap();
905        assert_eq!(
906            dsn,
907            Dsn {
908                driver: "taos".to_string(),
909                username: Some("root".to_string()),
910                subject: Some("db1".to_string()),
911                addresses: vec![Address::new("host1", 6030), Address::new("host2", 6031)],
912                ..Default::default()
913            }
914        );
915        assert_eq!(dsn.to_string(), s);
916
917        let s = "taos://root:taosdata@host1:6030,host2:6031/db1";
918        let dsn = Dsn::from_str(s).unwrap();
919        assert_eq!(
920            dsn,
921            Dsn {
922                driver: "taos".to_string(),
923                username: Some("root".to_string()),
924                password: Some("taosdata".to_string()),
925                subject: Some("db1".to_string()),
926                addresses: vec![Address::new("host1", 6030), Address::new("host2", 6031)],
927                ..Default::default()
928            }
929        );
930        assert_eq!(dsn.to_string(), s);
931    }
932
933    #[test]
934    fn protocol() {
935        let s = "taos://root@tcp(host1:6030,host2:6031)/db1";
936        let dsn = Dsn::from_str(s).unwrap();
937        assert_eq!(
938            dsn,
939            Dsn {
940                driver: "taos".to_string(),
941                username: Some("root".to_string()),
942                subject: Some("db1".to_string()),
943                protocol: Some("tcp".to_string()),
944                addresses: vec![Address::new("host1", 6030), Address::new("host2", 6031)],
945                ..Default::default()
946            }
947        );
948        assert_eq!(dsn.to_string(), "taos+tcp://root@host1:6030,host2:6031/db1");
949
950        let s = "taos+tcp://root@host1:6030,host2:6031/db1";
951        let dsn = Dsn::from_str(s).unwrap();
952        assert_eq!(
953            dsn,
954            Dsn {
955                driver: "taos".to_string(),
956                username: Some("root".to_string()),
957                subject: Some("db1".to_string()),
958                protocol: Some("tcp".to_string()),
959                addresses: vec![Address::new("host1", 6030), Address::new("host2", 6031)],
960                ..Default::default()
961            }
962        );
963        assert_eq!(dsn.to_string(), "taos+tcp://root@host1:6030,host2:6031/db1");
964    }
965
966    #[test]
967    fn fragment() {
968        let s = "postgresql://%2Fvar%2Flib%2Fpostgresql/dbname";
969        let dsn = Dsn::from_str(s).unwrap();
970        assert_eq!(
971            dsn,
972            Dsn {
973                driver: "postgresql".to_string(),
974                subject: Some("dbname".to_string()),
975                addresses: vec![Address {
976                    path: Some("/var/lib/postgresql".to_string()),
977                    ..Default::default()
978                }],
979                ..Default::default()
980            }
981        );
982        assert_eq!(dsn.to_string(), s);
983
984        let s = "unix:/path/to/unix.sock";
985        let dsn = Dsn::from_str(s).unwrap();
986        assert_eq!(
987            dsn,
988            Dsn {
989                driver: "unix".to_string(),
990                path: Some("/path/to/unix.sock".to_string()),
991                ..Default::default()
992            }
993        );
994        assert_eq!(dsn.to_string(), s);
995
996        let s = "sqlite:/c:/full/windows/path/to/file.db";
997        let dsn = Dsn::from_str(s).unwrap();
998        assert_eq!(
999            dsn,
1000            Dsn {
1001                driver: "sqlite".to_string(),
1002                path: Some("/c:/full/windows/path/to/file.db".to_string()),
1003                ..Default::default()
1004            }
1005        );
1006        assert_eq!(dsn.to_string(), s);
1007
1008        let s = "sqlite:./file.db";
1009        let dsn = Dsn::from_str(s).unwrap();
1010        assert_eq!(
1011            dsn,
1012            Dsn {
1013                driver: "sqlite".to_string(),
1014                path: Some("./file.db".to_string()),
1015                ..Default::default()
1016            }
1017        );
1018        assert_eq!(dsn.to_string(), s);
1019
1020        let s = "sqlite://root:pass@//full/unix/path/to/file.db?mode=0666&readonly=true";
1021        let dsn = Dsn::from_str(s).unwrap();
1022        assert_eq!(
1023            dsn,
1024            Dsn {
1025                driver: "sqlite".to_string(),
1026                username: Some("root".to_string()),
1027                password: Some("pass".to_string()),
1028                subject: Some("/full/unix/path/to/file.db".to_string()),
1029                params: (BTreeMap::from_iter(vec![
1030                    ("mode".to_string(), "0666".to_string()),
1031                    ("readonly".to_string(), "true".to_string())
1032                ])),
1033                ..Default::default()
1034            }
1035        );
1036        assert_eq!(dsn.to_string(), s);
1037    }
1038
1039    #[test]
1040    fn path_special_chars() {
1041        // support `Chinese characters`, `uppercase and lowercase letters`, `digits`, `spaces`, `%`, `$`, `@`, `.`, `-`, `_`, `(`, `)`, `[`, `]`, `{`, `}`, `(`, `)`, `【`, `】`, `{`, `}`
1042        let s = "csv:./files/1718243049903/文件Aa1 %$@.-_()[]{}()【】{}.csv";
1043        let dsn = Dsn::from_str(s).unwrap();
1044        assert_eq!(
1045            dsn,
1046            Dsn {
1047                driver: "csv".to_string(),
1048                path: Some(
1049                    "./files/1718243049903/文件Aa1 %$@.-_()[]{}()【】{}.csv".to_string()
1050                ),
1051                ..Default::default()
1052            }
1053        );
1054        assert_eq!(dsn.to_string(), s);
1055
1056        // do not support other characters which are not in the list above(such as `&` .etc)
1057        let s = "csv:./files/1718243049903/文件Aa1 %$@.-_()[]{}()【】{}&.csv";
1058        let dsn = Dsn::from_str(s).unwrap();
1059        assert_eq!(
1060            dsn,
1061            Dsn {
1062                driver: "csv".to_string(),
1063                path: Some("./files/1718243049903/文件Aa1 %$@.-_()[]{}()【】{}".to_string()),
1064                ..Default::default()
1065            }
1066        );
1067        assert_ne!(dsn.to_string(), s);
1068    }
1069
1070    #[test]
1071    fn params() {
1072        let s = r#"taos://?abc=abc"#;
1073        let dsn = Dsn::from_str(s).unwrap();
1074        assert_eq!(
1075            dsn,
1076            Dsn {
1077                driver: "taos".to_string(),
1078                params: (BTreeMap::from_iter(vec![("abc".to_string(), "abc".to_string())])),
1079                ..Default::default()
1080            }
1081        );
1082        assert_eq!(dsn.to_string(), s);
1083
1084        let s = r#"taos://root@localhost?abc=abc"#;
1085        let dsn = Dsn::from_str(s).unwrap();
1086        assert_eq!(
1087            dsn,
1088            Dsn {
1089                driver: "taos".to_string(),
1090                username: Some("root".to_string()),
1091                addresses: vec![Address::from_host("localhost")],
1092                params: (BTreeMap::from_iter(vec![("abc".to_string(), "abc".to_string())])),
1093                ..Default::default()
1094            }
1095        );
1096        assert_eq!(dsn.to_string(), s);
1097    }
1098
1099    #[test]
1100    fn parse_taos_tmq() {
1101        let s = "taos://root:taosdata@localhost/aa23d04011eca42cf7d8c1dd05a37985?topics=aa23d04011eca42cf7d8c1dd05a37985&group.id=tg2";
1102        let _ = Dsn::from_str(s).unwrap();
1103    }
1104
1105    #[test]
1106    fn tmq_ws_driver() {
1107        let dsn = Dsn::from_str("tmq+ws:///abc1?group.id=abc3&timeout=50ms").unwrap();
1108        assert_eq!(dsn.driver, "tmq");
1109        assert_eq!(dsn.to_string(), "tmq+ws:///abc1?group.id=abc3&timeout=50ms");
1110    }
1111
1112    #[test]
1113    fn tmq_offset() {
1114        let dsn = Dsn::from_str("tmq+ws:///abc1?offset=10:20,11:40").unwrap();
1115        let offset = dsn.get("offset").unwrap();
1116        dbg!(&dsn);
1117        dbg!(&offset);
1118    }
1119
1120    #[test]
1121    fn password_special_chars() {
1122        let p = "!@#$%^&*()";
1123        let e = urlencoding::encode(p);
1124        dbg!(&e);
1125
1126        let dsn = Dsn::from_str(&format!("taos://root:{e}@localhost:6030/")).unwrap();
1127        dbg!(&dsn);
1128        assert_eq!(dsn.password.as_deref().unwrap(), p);
1129        assert_eq!(dsn.to_string(), format!("taos://root:{e}@localhost:6030"));
1130    }
1131    #[test]
1132    fn param_special_chars() {
1133        let p = "!@#$%^&*()";
1134        let e = urlencoding::encode(p);
1135        dbg!(&e);
1136
1137        let dsn = Dsn::from_str(&format!("taos://root:{e}@localhost:6030?code1={e}")).unwrap();
1138        dbg!(&dsn);
1139        assert_eq!(dsn.password.as_deref().unwrap(), p);
1140        assert_eq!(dsn.get("code1").unwrap(), p);
1141        assert_eq!(
1142            dsn.to_string(),
1143            format!("taos://root:{e}@localhost:6030?code1={e}")
1144        );
1145    }
1146    #[test]
1147    fn param_special_chars_all() {
1148        let p = "!@#$%^&*()";
1149        let e = urlencoding::encode(p);
1150        dbg!(&e);
1151
1152        let dsn = Dsn::from_str(&format!("taos://{e}:{e}@localhost:6030?{e}={e}")).unwrap();
1153        dbg!(&dsn);
1154        assert_eq!(dsn.password.as_deref().unwrap(), p);
1155        assert_eq!(dsn.get(p).unwrap(), p);
1156        assert_eq!(
1157            dsn.to_string(),
1158            format!("taos://{e}:{e}@localhost:6030?{e}={e}")
1159        );
1160    }
1161    #[test]
1162    fn unix_path_with_glob() {
1163        let dsn = Dsn::from_str(&format!("csv:./**.csv?param=1")).unwrap();
1164        dbg!(&dsn);
1165        assert!(dsn.path.is_some());
1166        assert_eq!(dsn.get("param").unwrap(), "1");
1167
1168        let dsn = Dsn::from_str(&format!("csv:./**.csv?param=1")).unwrap();
1169        dbg!(&dsn);
1170        assert!(dsn.path.is_some());
1171        assert_eq!(dsn.get("param").unwrap(), "1");
1172
1173        let dsn = Dsn::from_str(&format!("csv:.\\**.csv?param=1")).unwrap();
1174        dbg!(&dsn);
1175        assert!(dsn.path.is_some());
1176        assert_eq!(dsn.get("param").unwrap(), "1");
1177    }
1178
1179    #[test]
1180    fn unix_path_with_space() {
1181        let dsn = Dsn::from_str(&format!("csv:./a b.csv?param=1")).unwrap();
1182        dbg!(&dsn);
1183        assert_eq!(dsn.path.unwrap(), "./a b.csv");
1184    }
1185
1186    #[test]
1187    fn with_space() {
1188        let dsn = Dsn::from_str("pi:///Met1 ABC").unwrap();
1189        assert_eq!(dsn.subject, Some("Met1 ABC".to_string()));
1190
1191        let dsn = Dsn::from_str("pi://u ser:pa ss@host:80/Met1 ABC").unwrap();
1192        dbg!(&dsn);
1193        assert_eq!(dsn.subject, Some("Met1 ABC".to_string()));
1194        assert_eq!(dsn.username, Some("u ser".to_string()));
1195        assert_eq!(dsn.password, Some("pa ss".to_string()));
1196    }
1197}