Skip to main content

caretta_sync_cli/cli/args/
config.rs

1use std::path::PathBuf;
2
3use caretta_sync_core::{
4    config::{Config, PartialConfig, PartialIrohConfig},
5    utils::{emptiable::Emptiable, mergeable::Mergeable},
6};
7use clap::Args;
8
9use tokio::sync::OnceCell;
10
11#[derive(Args, Clone, Debug)]
12pub struct ConfigArgs {
13    #[arg(short = 'c', long = "config")]
14    pub file_path: Option<PathBuf>,
15    #[arg(skip)]
16    pub file_content: OnceCell<PartialConfig>,
17    #[command(flatten)]
18    pub args: PartialConfig,
19}
20
21impl ConfigArgs {
22    fn get_file_path_or_default(&self, app_name: &'static str) -> PathBuf {
23        self.file_path.clone().unwrap_or(
24            dirs::config_local_dir()
25                .expect("Config user directory should be set")
26                .join(app_name)
27                .join("config.toml"),
28        )
29    }
30    async fn get_or_read_file_content(&self, app_name: &'static str) -> PartialConfig {
31        self.file_content
32            .get_or_init(|| async {
33                PartialConfig::read_from(self.get_file_path_or_default(app_name))
34                    .expect("Config file should be invalid!")
35            })
36            .await
37            .clone()
38    }
39    pub async fn to_partial_config_with_default(&self, app_name: &'static str) -> PartialConfig {
40        let mut default = PartialConfig::default(app_name);
41        default.merge(self.to_partial_config_without_default(app_name).await);
42        default
43    }
44    pub async fn to_partial_config_without_default(&self, app_name: &'static str) -> PartialConfig {
45        let mut file_content = self.get_or_read_file_content(app_name).await;
46        let args = self.args.clone();
47        file_content.merge(args);
48        file_content
49    }
50    async fn has_p2p_private_key(&self, app_name: &'static str) -> bool {
51        let merged = self.to_partial_config_with_default(app_name).await;
52        match merged.iroh {
53            Some(p2p) => p2p.secret_key.is_some(),
54            None => false,
55        }
56    }
57    pub async fn into_config(mut self, app_name: &'static str) -> Config {
58        if !self.has_p2p_private_key(app_name).await {
59            let path = self.get_file_path_or_default(app_name);
60            let content = self.file_content.get_mut().unwrap();
61            if let Some(p2p) = content.iroh.as_mut() {
62                p2p.renew_secret_key();
63            } else {
64                content
65                    .iroh
66                    .insert(PartialIrohConfig::empty().with_new_secret_key());
67            }
68            content
69                .write_to(path)
70                .expect("Config file should be writable first time to initialize secret");
71        }
72        self.to_partial_config_with_default(app_name)
73            .await
74            .try_into()
75            .expect("Some configurations are missing!")
76    }
77}