1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//! Parse Postgres service configuration files
//!
//! [Postgres service files](https://www.postgresql.org/docs/current/libpq-pgservice.html)
//! are configuration files that contain connection parameters
//!
//! # Example
//!
//! ```rust
//! # fn _norun() {
//! postgres_service::load_connect_params("mydb")
//!    .expect("reading postgres service")
//!    .user("username") // optionally override the username
//!    .connect(postgres::NoTls)
//!    .expect("connecting to postgres");
//! # }
//! ```

use ini::Properties;

fn build_from_section(section: &Properties) -> tokio_postgres::config::Config {
	let mut username: Option<String> = None;
	let mut password: Option<String> = None;

	let mut builder = tokio_postgres::config::Config::new();
	let mut options = String::new();

	for (k, v) in section.iter() {
		match k {
			"host" => {
				builder.host(v);
			}
			"hostaddr" => {
				builder.host(v);
			}
			"port" => {
				builder.port(v.parse().unwrap());
			}
			"dbname" => {
				builder.dbname(v);
			}
			"user" => username = Some(v.to_owned()),
			"password" => password = Some(v.to_owned()),
			_ => options += &format!("{}={} ", k, v),
		}
	}

	if !options.is_empty() {
		builder.options(&options);
	}

	if let Some(username) = username {
		builder.user(&username);
	}
	if let Some(password) = password {
		builder.password(&password);
	}

	builder
}

/// Load connection parameters for the named Postgresql service
pub fn load_connect_params(service_name: &str) -> Option<postgres::config::Config> {
	tokio::load_connect_params(service_name).map(Into::into)
}

/// For use with tokio-postgres
pub mod tokio {
	use crate::build_from_section;
	use ini::Ini;
	/// Load connection parameters for the named Postgresql service
	pub fn load_connect_params(service_name: &str) -> Option<tokio_postgres::config::Config> {
		if let Ok(home) = std::env::var("HOME") {
			if let Ok(ini) = Ini::load_from_file(home + "/" + ".pg_service.conf") {
				if let Some(section) = ini.section(Some(service_name)) {
					return Some(build_from_section(section));
				}
			}
		}

		let confdir = std::env::var("PGSYSCONFDIR").unwrap_or("/etc/postgresql-common".into());

		if let Ok(ini) = Ini::load_from_file(confdir + "/" + "pg_service.conf") {
			if let Some(section) = ini.section(Some(service_name)) {
				return Some(build_from_section(section));
			}
		}

		None
	}
}