1use std::net::Ipv4Addr;
2use std::path::PathBuf;
3use std::{env, fs};
4
5use anyhow::{Context, Result};
6use serde::Deserialize;
7use url::Url;
8
9#[must_use]
10#[derive(Deserialize)]
11pub struct Config {
12 pub server: ServerConfig,
13 pub https: HttpsConfig,
14 pub hugo: HugoConfig,
15 pub algolia: AlgoliaConfig,
16}
17
18impl Config {
19 pub fn load_config<T>(file_name: T) -> Result<Self>
20 where
21 T: AsRef<str>,
22 {
23 let file_name = file_name.as_ref();
24
25 let mut current_dir = env::current_dir()?;
26 loop {
27 let candidate = current_dir.join(file_name);
28 if candidate.try_exists()? {
29 tracing::info!("Load config file from `{}`", candidate.display());
30
31 let mut config: Self = toml::from_str(&fs::read_to_string(candidate)?)?;
32 config.canonicalize()?;
33
34 return Ok(config);
35 }
36
37 if !current_dir.pop() {
38 anyhow::bail!("cannot find `{file_name}`");
39 }
40 }
41 }
42
43 fn canonicalize(&mut self) -> Result<()> {
44 self.https.cert_path = self
45 .https
46 .cert_path
47 .canonicalize()
48 .context(format!("can not find `{}`", self.https.cert_path.display()))?;
49 self.https.key_path = self
50 .https
51 .key_path
52 .canonicalize()
53 .context(format!("can not find `{}`", self.https.key_path.display()))?;
54 self.hugo.repo_dst = env::current_dir()?.join(&self.hugo.repo_dst);
55
56 Ok(())
57 }
58}
59
60#[must_use]
61#[derive(Deserialize)]
62pub struct ServerConfig {
63 pub host: Ipv4Addr,
64 pub port: u16,
65}
66
67#[must_use]
68#[derive(Deserialize)]
69pub struct HttpsConfig {
70 pub cert_path: PathBuf,
71 pub key_path: PathBuf,
72}
73
74#[must_use]
75#[derive(Deserialize)]
76pub struct HugoConfig {
77 pub repo_url: Url,
78 pub repo_dst: PathBuf,
79}
80
81#[must_use]
82#[derive(Deserialize)]
83pub struct AlgoliaConfig {
84 pub records_file_name: PathBuf,
85 pub application_id: String,
86 pub api_key: String,
87 pub index_name: String,
88}
89
90#[cfg(test)]
91mod tests {
92 use testresult::TestResult;
93 use tracing_test::traced_test;
94
95 use super::*;
96
97 #[test]
98 #[traced_test]
99 fn test_load_config() -> TestResult {
100 let _ = Config::load_config(".config.toml")?;
101
102 Ok(())
103 }
104}