use std::path::PathBuf; #[cfg(feature="desktop")] use clap::Args; #[cfg(any(test, feature="test"))] use tempfile::tempdir; use crate::{config::{ConfigError, PartialConfig}, utils::{emptiable::Emptiable, get_binary_name, mergeable::Mergeable}}; use libp2p::mdns::Config; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug)] pub struct StorageConfig { pub data_directory: PathBuf, pub cache_directory: PathBuf, } impl TryFrom for StorageConfig { type Error = ConfigError; fn try_from(value: PartialStorageConfig) -> Result { Ok(Self { data_directory: value.data_directory.ok_or(ConfigError::MissingConfig("data_directory".to_string()))?, cache_directory: value.cache_directory.ok_or(ConfigError::MissingConfig("cache_directory".to_string()))?, }) } } #[cfg_attr(feature="desktop", derive(Args))] #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct PartialStorageConfig { #[cfg_attr(feature="desktop", arg(long))] pub data_directory: Option, #[cfg_attr(feature="desktop", arg(long))] pub cache_directory: Option, } impl PartialStorageConfig { #[cfg(not(any(target_os="android", target_os="ios")))] pub fn default(app_name: &'static str) -> Self { let mut data_dir = dirs::data_local_dir().unwrap(); data_dir.push(app_name); let mut cache_dir = dirs::cache_dir().unwrap(); cache_dir.push(app_name); Self { data_directory: Some(data_dir), cache_directory: Some(cache_dir) } } #[cfg(target_os="android")] fn default_android() -> Self{ let ctx = ndk_context::android_context(); let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?; let mut env = vm.attach_current_thread()?; let ctx = unsafe { jni::objects::JObject::from_raw(ctx.context().cast()) }; let cache_dir = env .call_method(ctx, "getFilesDir", "()Ljava/io/File;", &[])? .l()?; let cache_dir: jni::objects::JString = env .call_method(&cache_dir, "toString", "()Ljava/lang/String;", &[])? .l()? .try_into()?; let cache_dir = env.get_string(&cache_dir)?; let cache_dir = cache_dir.to_str()?; Ok(cache_dir.to_string()) } #[cfg(target_os="ios")] fn default_ios(){ unsafe { let file_manager: *mut Object = msg_send![Class::get("NSFileManager").unwrap(), defaultManager]; let paths: Id = msg_send![file_manager, URLsForDirectory:1 inDomains:1]; let first_path: *mut Object = msg_send![paths, firstObject]; let path: Id = Id::from_ptr(msg_send![first_path, path]); Some(path.as_str().to_string()) } } } impl From for PartialStorageConfig { fn from(config: StorageConfig) -> PartialStorageConfig { Self { data_directory: Some(config.data_directory), cache_directory: Some(config.cache_directory), } } } impl Emptiable for PartialStorageConfig { fn empty() -> Self { Self { data_directory: None, cache_directory: None } } fn is_empty(&self) -> bool { self.data_directory.is_none() && self.cache_directory.is_none() } } impl Mergeable for PartialStorageConfig { fn merge(&mut self, mut other: Self) { if let Some(x) = other.data_directory.take() { let _ = self.data_directory.insert(x); }; if let Some(x) = other.cache_directory.take() { let _ = self.cache_directory.insert(x); }; } }