Skip to content
This repository was archived by the owner on Jan 2, 2026. It is now read-only.

Commit 14a0e34

Browse files
committed
feat: finish config parsing
1 parent 57d4db5 commit 14a0e34

File tree

4 files changed

+118
-2
lines changed

4 files changed

+118
-2
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ poem = { version = "3.1.11", features = ["rustls", "hex", "compression"] }
2424
polyproto = { version = "0.10.0", features = ["serde"] }
2525
rand = "0.9.1"
2626
env_logger = { version = "0.11.8" }
27+
serde_with = "3.13.0"
2728

2829
# We use `opt-level = "s"` as it significantly reduces binary size.
2930
# We could then use the `#[optimize(speed)]` attribute for spot optimizations.

src/config/mod.rs

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,21 @@
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

55
use std::ops::Deref;
6+
use std::sync::OnceLock;
67

78
use serde::Deserialize;
9+
use serde_with::{DisplayFromStr, serde_as};
10+
11+
use crate::{StdError, StdResult};
12+
13+
static CONFIG: OnceLock<SonataConfig> = OnceLock::new();
14+
15+
const TLS_CONFIG_DISABLE: &str = "disable";
16+
const TLS_CONFIG_ALLOW: &str = "allow";
17+
const TLS_CONFIG_PREFER: &str = "prefer";
18+
const TLS_CONFIG_REQUIRE: &str = "require";
19+
const TLS_CONFIG_VERIFY_CA: &str = "verify_ca";
20+
const TLS_CONFIG_VERIFY_FULL: &str = "verify_full";
821

922
#[derive(Deserialize, Debug)]
1023
pub struct SonataConfig {
@@ -47,8 +60,19 @@ pub struct GeneralConfig {
4760
pub database: DatabaseConfig,
4861
}
4962

63+
#[serde_as]
5064
#[derive(Deserialize, Debug)]
51-
pub struct DatabaseConfig;
65+
pub struct DatabaseConfig {
66+
pub max_connections: u16,
67+
pub database: String,
68+
pub username: String,
69+
pub password: String,
70+
pub port: u16,
71+
pub host: String,
72+
#[serde(default)]
73+
#[serde_as(as = "DisplayFromStr")]
74+
pub tls: TlsConfig,
75+
}
5276

5377
#[derive(Deserialize, Debug)]
5478
pub struct ComponentConfig {
@@ -57,3 +81,79 @@ pub struct ComponentConfig {
5781
pub host: String,
5882
pub tls: bool,
5983
}
84+
85+
impl SonataConfig {
86+
pub fn init(input: &str) -> StdResult<()> {
87+
let cfg = toml::from_str::<Self>(input)?;
88+
CONFIG.set(cfg).map_err(|_| String::from("config global was already set"))?;
89+
Ok(())
90+
}
91+
92+
#[allow(clippy::expect_used)]
93+
/// Gets a static reference to the parsed configuration file. Will panic, if [Self] has not been initialized using [Self::init()].
94+
pub fn get_or_panic() -> &'static Self {
95+
CONFIG.get().expect("config has not been initialized yet")
96+
}
97+
}
98+
99+
#[derive(Debug, Deserialize, Default)]
100+
/// TLS configuration modes. Also called `sslconfig` by PostgreSQL. See <https://www.postgresql.org/docs/current/libpq-ssl.html#:~:text=32.1.%C2%A0SSL%20Mode-,descriptions,-sslmode>
101+
/// for the security implications of this choice.
102+
pub enum TlsConfig {
103+
/// I don't care about security, and I don't want to pay the overhead of
104+
/// encryption.
105+
Disable,
106+
/// I don't care about security, but I will pay the overhead of encryption
107+
/// if the server insists on it.
108+
Allow,
109+
/// I don't care about encryption, but I wish to pay the overhead of
110+
/// encryption if the server supports it.
111+
Prefer,
112+
/// I want my data to be encrypted, and I accept the overhead. I trust that
113+
/// the network will make sure I always connect to the server I want.
114+
#[default]
115+
Require,
116+
/// I want my data encrypted, and I accept the overhead. I want to be sure
117+
/// that I connect to a server that I trust.
118+
VerifyCa,
119+
/// I want my data encrypted, and I accept the overhead. I want to be sure
120+
/// that I connect to a server I trust, and that it's the one I specify.
121+
VerifyFull,
122+
}
123+
124+
impl TryFrom<&str> for TlsConfig {
125+
type Error = StdError;
126+
127+
fn try_from(value: &str) -> Result<Self, Self::Error> {
128+
match value.to_lowercase().as_str() {
129+
TLS_CONFIG_DISABLE => Ok(Self::Disable),
130+
TLS_CONFIG_ALLOW => Ok(Self::Disable),
131+
TLS_CONFIG_PREFER => Ok(Self::Disable),
132+
TLS_CONFIG_REQUIRE => Ok(Self::Disable),
133+
"verifyca" | TLS_CONFIG_VERIFY_CA | "verify-ca" => Ok(Self::Disable),
134+
"verifyfull" | TLS_CONFIG_VERIFY_FULL | "verify-full" => Ok(Self::Disable),
135+
other => Err(format!("{other} is not a valid TLS configuration value").into()),
136+
}
137+
}
138+
}
139+
140+
impl std::fmt::Display for TlsConfig {
141+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142+
f.write_str(match self {
143+
TlsConfig::Disable => TLS_CONFIG_DISABLE,
144+
TlsConfig::Allow => TLS_CONFIG_ALLOW,
145+
TlsConfig::Prefer => TLS_CONFIG_PREFER,
146+
TlsConfig::Require => TLS_CONFIG_REQUIRE,
147+
TlsConfig::VerifyCa => TLS_CONFIG_VERIFY_CA,
148+
TlsConfig::VerifyFull => TLS_CONFIG_VERIFY_FULL,
149+
})
150+
}
151+
}
152+
153+
impl std::str::FromStr for TlsConfig {
154+
type Err = Box<dyn std::error::Error + 'static>;
155+
156+
fn from_str(s: &str) -> Result<Self, Self::Err> {
157+
TlsConfig::try_from(s)
158+
}
159+
}

src/main.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ pub(crate) type StdResult<T> = Result<T, StdError>;
2020
#[tokio::main]
2121
#[cfg(not(tarpaulin))]
2222
async fn main() -> StdResult<()> {
23+
use std::path::PathBuf;
24+
use std::str::FromStr;
25+
2326
use clap::Parser;
24-
use log::{LevelFilter, debug};
27+
use log::{LevelFilter, debug, trace};
2528

2629
use crate::cli::Args;
30+
use crate::config::SonataConfig;
2731
_ = Args::parse(); // Has to be done, else clap doesn't work correctly.
2832
Args::init_global()?;
2933
let verbose_level = match Args::get_or_panic().verbose {
@@ -54,5 +58,15 @@ async fn main() -> StdResult<()> {
5458
.filter(Some("sonata"), log_level)
5559
.try_init()?;
5660
debug!("Hello, world!");
61+
let config_location = match &Args::get_or_panic().config {
62+
Some(path) => path,
63+
None => &PathBuf::from_str("sonata.toml")?,
64+
};
65+
66+
debug!("Parsing config at {config_location:?}...");
67+
SonataConfig::init(&std::fs::read_to_string(config_location)?)?;
68+
debug!("Parsed config!");
69+
trace!("Read config {:#?}", SonataConfig::get_or_panic());
70+
5771
Ok(())
5872
}

0 commit comments

Comments
 (0)