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}
38
39#[derive(Debug, Copy, Clone, PartialEq, Eq)]
41#[non_exhaustive]
42pub enum SslMode {
43 Disable,
45 Prefer,
47 Require,
49}
50
51#[derive(Debug, Copy, Clone, PartialEq, Eq)]
53#[non_exhaustive]
54pub enum ChannelBinding {
55 Disable,
57 Prefer,
59 Require,
61}
62
63#[derive(Debug, Copy, Clone, PartialEq, Eq)]
65#[non_exhaustive]
66pub enum LoadBalanceHosts {
67 Disable,
69 Random,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
75pub enum Host {
76 Tcp(String),
78 #[cfg(unix)]
82 Unix(PathBuf),
83}
84
85#[derive(Clone, PartialEq, Eq)]
192pub struct Config {
193 pub(crate) user: Option<String>,
194 pub(crate) password: Option<Vec<u8>>,
195 pub(crate) dbname: Option<String>,
196 pub(crate) options: Option<String>,
197 pub(crate) application_name: Option<String>,
198 pub(crate) ssl_mode: SslMode,
199 pub(crate) host: Vec<Host>,
200 pub(crate) hostaddr: Vec<IpAddr>,
201 pub(crate) port: Vec<u16>,
202 pub(crate) connect_timeout: Option<Duration>,
203 pub(crate) tcp_user_timeout: Option<Duration>,
204 pub(crate) keepalives: bool,
205 #[cfg(not(target_arch = "wasm32"))]
206 pub(crate) keepalive_config: KeepaliveConfig,
207 pub(crate) target_session_attrs: TargetSessionAttrs,
208 pub(crate) channel_binding: ChannelBinding,
209 pub(crate) load_balance_hosts: LoadBalanceHosts,
210}
211
212impl Default for Config {
213 fn default() -> Config {
214 Config::new()
215 }
216}
217
218impl Config {
219 pub fn new() -> Config {
221 Config {
222 user: None,
223 password: None,
224 dbname: None,
225 options: None,
226 application_name: None,
227 ssl_mode: SslMode::Prefer,
228 host: vec![],
229 hostaddr: vec![],
230 port: vec![],
231 connect_timeout: None,
232 tcp_user_timeout: None,
233 keepalives: true,
234 #[cfg(not(target_arch = "wasm32"))]
235 keepalive_config: KeepaliveConfig {
236 idle: Duration::from_secs(2 * 60 * 60),
237 interval: None,
238 retries: None,
239 },
240 target_session_attrs: TargetSessionAttrs::Any,
241 channel_binding: ChannelBinding::Prefer,
242 load_balance_hosts: LoadBalanceHosts::Disable,
243 }
244 }
245
246 pub fn user(&mut self, user: &str) -> &mut Config {
250 self.user = Some(user.to_string());
251 self
252 }
253
254 pub fn get_user(&self) -> Option<&str> {
257 self.user.as_deref()
258 }
259
260 pub fn password<T>(&mut self, password: T) -> &mut Config
262 where
263 T: AsRef<[u8]>,
264 {
265 self.password = Some(password.as_ref().to_vec());
266 self
267 }
268
269 pub fn get_password(&self) -> Option<&[u8]> {
272 self.password.as_deref()
273 }
274
275 pub fn dbname(&mut self, dbname: &str) -> &mut Config {
279 self.dbname = Some(dbname.to_string());
280 self
281 }
282
283 pub fn get_dbname(&self) -> Option<&str> {
286 self.dbname.as_deref()
287 }
288
289 pub fn options(&mut self, options: &str) -> &mut Config {
291 self.options = Some(options.to_string());
292 self
293 }
294
295 pub fn get_options(&self) -> Option<&str> {
298 self.options.as_deref()
299 }
300
301 pub fn application_name(&mut self, application_name: &str) -> &mut Config {
303 self.application_name = Some(application_name.to_string());
304 self
305 }
306
307 pub fn get_application_name(&self) -> Option<&str> {
310 self.application_name.as_deref()
311 }
312
313 pub fn ssl_mode(&mut self, ssl_mode: SslMode) -> &mut Config {
317 self.ssl_mode = ssl_mode;
318 self
319 }
320
321 pub fn get_ssl_mode(&self) -> SslMode {
323 self.ssl_mode
324 }
325
326 pub fn host(&mut self, host: &str) -> &mut Config {
332 #[cfg(unix)]
333 {
334 if host.starts_with('/') {
335 return self.host_path(host);
336 }
337 }
338
339 self.host.push(Host::Tcp(host.to_string()));
340 self
341 }
342
343 pub fn get_hosts(&self) -> &[Host] {
345 &self.host
346 }
347
348 pub fn get_hostaddrs(&self) -> &[IpAddr] {
350 self.hostaddr.deref()
351 }
352
353 #[cfg(unix)]
357 pub fn host_path<T>(&mut self, host: T) -> &mut Config
358 where
359 T: AsRef<Path>,
360 {
361 self.host.push(Host::Unix(host.as_ref().to_path_buf()));
362 self
363 }
364
365 pub fn hostaddr(&mut self, hostaddr: IpAddr) -> &mut Config {
370 self.hostaddr.push(hostaddr);
371 self
372 }
373
374 pub fn port(&mut self, port: u16) -> &mut Config {
380 self.port.push(port);
381 self
382 }
383
384 pub fn get_ports(&self) -> &[u16] {
386 &self.port
387 }
388
389 pub fn connect_timeout(&mut self, connect_timeout: Duration) -> &mut Config {
394 self.connect_timeout = Some(connect_timeout);
395 self
396 }
397
398 pub fn get_connect_timeout(&self) -> Option<&Duration> {
401 self.connect_timeout.as_ref()
402 }
403
404 pub fn tcp_user_timeout(&mut self, tcp_user_timeout: Duration) -> &mut Config {
410 self.tcp_user_timeout = Some(tcp_user_timeout);
411 self
412 }
413
414 pub fn get_tcp_user_timeout(&self) -> Option<&Duration> {
417 self.tcp_user_timeout.as_ref()
418 }
419
420 pub fn keepalives(&mut self, keepalives: bool) -> &mut Config {
424 self.keepalives = keepalives;
425 self
426 }
427
428 pub fn get_keepalives(&self) -> bool {
430 self.keepalives
431 }
432
433 #[cfg(not(target_arch = "wasm32"))]
437 pub fn keepalives_idle(&mut self, keepalives_idle: Duration) -> &mut Config {
438 self.keepalive_config.idle = keepalives_idle;
439 self
440 }
441
442 #[cfg(not(target_arch = "wasm32"))]
445 pub fn get_keepalives_idle(&self) -> Duration {
446 self.keepalive_config.idle
447 }
448
449 #[cfg(not(target_arch = "wasm32"))]
454 pub fn keepalives_interval(&mut self, keepalives_interval: Duration) -> &mut Config {
455 self.keepalive_config.interval = Some(keepalives_interval);
456 self
457 }
458
459 #[cfg(not(target_arch = "wasm32"))]
461 pub fn get_keepalives_interval(&self) -> Option<Duration> {
462 self.keepalive_config.interval
463 }
464
465 #[cfg(not(target_arch = "wasm32"))]
469 pub fn keepalives_retries(&mut self, keepalives_retries: u32) -> &mut Config {
470 self.keepalive_config.retries = Some(keepalives_retries);
471 self
472 }
473
474 #[cfg(not(target_arch = "wasm32"))]
476 pub fn get_keepalives_retries(&self) -> Option<u32> {
477 self.keepalive_config.retries
478 }
479
480 pub fn target_session_attrs(
485 &mut self,
486 target_session_attrs: TargetSessionAttrs,
487 ) -> &mut Config {
488 self.target_session_attrs = target_session_attrs;
489 self
490 }
491
492 pub fn get_target_session_attrs(&self) -> TargetSessionAttrs {
494 self.target_session_attrs
495 }
496
497 pub fn channel_binding(&mut self, channel_binding: ChannelBinding) -> &mut Config {
501 self.channel_binding = channel_binding;
502 self
503 }
504
505 pub fn get_channel_binding(&self) -> ChannelBinding {
507 self.channel_binding
508 }
509
510 pub fn load_balance_hosts(&mut self, load_balance_hosts: LoadBalanceHosts) -> &mut Config {
514 self.load_balance_hosts = load_balance_hosts;
515 self
516 }
517
518 pub fn get_load_balance_hosts(&self) -> LoadBalanceHosts {
520 self.load_balance_hosts
521 }
522
523 fn param(&mut self, key: &str, value: &str) -> Result<(), Error> {
524 match key {
525 "user" => {
526 self.user(value);
527 }
528 "password" => {
529 self.password(value);
530 }
531 "dbname" => {
532 self.dbname(value);
533 }
534 "options" => {
535 self.options(value);
536 }
537 "application_name" => {
538 self.application_name(value);
539 }
540 "sslmode" => {
541 let mode = match value {
542 "disable" => SslMode::Disable,
543 "prefer" => SslMode::Prefer,
544 "require" => SslMode::Require,
545 _ => return Err(Error::config_parse(Box::new(InvalidValue("sslmode")))),
546 };
547 self.ssl_mode(mode);
548 }
549 "host" => {
550 for host in value.split(',') {
551 self.host(host);
552 }
553 }
554 "hostaddr" => {
555 for hostaddr in value.split(',') {
556 let addr = hostaddr
557 .parse()
558 .map_err(|_| Error::config_parse(Box::new(InvalidValue("hostaddr"))))?;
559 self.hostaddr(addr);
560 }
561 }
562 "port" => {
563 for port in value.split(',') {
564 let port = if port.is_empty() {
565 5432
566 } else {
567 port.parse()
568 .map_err(|_| Error::config_parse(Box::new(InvalidValue("port"))))?
569 };
570 self.port(port);
571 }
572 }
573 "connect_timeout" => {
574 let timeout = value
575 .parse::<i64>()
576 .map_err(|_| Error::config_parse(Box::new(InvalidValue("connect_timeout"))))?;
577 if timeout > 0 {
578 self.connect_timeout(Duration::from_secs(timeout as u64));
579 }
580 }
581 "tcp_user_timeout" => {
582 let timeout = value
583 .parse::<i64>()
584 .map_err(|_| Error::config_parse(Box::new(InvalidValue("tcp_user_timeout"))))?;
585 if timeout > 0 {
586 self.tcp_user_timeout(Duration::from_secs(timeout as u64));
587 }
588 }
589 #[cfg(not(target_arch = "wasm32"))]
590 "keepalives" => {
591 let keepalives = value
592 .parse::<u64>()
593 .map_err(|_| Error::config_parse(Box::new(InvalidValue("keepalives"))))?;
594 self.keepalives(keepalives != 0);
595 }
596 #[cfg(not(target_arch = "wasm32"))]
597 "keepalives_idle" => {
598 let keepalives_idle = value
599 .parse::<i64>()
600 .map_err(|_| Error::config_parse(Box::new(InvalidValue("keepalives_idle"))))?;
601 if keepalives_idle > 0 {
602 self.keepalives_idle(Duration::from_secs(keepalives_idle as u64));
603 }
604 }
605 #[cfg(not(target_arch = "wasm32"))]
606 "keepalives_interval" => {
607 let keepalives_interval = value.parse::<i64>().map_err(|_| {
608 Error::config_parse(Box::new(InvalidValue("keepalives_interval")))
609 })?;
610 if keepalives_interval > 0 {
611 self.keepalives_interval(Duration::from_secs(keepalives_interval as u64));
612 }
613 }
614 #[cfg(not(target_arch = "wasm32"))]
615 "keepalives_retries" => {
616 let keepalives_retries = value.parse::<u32>().map_err(|_| {
617 Error::config_parse(Box::new(InvalidValue("keepalives_retries")))
618 })?;
619 self.keepalives_retries(keepalives_retries);
620 }
621 "target_session_attrs" => {
622 let target_session_attrs = match value {
623 "any" => TargetSessionAttrs::Any,
624 "read-write" => TargetSessionAttrs::ReadWrite,
625 _ => {
626 return Err(Error::config_parse(Box::new(InvalidValue(
627 "target_session_attrs",
628 ))));
629 }
630 };
631 self.target_session_attrs(target_session_attrs);
632 }
633 "channel_binding" => {
634 let channel_binding = match value {
635 "disable" => ChannelBinding::Disable,
636 "prefer" => ChannelBinding::Prefer,
637 "require" => ChannelBinding::Require,
638 _ => {
639 return Err(Error::config_parse(Box::new(InvalidValue(
640 "channel_binding",
641 ))))
642 }
643 };
644 self.channel_binding(channel_binding);
645 }
646 "load_balance_hosts" => {
647 let load_balance_hosts = match value {
648 "disable" => LoadBalanceHosts::Disable,
649 "random" => LoadBalanceHosts::Random,
650 _ => {
651 return Err(Error::config_parse(Box::new(InvalidValue(
652 "load_balance_hosts",
653 ))))
654 }
655 };
656 self.load_balance_hosts(load_balance_hosts);
657 }
658 key => {
659 return Err(Error::config_parse(Box::new(UnknownOption(
660 key.to_string(),
661 ))));
662 }
663 }
664
665 Ok(())
666 }
667
668 #[cfg(feature = "runtime")]
672 pub async fn connect<T>(&self, tls: T) -> Result<(Client, Connection<Socket, T::Stream>), Error>
673 where
674 T: MakeTlsConnect<Socket>,
675 {
676 connect(tls, self).await
677 }
678
679 pub async fn connect_raw<S, T>(
683 &self,
684 stream: S,
685 tls: T,
686 ) -> Result<(Client, Connection<S, T::Stream>), Error>
687 where
688 S: AsyncRead + AsyncWrite + Unpin,
689 T: TlsConnect<S>,
690 {
691 connect_raw(stream, tls, true, self).await
692 }
693}
694
695impl FromStr for Config {
696 type Err = Error;
697
698 fn from_str(s: &str) -> Result<Config, Error> {
699 match UrlParser::parse(s)? {
700 Some(config) => Ok(config),
701 None => Parser::parse(s),
702 }
703 }
704}
705
706impl fmt::Debug for Config {
708 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
709 struct Redaction {}
710 impl fmt::Debug for Redaction {
711 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
712 write!(f, "_")
713 }
714 }
715
716 let mut config_dbg = &mut f.debug_struct("Config");
717 config_dbg = config_dbg
718 .field("user", &self.user)
719 .field("password", &self.password.as_ref().map(|_| Redaction {}))
720 .field("dbname", &self.dbname)
721 .field("options", &self.options)
722 .field("application_name", &self.application_name)
723 .field("ssl_mode", &self.ssl_mode)
724 .field("host", &self.host)
725 .field("hostaddr", &self.hostaddr)
726 .field("port", &self.port)
727 .field("connect_timeout", &self.connect_timeout)
728 .field("tcp_user_timeout", &self.tcp_user_timeout)
729 .field("keepalives", &self.keepalives);
730
731 #[cfg(not(target_arch = "wasm32"))]
732 {
733 config_dbg = config_dbg
734 .field("keepalives_idle", &self.keepalive_config.idle)
735 .field("keepalives_interval", &self.keepalive_config.interval)
736 .field("keepalives_retries", &self.keepalive_config.retries);
737 }
738
739 config_dbg
740 .field("target_session_attrs", &self.target_session_attrs)
741 .field("channel_binding", &self.channel_binding)
742 .finish()
743 }
744}
745
746#[derive(Debug)]
747struct UnknownOption(String);
748
749impl fmt::Display for UnknownOption {
750 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
751 write!(fmt, "unknown option `{}`", self.0)
752 }
753}
754
755impl error::Error for UnknownOption {}
756
757#[derive(Debug)]
758struct InvalidValue(&'static str);
759
760impl fmt::Display for InvalidValue {
761 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
762 write!(fmt, "invalid value for option `{}`", self.0)
763 }
764}
765
766impl error::Error for InvalidValue {}
767
768struct Parser<'a> {
769 s: &'a str,
770 it: iter::Peekable<str::CharIndices<'a>>,
771}
772
773impl<'a> Parser<'a> {
774 fn parse(s: &'a str) -> Result<Config, Error> {
775 let mut parser = Parser {
776 s,
777 it: s.char_indices().peekable(),
778 };
779
780 let mut config = Config::new();
781
782 while let Some((key, value)) = parser.parameter()? {
783 config.param(key, &value)?;
784 }
785
786 Ok(config)
787 }
788
789 fn skip_ws(&mut self) {
790 self.take_while(char::is_whitespace);
791 }
792
793 fn take_while<F>(&mut self, f: F) -> &'a str
794 where
795 F: Fn(char) -> bool,
796 {
797 let start = match self.it.peek() {
798 Some(&(i, _)) => i,
799 None => return "",
800 };
801
802 loop {
803 match self.it.peek() {
804 Some(&(_, c)) if f(c) => {
805 self.it.next();
806 }
807 Some(&(i, _)) => return &self.s[start..i],
808 None => return &self.s[start..],
809 }
810 }
811 }
812
813 fn eat(&mut self, target: char) -> Result<(), Error> {
814 match self.it.next() {
815 Some((_, c)) if c == target => Ok(()),
816 Some((i, c)) => {
817 let m = format!(
818 "unexpected character at byte {}: expected `{}` but got `{}`",
819 i, target, c
820 );
821 Err(Error::config_parse(m.into()))
822 }
823 None => Err(Error::config_parse("unexpected EOF".into())),
824 }
825 }
826
827 fn eat_if(&mut self, target: char) -> bool {
828 match self.it.peek() {
829 Some(&(_, c)) if c == target => {
830 self.it.next();
831 true
832 }
833 _ => false,
834 }
835 }
836
837 fn keyword(&mut self) -> Option<&'a str> {
838 let s = self.take_while(|c| match c {
839 c if c.is_whitespace() => false,
840 '=' => false,
841 _ => true,
842 });
843
844 if s.is_empty() {
845 None
846 } else {
847 Some(s)
848 }
849 }
850
851 fn value(&mut self) -> Result<String, Error> {
852 let value = if self.eat_if('\'') {
853 let value = self.quoted_value()?;
854 self.eat('\'')?;
855 value
856 } else {
857 self.simple_value()?
858 };
859
860 Ok(value)
861 }
862
863 fn simple_value(&mut self) -> Result<String, Error> {
864 let mut value = String::new();
865
866 while let Some(&(_, c)) = self.it.peek() {
867 if c.is_whitespace() {
868 break;
869 }
870
871 self.it.next();
872 if c == '\\' {
873 if let Some((_, c2)) = self.it.next() {
874 value.push(c2);
875 }
876 } else {
877 value.push(c);
878 }
879 }
880
881 if value.is_empty() {
882 return Err(Error::config_parse("unexpected EOF".into()));
883 }
884
885 Ok(value)
886 }
887
888 fn quoted_value(&mut self) -> Result<String, Error> {
889 let mut value = String::new();
890
891 while let Some(&(_, c)) = self.it.peek() {
892 if c == '\'' {
893 return Ok(value);
894 }
895
896 self.it.next();
897 if c == '\\' {
898 if let Some((_, c2)) = self.it.next() {
899 value.push(c2);
900 }
901 } else {
902 value.push(c);
903 }
904 }
905
906 Err(Error::config_parse(
907 "unterminated quoted connection parameter value".into(),
908 ))
909 }
910
911 fn parameter(&mut self) -> Result<Option<(&'a str, String)>, Error> {
912 self.skip_ws();
913 let keyword = match self.keyword() {
914 Some(keyword) => keyword,
915 None => return Ok(None),
916 };
917 self.skip_ws();
918 self.eat('=')?;
919 self.skip_ws();
920 let value = self.value()?;
921
922 Ok(Some((keyword, value)))
923 }
924}
925
926struct UrlParser<'a> {
928 s: &'a str,
929 config: Config,
930}
931
932impl<'a> UrlParser<'a> {
933 fn parse(s: &'a str) -> Result<Option<Config>, Error> {
934 let s = match Self::remove_url_prefix(s) {
935 Some(s) => s,
936 None => return Ok(None),
937 };
938
939 let mut parser = UrlParser {
940 s,
941 config: Config::new(),
942 };
943
944 parser.parse_credentials()?;
945 parser.parse_host()?;
946 parser.parse_path()?;
947 parser.parse_params()?;
948
949 Ok(Some(parser.config))
950 }
951
952 fn remove_url_prefix(s: &str) -> Option<&str> {
953 for prefix in &["postgres://", "postgresql://"] {
954 if let Some(stripped) = s.strip_prefix(prefix) {
955 return Some(stripped);
956 }
957 }
958
959 None
960 }
961
962 fn take_until(&mut self, end: &[char]) -> Option<&'a str> {
963 match self.s.find(end) {
964 Some(pos) => {
965 let (head, tail) = self.s.split_at(pos);
966 self.s = tail;
967 Some(head)
968 }
969 None => None,
970 }
971 }
972
973 fn take_all(&mut self) -> &'a str {
974 mem::take(&mut self.s)
975 }
976
977 fn eat_byte(&mut self) {
978 self.s = &self.s[1..];
979 }
980
981 fn parse_credentials(&mut self) -> Result<(), Error> {
982 let creds = match self.take_until(&['@']) {
983 Some(creds) => creds,
984 None => return Ok(()),
985 };
986 self.eat_byte();
987
988 let mut it = creds.splitn(2, ':');
989 let user = self.decode(it.next().unwrap())?;
990 self.config.user(&user);
991
992 if let Some(password) = it.next() {
993 let password = Cow::from(percent_encoding::percent_decode(password.as_bytes()));
994 self.config.password(password);
995 }
996
997 Ok(())
998 }
999
1000 fn parse_host(&mut self) -> Result<(), Error> {
1001 let host = match self.take_until(&['/', '?']) {
1002 Some(host) => host,
1003 None => self.take_all(),
1004 };
1005
1006 if host.is_empty() {
1007 return Ok(());
1008 }
1009
1010 for chunk in host.split(',') {
1011 let (host, port) = if chunk.starts_with('[') {
1012 let idx = match chunk.find(']') {
1013 Some(idx) => idx,
1014 None => return Err(Error::config_parse(InvalidValue("host").into())),
1015 };
1016
1017 let host = &chunk[1..idx];
1018 let remaining = &chunk[idx + 1..];
1019 let port = if let Some(port) = remaining.strip_prefix(':') {
1020 Some(port)
1021 } else if remaining.is_empty() {
1022 None
1023 } else {
1024 return Err(Error::config_parse(InvalidValue("host").into()));
1025 };
1026
1027 (host, port)
1028 } else {
1029 let mut it = chunk.splitn(2, ':');
1030 (it.next().unwrap(), it.next())
1031 };
1032
1033 self.host_param(host)?;
1034 let port = self.decode(port.unwrap_or("5432"))?;
1035 self.config.param("port", &port)?;
1036 }
1037
1038 Ok(())
1039 }
1040
1041 fn parse_path(&mut self) -> Result<(), Error> {
1042 if !self.s.starts_with('/') {
1043 return Ok(());
1044 }
1045 self.eat_byte();
1046
1047 let dbname = match self.take_until(&['?']) {
1048 Some(dbname) => dbname,
1049 None => self.take_all(),
1050 };
1051
1052 if !dbname.is_empty() {
1053 self.config.dbname(&self.decode(dbname)?);
1054 }
1055
1056 Ok(())
1057 }
1058
1059 fn parse_params(&mut self) -> Result<(), Error> {
1060 if !self.s.starts_with('?') {
1061 return Ok(());
1062 }
1063 self.eat_byte();
1064
1065 while !self.s.is_empty() {
1066 let key = match self.take_until(&['=']) {
1067 Some(key) => self.decode(key)?,
1068 None => return Err(Error::config_parse("unterminated parameter".into())),
1069 };
1070 self.eat_byte();
1071
1072 let value = match self.take_until(&['&']) {
1073 Some(value) => {
1074 self.eat_byte();
1075 value
1076 }
1077 None => self.take_all(),
1078 };
1079
1080 if key == "host" {
1081 self.host_param(value)?;
1082 } else {
1083 let value = self.decode(value)?;
1084 self.config.param(&key, &value)?;
1085 }
1086 }
1087
1088 Ok(())
1089 }
1090
1091 #[cfg(unix)]
1092 fn host_param(&mut self, s: &str) -> Result<(), Error> {
1093 let decoded = Cow::from(percent_encoding::percent_decode(s.as_bytes()));
1094 if decoded.first() == Some(&b'/') {
1095 self.config.host_path(OsStr::from_bytes(&decoded));
1096 } else {
1097 let decoded = str::from_utf8(&decoded).map_err(|e| Error::config_parse(Box::new(e)))?;
1098 self.config.host(decoded);
1099 }
1100
1101 Ok(())
1102 }
1103
1104 #[cfg(not(unix))]
1105 fn host_param(&mut self, s: &str) -> Result<(), Error> {
1106 let s = self.decode(s)?;
1107 self.config.param("host", &s)
1108 }
1109
1110 fn decode(&self, s: &'a str) -> Result<Cow<'a, str>, Error> {
1111 percent_encoding::percent_decode(s.as_bytes())
1112 .decode_utf8()
1113 .map_err(|e| Error::config_parse(e.into()))
1114 }
1115}
1116
1117#[cfg(test)]
1118mod tests {
1119 use std::net::IpAddr;
1120
1121 use crate::{config::Host, Config};
1122
1123 #[test]
1124 fn test_simple_parsing() {
1125 let s = "user=pass_user dbname=postgres host=host1,host2 hostaddr=127.0.0.1,127.0.0.2 port=26257";
1126 let config = s.parse::<Config>().unwrap();
1127 assert_eq!(Some("pass_user"), config.get_user());
1128 assert_eq!(Some("postgres"), config.get_dbname());
1129 assert_eq!(
1130 [
1131 Host::Tcp("host1".to_string()),
1132 Host::Tcp("host2".to_string())
1133 ],
1134 config.get_hosts(),
1135 );
1136
1137 assert_eq!(
1138 [
1139 "127.0.0.1".parse::<IpAddr>().unwrap(),
1140 "127.0.0.2".parse::<IpAddr>().unwrap()
1141 ],
1142 config.get_hostaddrs(),
1143 );
1144
1145 assert_eq!(1, 1);
1146 }
1147
1148 #[test]
1149 fn test_invalid_hostaddr_parsing() {
1150 let s = "user=pass_user dbname=postgres host=host1 hostaddr=127.0.0 port=26257";
1151 s.parse::<Config>().err().unwrap();
1152 }
1153}