1#[cfg(feature = "runtime")]
4use crate::connect::connect;
5use crate::connect_raw::connect_raw;
6#[cfg(not(target_arch = "wasm32"))]
7use crate::keepalive::KeepaliveConfig;
8#[cfg(feature = "runtime")]
9use crate::tls::MakeTlsConnect;
10use crate::tls::TlsConnect;
11#[cfg(feature = "runtime")]
12use crate::Socket;
13use crate::{Client, Connection, Error};
14use std::borrow::Cow;
15#[cfg(unix)]
16use std::ffi::OsStr;
17use std::net::IpAddr;
18use std::ops::Deref;
19#[cfg(unix)]
20use std::os::unix::ffi::OsStrExt;
21#[cfg(unix)]
22use std::path::{Path, PathBuf};
23use std::str;
24use std::str::FromStr;
25use std::time::Duration;
26use std::{error, fmt, iter, mem};
27use tokio::io::{AsyncRead, AsyncWrite};
28
29#[derive(Debug, Copy, Clone, PartialEq, Eq)]
31#[non_exhaustive]
32pub enum TargetSessionAttrs {
33 Any,
35 ReadWrite,
37 ReadOnly,
39}
40
41#[derive(Debug, Copy, Clone, PartialEq, Eq)]
43#[non_exhaustive]
44pub enum SslMode {
45 Disable,
47 Prefer,
49 Require,
51}
52
53#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
58#[non_exhaustive]
59pub enum SslNegotiation {
60 #[default]
62 Postgres,
63 Direct,
65}
66
67#[derive(Debug, Copy, Clone, PartialEq, Eq)]
69#[non_exhaustive]
70pub enum ChannelBinding {
71 Disable,
73 Prefer,
75 Require,
77}
78
79#[derive(Debug, Copy, Clone, PartialEq, Eq)]
81#[non_exhaustive]
82pub enum LoadBalanceHosts {
83 Disable,
85 Random,
87}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
91pub enum Host {
92 Tcp(String),
94 #[cfg(unix)]
98 Unix(PathBuf),
99}
100
101#[derive(Clone, PartialEq, Eq)]
217pub struct Config {
218 pub(crate) user: Option<String>,
219 pub(crate) password: Option<Vec<u8>>,
220 pub(crate) dbname: Option<String>,
221 pub(crate) options: Option<String>,
222 pub(crate) application_name: Option<String>,
223 pub(crate) ssl_mode: SslMode,
224 pub(crate) ssl_negotiation: SslNegotiation,
225 pub(crate) host: Vec<Host>,
226 pub(crate) hostaddr: Vec<IpAddr>,
227 pub(crate) port: Vec<u16>,
228 pub(crate) connect_timeout: Option<Duration>,
229 pub(crate) tcp_user_timeout: Option<Duration>,
230 pub(crate) keepalives: bool,
231 #[cfg(not(target_arch = "wasm32"))]
232 pub(crate) keepalive_config: KeepaliveConfig,
233 pub(crate) target_session_attrs: TargetSessionAttrs,
234 pub(crate) channel_binding: ChannelBinding,
235 pub(crate) load_balance_hosts: LoadBalanceHosts,
236}
237
238impl Default for Config {
239 fn default() -> Config {
240 Config::new()
241 }
242}
243
244impl Config {
245 pub fn new() -> Config {
247 Config {
248 user: None,
249 password: None,
250 dbname: None,
251 options: None,
252 application_name: None,
253 ssl_mode: SslMode::Prefer,
254 ssl_negotiation: SslNegotiation::Postgres,
255 host: vec![],
256 hostaddr: vec![],
257 port: vec![],
258 connect_timeout: None,
259 tcp_user_timeout: None,
260 keepalives: true,
261 #[cfg(not(target_arch = "wasm32"))]
262 keepalive_config: KeepaliveConfig {
263 idle: Duration::from_secs(2 * 60 * 60),
264 interval: None,
265 retries: None,
266 },
267 target_session_attrs: TargetSessionAttrs::Any,
268 channel_binding: ChannelBinding::Prefer,
269 load_balance_hosts: LoadBalanceHosts::Disable,
270 }
271 }
272
273 pub fn user(&mut self, user: impl Into<String>) -> &mut Config {
277 self.user = Some(user.into());
278 self
279 }
280
281 pub fn get_user(&self) -> Option<&str> {
284 self.user.as_deref()
285 }
286
287 pub fn password<T>(&mut self, password: T) -> &mut Config
289 where
290 T: AsRef<[u8]>,
291 {
292 self.password = Some(password.as_ref().to_vec());
293 self
294 }
295
296 pub fn get_password(&self) -> Option<&[u8]> {
299 self.password.as_deref()
300 }
301
302 pub fn dbname(&mut self, dbname: impl Into<String>) -> &mut Config {
306 self.dbname = Some(dbname.into());
307 self
308 }
309
310 pub fn get_dbname(&self) -> Option<&str> {
313 self.dbname.as_deref()
314 }
315
316 pub fn options(&mut self, options: impl Into<String>) -> &mut Config {
318 self.options = Some(options.into());
319 self
320 }
321
322 pub fn get_options(&self) -> Option<&str> {
325 self.options.as_deref()
326 }
327
328 pub fn application_name(&mut self, application_name: impl Into<String>) -> &mut Config {
330 self.application_name = Some(application_name.into());
331 self
332 }
333
334 pub fn get_application_name(&self) -> Option<&str> {
337 self.application_name.as_deref()
338 }
339
340 pub fn ssl_mode(&mut self, ssl_mode: SslMode) -> &mut Config {
344 self.ssl_mode = ssl_mode;
345 self
346 }
347
348 pub fn get_ssl_mode(&self) -> SslMode {
350 self.ssl_mode
351 }
352
353 pub fn ssl_negotiation(&mut self, ssl_negotiation: SslNegotiation) -> &mut Config {
357 self.ssl_negotiation = ssl_negotiation;
358 self
359 }
360
361 pub fn get_ssl_negotiation(&self) -> SslNegotiation {
363 self.ssl_negotiation
364 }
365
366 pub fn host(&mut self, host: impl Into<String>) -> &mut Config {
372 let host = host.into();
373
374 #[cfg(unix)]
375 {
376 if host.starts_with('/') {
377 return self.host_path(host);
378 }
379 }
380
381 self.host.push(Host::Tcp(host));
382 self
383 }
384
385 pub fn get_hosts(&self) -> &[Host] {
387 &self.host
388 }
389
390 pub fn get_hostaddrs(&self) -> &[IpAddr] {
392 self.hostaddr.deref()
393 }
394
395 #[cfg(unix)]
399 pub fn host_path<T>(&mut self, host: T) -> &mut Config
400 where
401 T: AsRef<Path>,
402 {
403 self.host.push(Host::Unix(host.as_ref().to_path_buf()));
404 self
405 }
406
407 pub fn hostaddr(&mut self, hostaddr: IpAddr) -> &mut Config {
412 self.hostaddr.push(hostaddr);
413 self
414 }
415
416 pub fn port(&mut self, port: u16) -> &mut Config {
422 self.port.push(port);
423 self
424 }
425
426 pub fn get_ports(&self) -> &[u16] {
428 &self.port
429 }
430
431 pub fn connect_timeout(&mut self, connect_timeout: Duration) -> &mut Config {
436 self.connect_timeout = Some(connect_timeout);
437 self
438 }
439
440 pub fn get_connect_timeout(&self) -> Option<&Duration> {
443 self.connect_timeout.as_ref()
444 }
445
446 pub fn tcp_user_timeout(&mut self, tcp_user_timeout: Duration) -> &mut Config {
452 self.tcp_user_timeout = Some(tcp_user_timeout);
453 self
454 }
455
456 pub fn get_tcp_user_timeout(&self) -> Option<&Duration> {
459 self.tcp_user_timeout.as_ref()
460 }
461
462 pub fn keepalives(&mut self, keepalives: bool) -> &mut Config {
466 self.keepalives = keepalives;
467 self
468 }
469
470 pub fn get_keepalives(&self) -> bool {
472 self.keepalives
473 }
474
475 #[cfg(not(target_arch = "wasm32"))]
479 pub fn keepalives_idle(&mut self, keepalives_idle: Duration) -> &mut Config {
480 self.keepalive_config.idle = keepalives_idle;
481 self
482 }
483
484 #[cfg(not(target_arch = "wasm32"))]
487 pub fn get_keepalives_idle(&self) -> Duration {
488 self.keepalive_config.idle
489 }
490
491 #[cfg(not(target_arch = "wasm32"))]
496 pub fn keepalives_interval(&mut self, keepalives_interval: Duration) -> &mut Config {
497 self.keepalive_config.interval = Some(keepalives_interval);
498 self
499 }
500
501 #[cfg(not(target_arch = "wasm32"))]
503 pub fn get_keepalives_interval(&self) -> Option<Duration> {
504 self.keepalive_config.interval
505 }
506
507 #[cfg(not(target_arch = "wasm32"))]
511 pub fn keepalives_retries(&mut self, keepalives_retries: u32) -> &mut Config {
512 self.keepalive_config.retries = Some(keepalives_retries);
513 self
514 }
515
516 #[cfg(not(target_arch = "wasm32"))]
518 pub fn get_keepalives_retries(&self) -> Option<u32> {
519 self.keepalive_config.retries
520 }
521
522 pub fn target_session_attrs(
527 &mut self,
528 target_session_attrs: TargetSessionAttrs,
529 ) -> &mut Config {
530 self.target_session_attrs = target_session_attrs;
531 self
532 }
533
534 pub fn get_target_session_attrs(&self) -> TargetSessionAttrs {
536 self.target_session_attrs
537 }
538
539 pub fn channel_binding(&mut self, channel_binding: ChannelBinding) -> &mut Config {
543 self.channel_binding = channel_binding;
544 self
545 }
546
547 pub fn get_channel_binding(&self) -> ChannelBinding {
549 self.channel_binding
550 }
551
552 pub fn load_balance_hosts(&mut self, load_balance_hosts: LoadBalanceHosts) -> &mut Config {
556 self.load_balance_hosts = load_balance_hosts;
557 self
558 }
559
560 pub fn get_load_balance_hosts(&self) -> LoadBalanceHosts {
562 self.load_balance_hosts
563 }
564
565 fn param(&mut self, key: &str, value: &str) -> Result<(), Error> {
566 match key {
567 "user" => {
568 self.user(value);
569 }
570 "password" => {
571 self.password(value);
572 }
573 "dbname" => {
574 self.dbname(value);
575 }
576 "options" => {
577 self.options(value);
578 }
579 "application_name" => {
580 self.application_name(value);
581 }
582 "sslmode" => {
583 let mode = match value {
584 "disable" => SslMode::Disable,
585 "prefer" => SslMode::Prefer,
586 "require" => SslMode::Require,
587 _ => return Err(Error::config_parse(Box::new(InvalidValue("sslmode")))),
588 };
589 self.ssl_mode(mode);
590 }
591 "sslnegotiation" => {
592 let mode = match value {
593 "postgres" => SslNegotiation::Postgres,
594 "direct" => SslNegotiation::Direct,
595 _ => {
596 return Err(Error::config_parse(Box::new(InvalidValue(
597 "sslnegotiation",
598 ))))
599 }
600 };
601 self.ssl_negotiation(mode);
602 }
603 "host" => {
604 for host in value.split(',') {
605 self.host(host);
606 }
607 }
608 "hostaddr" => {
609 for hostaddr in value.split(',') {
610 let addr = hostaddr
611 .parse()
612 .map_err(|_| Error::config_parse(Box::new(InvalidValue("hostaddr"))))?;
613 self.hostaddr(addr);
614 }
615 }
616 "port" => {
617 for port in value.split(',') {
618 let port = if port.is_empty() {
619 5432
620 } else {
621 port.parse()
622 .map_err(|_| Error::config_parse(Box::new(InvalidValue("port"))))?
623 };
624 self.port(port);
625 }
626 }
627 "connect_timeout" => {
628 let timeout = value
629 .parse::<i64>()
630 .map_err(|_| Error::config_parse(Box::new(InvalidValue("connect_timeout"))))?;
631 if timeout > 0 {
632 self.connect_timeout(Duration::from_secs(timeout as u64));
633 }
634 }
635 "tcp_user_timeout" => {
636 let timeout = value
637 .parse::<i64>()
638 .map_err(|_| Error::config_parse(Box::new(InvalidValue("tcp_user_timeout"))))?;
639 if timeout > 0 {
640 self.tcp_user_timeout(Duration::from_secs(timeout as u64));
641 }
642 }
643 #[cfg(not(target_arch = "wasm32"))]
644 "keepalives" => {
645 let keepalives = value
646 .parse::<u64>()
647 .map_err(|_| Error::config_parse(Box::new(InvalidValue("keepalives"))))?;
648 self.keepalives(keepalives != 0);
649 }
650 #[cfg(not(target_arch = "wasm32"))]
651 "keepalives_idle" => {
652 let keepalives_idle = value
653 .parse::<i64>()
654 .map_err(|_| Error::config_parse(Box::new(InvalidValue("keepalives_idle"))))?;
655 if keepalives_idle > 0 {
656 self.keepalives_idle(Duration::from_secs(keepalives_idle as u64));
657 }
658 }
659 #[cfg(not(target_arch = "wasm32"))]
660 "keepalives_interval" => {
661 let keepalives_interval = value.parse::<i64>().map_err(|_| {
662 Error::config_parse(Box::new(InvalidValue("keepalives_interval")))
663 })?;
664 if keepalives_interval > 0 {
665 self.keepalives_interval(Duration::from_secs(keepalives_interval as u64));
666 }
667 }
668 #[cfg(not(target_arch = "wasm32"))]
669 "keepalives_retries" => {
670 let keepalives_retries = value.parse::<u32>().map_err(|_| {
671 Error::config_parse(Box::new(InvalidValue("keepalives_retries")))
672 })?;
673 self.keepalives_retries(keepalives_retries);
674 }
675 "target_session_attrs" => {
676 let target_session_attrs = match value {
677 "any" => TargetSessionAttrs::Any,
678 "read-write" => TargetSessionAttrs::ReadWrite,
679 "read-only" => TargetSessionAttrs::ReadOnly,
680 _ => {
681 return Err(Error::config_parse(Box::new(InvalidValue(
682 "target_session_attrs",
683 ))));
684 }
685 };
686 self.target_session_attrs(target_session_attrs);
687 }
688 "channel_binding" => {
689 let channel_binding = match value {
690 "disable" => ChannelBinding::Disable,
691 "prefer" => ChannelBinding::Prefer,
692 "require" => ChannelBinding::Require,
693 _ => {
694 return Err(Error::config_parse(Box::new(InvalidValue(
695 "channel_binding",
696 ))))
697 }
698 };
699 self.channel_binding(channel_binding);
700 }
701 "load_balance_hosts" => {
702 let load_balance_hosts = match value {
703 "disable" => LoadBalanceHosts::Disable,
704 "random" => LoadBalanceHosts::Random,
705 _ => {
706 return Err(Error::config_parse(Box::new(InvalidValue(
707 "load_balance_hosts",
708 ))))
709 }
710 };
711 self.load_balance_hosts(load_balance_hosts);
712 }
713 key => {
714 return Err(Error::config_parse(Box::new(UnknownOption(
715 key.to_string(),
716 ))));
717 }
718 }
719
720 Ok(())
721 }
722
723 #[cfg(feature = "runtime")]
727 pub async fn connect<T>(&self, tls: T) -> Result<(Client, Connection<Socket, T::Stream>), Error>
728 where
729 T: MakeTlsConnect<Socket>,
730 {
731 connect(tls, self).await
732 }
733
734 pub async fn connect_raw<S, T>(
738 &self,
739 stream: S,
740 tls: T,
741 ) -> Result<(Client, Connection<S, T::Stream>), Error>
742 where
743 S: AsyncRead + AsyncWrite + Unpin,
744 T: TlsConnect<S>,
745 {
746 connect_raw(stream, tls, true, self).await
747 }
748}
749
750impl FromStr for Config {
751 type Err = Error;
752
753 fn from_str(s: &str) -> Result<Config, Error> {
754 match UrlParser::parse(s)? {
755 Some(config) => Ok(config),
756 None => Parser::parse(s),
757 }
758 }
759}
760
761impl fmt::Debug for Config {
763 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
764 struct Redaction {}
765 impl fmt::Debug for Redaction {
766 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
767 write!(f, "_")
768 }
769 }
770
771 let mut config_dbg = &mut f.debug_struct("Config");
772 config_dbg = config_dbg
773 .field("user", &self.user)
774 .field("password", &self.password.as_ref().map(|_| Redaction {}))
775 .field("dbname", &self.dbname)
776 .field("options", &self.options)
777 .field("application_name", &self.application_name)
778 .field("ssl_mode", &self.ssl_mode)
779 .field("host", &self.host)
780 .field("hostaddr", &self.hostaddr)
781 .field("port", &self.port)
782 .field("connect_timeout", &self.connect_timeout)
783 .field("tcp_user_timeout", &self.tcp_user_timeout)
784 .field("keepalives", &self.keepalives);
785
786 #[cfg(not(target_arch = "wasm32"))]
787 {
788 config_dbg = config_dbg
789 .field("keepalives_idle", &self.keepalive_config.idle)
790 .field("keepalives_interval", &self.keepalive_config.interval)
791 .field("keepalives_retries", &self.keepalive_config.retries);
792 }
793
794 config_dbg
795 .field("target_session_attrs", &self.target_session_attrs)
796 .field("channel_binding", &self.channel_binding)
797 .field("load_balance_hosts", &self.load_balance_hosts)
798 .finish()
799 }
800}
801
802#[derive(Debug)]
803struct UnknownOption(String);
804
805impl fmt::Display for UnknownOption {
806 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
807 write!(fmt, "unknown option `{}`", self.0)
808 }
809}
810
811impl error::Error for UnknownOption {}
812
813#[derive(Debug)]
814struct InvalidValue(&'static str);
815
816impl fmt::Display for InvalidValue {
817 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
818 write!(fmt, "invalid value for option `{}`", self.0)
819 }
820}
821
822impl error::Error for InvalidValue {}
823
824struct Parser<'a> {
825 s: &'a str,
826 it: iter::Peekable<str::CharIndices<'a>>,
827}
828
829impl<'a> Parser<'a> {
830 fn parse(s: &'a str) -> Result<Config, Error> {
831 let mut parser = Parser {
832 s,
833 it: s.char_indices().peekable(),
834 };
835
836 let mut config = Config::new();
837
838 while let Some((key, value)) = parser.parameter()? {
839 config.param(key, &value)?;
840 }
841
842 Ok(config)
843 }
844
845 fn skip_ws(&mut self) {
846 self.take_while(char::is_whitespace);
847 }
848
849 fn take_while<F>(&mut self, f: F) -> &'a str
850 where
851 F: Fn(char) -> bool,
852 {
853 let start = match self.it.peek() {
854 Some(&(i, _)) => i,
855 None => return "",
856 };
857
858 loop {
859 match self.it.peek() {
860 Some(&(_, c)) if f(c) => {
861 self.it.next();
862 }
863 Some(&(i, _)) => return &self.s[start..i],
864 None => return &self.s[start..],
865 }
866 }
867 }
868
869 fn eat(&mut self, target: char) -> Result<(), Error> {
870 match self.it.next() {
871 Some((_, c)) if c == target => Ok(()),
872 Some((i, c)) => {
873 let m = format!(
874 "unexpected character at byte {}: expected `{}` but got `{}`",
875 i, target, c
876 );
877 Err(Error::config_parse(m.into()))
878 }
879 None => Err(Error::config_parse("unexpected EOF".into())),
880 }
881 }
882
883 fn eat_if(&mut self, target: char) -> bool {
884 match self.it.peek() {
885 Some(&(_, c)) if c == target => {
886 self.it.next();
887 true
888 }
889 _ => false,
890 }
891 }
892
893 fn keyword(&mut self) -> Option<&'a str> {
894 let s = self.take_while(|c| match c {
895 c if c.is_whitespace() => false,
896 '=' => false,
897 _ => true,
898 });
899
900 if s.is_empty() {
901 None
902 } else {
903 Some(s)
904 }
905 }
906
907 fn value(&mut self) -> Result<String, Error> {
908 let value = if self.eat_if('\'') {
909 let value = self.quoted_value()?;
910 self.eat('\'')?;
911 value
912 } else {
913 self.simple_value()?
914 };
915
916 Ok(value)
917 }
918
919 fn simple_value(&mut self) -> Result<String, Error> {
920 let mut value = String::new();
921
922 while let Some(&(_, c)) = self.it.peek() {
923 if c.is_whitespace() {
924 break;
925 }
926
927 self.it.next();
928 if c == '\\' {
929 if let Some((_, c2)) = self.it.next() {
930 value.push(c2);
931 }
932 } else {
933 value.push(c);
934 }
935 }
936
937 if value.is_empty() {
938 return Err(Error::config_parse("unexpected EOF".into()));
939 }
940
941 Ok(value)
942 }
943
944 fn quoted_value(&mut self) -> Result<String, Error> {
945 let mut value = String::new();
946
947 while let Some(&(_, c)) = self.it.peek() {
948 if c == '\'' {
949 return Ok(value);
950 }
951
952 self.it.next();
953 if c == '\\' {
954 if let Some((_, c2)) = self.it.next() {
955 value.push(c2);
956 }
957 } else {
958 value.push(c);
959 }
960 }
961
962 Err(Error::config_parse(
963 "unterminated quoted connection parameter value".into(),
964 ))
965 }
966
967 fn parameter(&mut self) -> Result<Option<(&'a str, String)>, Error> {
968 self.skip_ws();
969 let keyword = match self.keyword() {
970 Some(keyword) => keyword,
971 None => return Ok(None),
972 };
973 self.skip_ws();
974 self.eat('=')?;
975 self.skip_ws();
976 let value = self.value()?;
977
978 Ok(Some((keyword, value)))
979 }
980}
981
982struct UrlParser<'a> {
984 s: &'a str,
985 config: Config,
986}
987
988impl<'a> UrlParser<'a> {
989 fn parse(s: &'a str) -> Result<Option<Config>, Error> {
990 let s = match Self::remove_url_prefix(s) {
991 Some(s) => s,
992 None => return Ok(None),
993 };
994
995 let mut parser = UrlParser {
996 s,
997 config: Config::new(),
998 };
999
1000 parser.parse_credentials()?;
1001 parser.parse_host()?;
1002 parser.parse_path()?;
1003 parser.parse_params()?;
1004
1005 Ok(Some(parser.config))
1006 }
1007
1008 fn remove_url_prefix(s: &str) -> Option<&str> {
1009 for prefix in &["postgres://", "postgresql://"] {
1010 if let Some(stripped) = s.strip_prefix(prefix) {
1011 return Some(stripped);
1012 }
1013 }
1014
1015 None
1016 }
1017
1018 fn take_until(&mut self, end: &[char]) -> Option<&'a str> {
1019 match self.s.find(end) {
1020 Some(pos) => {
1021 let (head, tail) = self.s.split_at(pos);
1022 self.s = tail;
1023 Some(head)
1024 }
1025 None => None,
1026 }
1027 }
1028
1029 fn take_all(&mut self) -> &'a str {
1030 mem::take(&mut self.s)
1031 }
1032
1033 fn eat_byte(&mut self) {
1034 self.s = &self.s[1..];
1035 }
1036
1037 fn parse_credentials(&mut self) -> Result<(), Error> {
1038 let creds = match self.take_until(&['@']) {
1039 Some(creds) => creds,
1040 None => return Ok(()),
1041 };
1042 self.eat_byte();
1043
1044 let mut it = creds.splitn(2, ':');
1045 let user = self.decode(it.next().unwrap())?;
1046 self.config.user(user);
1047
1048 if let Some(password) = it.next() {
1049 let password = Cow::from(percent_encoding::percent_decode(password.as_bytes()));
1050 self.config.password(password);
1051 }
1052
1053 Ok(())
1054 }
1055
1056 fn parse_host(&mut self) -> Result<(), Error> {
1057 let host = match self.take_until(&['/', '?']) {
1058 Some(host) => host,
1059 None => self.take_all(),
1060 };
1061
1062 if host.is_empty() {
1063 return Ok(());
1064 }
1065
1066 for chunk in host.split(',') {
1067 let (host, port) = if chunk.starts_with('[') {
1068 let idx = match chunk.find(']') {
1069 Some(idx) => idx,
1070 None => return Err(Error::config_parse(InvalidValue("host").into())),
1071 };
1072
1073 let host = &chunk[1..idx];
1074 let remaining = &chunk[idx + 1..];
1075 let port = if let Some(port) = remaining.strip_prefix(':') {
1076 Some(port)
1077 } else if remaining.is_empty() {
1078 None
1079 } else {
1080 return Err(Error::config_parse(InvalidValue("host").into()));
1081 };
1082
1083 (host, port)
1084 } else {
1085 let mut it = chunk.splitn(2, ':');
1086 (it.next().unwrap(), it.next())
1087 };
1088
1089 self.host_param(host)?;
1090 let port = self.decode(port.unwrap_or("5432"))?;
1091 self.config.param("port", &port)?;
1092 }
1093
1094 Ok(())
1095 }
1096
1097 fn parse_path(&mut self) -> Result<(), Error> {
1098 if !self.s.starts_with('/') {
1099 return Ok(());
1100 }
1101 self.eat_byte();
1102
1103 let dbname = match self.take_until(&['?']) {
1104 Some(dbname) => dbname,
1105 None => self.take_all(),
1106 };
1107
1108 if !dbname.is_empty() {
1109 self.config.dbname(self.decode(dbname)?);
1110 }
1111
1112 Ok(())
1113 }
1114
1115 fn parse_params(&mut self) -> Result<(), Error> {
1116 if !self.s.starts_with('?') {
1117 return Ok(());
1118 }
1119 self.eat_byte();
1120
1121 while !self.s.is_empty() {
1122 let key = match self.take_until(&['=']) {
1123 Some(key) => self.decode(key)?,
1124 None => return Err(Error::config_parse("unterminated parameter".into())),
1125 };
1126 self.eat_byte();
1127
1128 let value = match self.take_until(&['&']) {
1129 Some(value) => {
1130 self.eat_byte();
1131 value
1132 }
1133 None => self.take_all(),
1134 };
1135
1136 if key == "host" {
1137 self.host_param(value)?;
1138 } else {
1139 let value = self.decode(value)?;
1140 self.config.param(&key, &value)?;
1141 }
1142 }
1143
1144 Ok(())
1145 }
1146
1147 #[cfg(unix)]
1148 fn host_param(&mut self, s: &str) -> Result<(), Error> {
1149 let decoded = Cow::from(percent_encoding::percent_decode(s.as_bytes()));
1150 if decoded.first() == Some(&b'/') {
1151 self.config.host_path(OsStr::from_bytes(&decoded));
1152 } else {
1153 let decoded = str::from_utf8(&decoded).map_err(|e| Error::config_parse(Box::new(e)))?;
1154 self.config.host(decoded);
1155 }
1156
1157 Ok(())
1158 }
1159
1160 #[cfg(not(unix))]
1161 fn host_param(&mut self, s: &str) -> Result<(), Error> {
1162 let s = self.decode(s)?;
1163 self.config.param("host", &s)
1164 }
1165
1166 fn decode(&self, s: &'a str) -> Result<Cow<'a, str>, Error> {
1167 percent_encoding::percent_decode(s.as_bytes())
1168 .decode_utf8()
1169 .map_err(|e| Error::config_parse(e.into()))
1170 }
1171}
1172
1173#[cfg(test)]
1174mod tests {
1175 use std::net::IpAddr;
1176
1177 use crate::{config::Host, Config};
1178
1179 #[test]
1180 fn test_simple_parsing() {
1181 let s = "user=pass_user dbname=postgres host=host1,host2 hostaddr=127.0.0.1,127.0.0.2 port=26257";
1182 let config = s.parse::<Config>().unwrap();
1183 assert_eq!(Some("pass_user"), config.get_user());
1184 assert_eq!(Some("postgres"), config.get_dbname());
1185 assert_eq!(
1186 [
1187 Host::Tcp("host1".to_string()),
1188 Host::Tcp("host2".to_string())
1189 ],
1190 config.get_hosts(),
1191 );
1192
1193 assert_eq!(
1194 [
1195 "127.0.0.1".parse::<IpAddr>().unwrap(),
1196 "127.0.0.2".parse::<IpAddr>().unwrap()
1197 ],
1198 config.get_hostaddrs(),
1199 );
1200
1201 assert_eq!(1, 1);
1202 }
1203
1204 #[test]
1205 fn test_invalid_hostaddr_parsing() {
1206 let s = "user=pass_user dbname=postgres host=host1 hostaddr=127.0.0 port=26257";
1207 s.parse::<Config>().err().unwrap();
1208 }
1209}