Skip to main content

caretta_sync_core/config/
mod.rs

1pub mod error;
2mod iroh;
3mod rpc;
4mod storage;
5
6use crate::utils::{emptiable::Emptiable, mergeable::Mergeable};
7pub use error::ConfigError;
8use serde::{Deserialize, Serialize};
9use std::{
10    default::Default,
11    fs::File,
12    io::{Read, Write},
13    path::Path,
14};
15
16pub use iroh::{IrohConfig, PartialIrohConfig};
17pub use rpc::*;
18pub use storage::{PartialStorageConfig, StorageConfig};
19use tokio::io::{AsyncReadExt, AsyncWriteExt};
20
21#[cfg(feature = "cli")]
22use clap::Args;
23
24#[derive(Clone, Debug)]
25pub struct Config {
26    pub iroh: IrohConfig,
27    pub storage: StorageConfig,
28    pub rpc: RpcConfig,
29}
30
31impl AsRef<StorageConfig> for Config {
32    fn as_ref(&self) -> &StorageConfig {
33        &self.storage
34    }
35}
36
37impl AsRef<IrohConfig> for Config {
38    fn as_ref(&self) -> &IrohConfig {
39        &self.iroh
40    }
41}
42
43impl AsRef<RpcConfig> for Config {
44    fn as_ref(&self) -> &RpcConfig {
45        &self.rpc
46    }
47}
48
49impl TryFrom<PartialConfig> for Config {
50    type Error = crate::error::Error;
51    fn try_from(value: PartialConfig) -> Result<Self, Self::Error> {
52        Ok(Self {
53            rpc: value
54                .rpc
55                .ok_or(crate::error::Error::MissingConfig("rpc"))?
56                .try_into()?,
57            iroh: value
58                .iroh
59                .ok_or(crate::error::Error::MissingConfig("p2p"))?
60                .try_into()?,
61            storage: value
62                .storage
63                .ok_or(crate::error::Error::MissingConfig("storage"))?
64                .try_into()?,
65        })
66    }
67}
68
69#[cfg_attr(feature = "cli", derive(Args))]
70#[derive(Clone, Debug, Deserialize, Serialize)]
71pub struct PartialConfig {
72    #[cfg_attr(feature = "cli", command(flatten))]
73    pub iroh: Option<PartialIrohConfig>,
74    #[cfg_attr(feature = "cli", command(flatten))]
75    pub storage: Option<PartialStorageConfig>,
76    #[cfg_attr(feature = "cli", command(flatten))]
77    pub rpc: Option<PartialRpcConfig>,
78}
79
80impl Default for PartialConfig {
81    fn default() -> Self {
82        Self::new()
83    }
84}
85
86impl PartialConfig {
87    pub fn new() -> Self {
88        Self {
89            iroh: Some(PartialIrohConfig::empty().with_new_secret_key()),
90            storage: Some(PartialStorageConfig::empty()),
91            rpc: Some(PartialRpcConfig::empty()),
92        }
93    }
94    pub fn from_toml(s: &str) -> Result<Self, toml::de::Error> {
95        toml::from_str(s)
96    }
97    pub fn into_toml(&self) -> Result<String, toml::ser::Error> {
98        toml::to_string(self)
99    }
100    pub fn read_or_create<T>(path: T) -> Result<Self, ConfigError>
101    where
102        T: AsRef<Path>,
103    {
104        if !path.as_ref().exists() {
105            Self::new().write_to(&path)?;
106        }
107        Self::read_from(&path)
108    }
109    pub fn read_from<T>(path: T) -> Result<Self, ConfigError>
110    where
111        T: AsRef<Path>,
112    {
113        if !path.as_ref().exists() {
114            if let Some(x) = path.as_ref().parent() {
115                std::fs::create_dir_all(x)?;
116            };
117            let _ = File::create(&path)?;
118        }
119        let mut file = File::open(path.as_ref())?;
120        let mut content = String::new();
121        file.read_to_string(&mut content)?;
122        let config: Self = toml::from_str(&content)?;
123        Ok(config)
124    }
125    pub fn write_to<T>(&self, path: T) -> Result<(), ConfigError>
126    where
127        T: AsRef<Path>,
128    {
129        if !path.as_ref().exists() {
130            if let Some(x) = path.as_ref().parent() {
131                std::fs::create_dir_all(x)?;
132            };
133            let _ = File::create(&path)?;
134        }
135        let mut file = File::create(&path)?;
136        file.write_all(toml::to_string(self)?.as_bytes())?;
137        Ok(())
138    }
139    pub fn default(app_name: &'static str) -> Self {
140        Self {
141            iroh: Some(PartialIrohConfig::default()),
142            rpc: Some(PartialRpcConfig::default(app_name)),
143            storage: Some(PartialStorageConfig::default(app_name)),
144        }
145    }
146}
147
148impl From<Config> for PartialConfig {
149    fn from(value: Config) -> Self {
150        Self {
151            iroh: Some(value.iroh.into()),
152            storage: Some(value.storage.into()),
153            rpc: Some(value.rpc.into()),
154        }
155    }
156}
157
158impl Emptiable for PartialConfig {
159    fn empty() -> Self {
160        Self {
161            iroh: None,
162            storage: None,
163            rpc: None,
164        }
165    }
166
167    fn is_empty(&self) -> bool {
168        self.iroh.is_empty() && self.rpc.is_empty() && self.storage.is_empty()
169    }
170}
171
172impl Mergeable for PartialConfig {
173    fn merge(&mut self, other: Self) {
174        self.iroh.merge(other.iroh);
175        self.rpc.merge(other.rpc);
176        self.storage.merge(other.storage);
177    }
178}