1use serde::{Deserialize, Serialize};
8
9use crate::library_dir::LibraryDir;
10
11#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
13pub enum CloudProvider {
14 S3,
15 GoogleDrive,
16 Dropbox,
17 OneDrive,
18 HttpProxy,
19 CloudKit,
20}
21
22impl CloudProvider {
23 pub fn needs_email(&self) -> bool {
25 matches!(self, Self::GoogleDrive | Self::Dropbox | Self::OneDrive)
26 }
27}
28
29#[derive(Clone, Debug, Default, Serialize, Deserialize)]
33pub struct CloudHomeConfig {
34 #[serde(default)]
36 pub provider: Option<CloudProvider>,
37 #[serde(default)]
38 pub s3_bucket: Option<String>,
39 #[serde(default)]
40 pub s3_region: Option<String>,
41 #[serde(default)]
42 pub s3_endpoint: Option<String>,
43 #[serde(default)]
44 pub s3_key_prefix: Option<String>,
45 #[serde(default)]
46 pub google_drive_folder_id: Option<String>,
47 #[serde(default)]
48 pub dropbox_folder_path: Option<String>,
49 #[serde(default)]
50 pub onedrive_drive_id: Option<String>,
51 #[serde(default)]
52 pub onedrive_folder_id: Option<String>,
53 #[serde(default)]
55 pub cloudkit_is_shared: bool,
56 #[serde(default)]
57 pub http_url: Option<String>,
58}
59
60#[derive(thiserror::Error, Debug)]
62pub enum ConfigError {
63 #[error("Serialization error: {0}")]
64 Serialization(String),
65 #[error("Configuration error: {0}")]
66 Config(String),
67 #[error("IO error: {0}")]
68 Io(#[from] std::io::Error),
69}
70
71#[derive(Clone, Debug)]
73pub struct Config {
74 pub library_id: String,
75 pub device_id: String,
77 pub library_dir: LibraryDir,
78 pub library_name: String,
79 pub encryption_key_stored: bool,
81 pub encryption_key_fingerprint: Option<String>,
83 pub cloud_home: CloudHomeConfig,
85}
86
87impl Config {
88 pub fn sync_enabled(&self, key_service: &crate::keys::KeyService) -> bool {
91 use crate::keys::CloudHomeCredentials;
92
93 let creds = key_service
94 .get_cloud_home_credentials()
95 .unwrap_or_else(|e| {
96 tracing::warn!("reading cloud home credentials for sync_enabled: {e}");
97 None
98 });
99 let has_s3 = matches!(creds, Some(CloudHomeCredentials::S3 { .. }));
100 let has_oauth = matches!(creds, Some(CloudHomeCredentials::OAuth { .. }));
101
102 let ch = &self.cloud_home;
103 match ch.provider {
104 Some(CloudProvider::S3) => ch.s3_bucket.is_some() && ch.s3_region.is_some() && has_s3,
105 Some(CloudProvider::GoogleDrive) => ch.google_drive_folder_id.is_some() && has_oauth,
106 Some(CloudProvider::Dropbox) => ch.dropbox_folder_path.is_some() && has_oauth,
107 Some(CloudProvider::OneDrive) => {
108 ch.onedrive_drive_id.is_some() && ch.onedrive_folder_id.is_some() && has_oauth
109 }
110 Some(CloudProvider::HttpProxy) => ch.http_url.is_some(),
111 Some(CloudProvider::CloudKit) => true,
112 None => false,
113 }
114 }
115
116 pub fn is_dev_mode() -> bool {
119 std::env::var("COVEN_DEV_MODE").is_ok() || std::path::Path::new(".env").exists()
120 }
121
122 pub fn with_defaults(
124 library_id: String,
125 device_id: String,
126 library_dir: LibraryDir,
127 library_name: String,
128 ) -> Self {
129 Self {
130 library_id,
131 device_id,
132 library_dir,
133 library_name,
134 encryption_key_stored: false,
135 encryption_key_fingerprint: None,
136 cloud_home: CloudHomeConfig::default(),
137 }
138 }
139
140 pub fn save(&self) -> Result<(), ConfigError> {
142 self.save_to_config_yaml()
143 }
144
145 pub fn save_to_config_yaml(&self) -> Result<(), ConfigError> {
147 std::fs::create_dir_all(&*self.library_dir)?;
148 let yaml: ConfigYaml = self.into();
149 let text =
150 serde_yaml::to_string(&yaml).map_err(|e| ConfigError::Serialization(e.to_string()))?;
151 std::fs::write(self.library_dir.config_path(), text)?;
152 Ok(())
153 }
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct ConfigYaml {
159 pub library_id: String,
160 pub library_name: String,
161 #[serde(default)]
162 pub device_id: Option<String>,
163 #[serde(default)]
164 pub encryption_key_stored: bool,
165 #[serde(default)]
166 pub encryption_key_fingerprint: Option<String>,
167 #[serde(default, flatten)]
168 pub cloud_home: CloudHomeConfig,
169}
170
171impl ConfigYaml {
172 pub fn into_config(self, device_id: String, library_dir: LibraryDir) -> Config {
175 Config {
176 library_id: self.library_id,
177 device_id,
178 library_dir,
179 library_name: self.library_name,
180 encryption_key_stored: self.encryption_key_stored,
181 encryption_key_fingerprint: self.encryption_key_fingerprint,
182 cloud_home: self.cloud_home,
183 }
184 }
185}
186
187impl From<&Config> for ConfigYaml {
188 fn from(config: &Config) -> Self {
189 Self {
190 library_id: config.library_id.clone(),
191 library_name: config.library_name.clone(),
192 device_id: Some(config.device_id.clone()),
193 encryption_key_stored: config.encryption_key_stored,
194 encryption_key_fingerprint: config.encryption_key_fingerprint.clone(),
195 cloud_home: config.cloud_home.clone(),
196 }
197 }
198}