hashiverse_client_rust/
lib.rs1use std::path::PathBuf;
27use std::sync::Arc;
28
29use anyhow::Result;
30
31use hashiverse_lib::client::args::Args;
32use hashiverse_lib::client::client_storage::sqlite_client_storage::SqliteClientStorage;
33use hashiverse_lib::client::hashiverse_client::HashiverseClient;
34use hashiverse_lib::client::key_locker::disk_key_locker::{DiskKeyLocker, DiskKeyLockerManager};
35use hashiverse_lib::client::key_locker::key_locker::KeyLockerManager;
36use hashiverse_lib::tools::pow_generator::native_parallel_pow_generator::NativeParallelPowGenerator;
37use hashiverse_lib::tools::runtime_services::RuntimeServices;
38use hashiverse_lib::tools::time_provider::time_provider::RealTimeProvider;
39use hashiverse_lib::transport::bootstrap_provider::bootstrap_provider::BootstrapProvider;
40use hashiverse_lib::transport::bootstrap_provider::dnssec_bootstrap_provider::DnssecBootstrapProvider;
41use hashiverse_lib::transport::bootstrap_provider::manual_bootstrap_provider::ManualBootstrapProvider;
42use hashiverse_lib::transport::partial_https_transport::PartialHttpsTransportFactory;
43
44pub use hashiverse_lib;
45
46pub struct HashiverseBuilder {
49 data_dir: PathBuf,
50 passphrase: String,
51 bootstrap_addresses: Option<Vec<String>>,
52}
53
54impl Default for HashiverseBuilder {
55 fn default() -> Self {
56 Self {
57 data_dir: default_data_dir(),
58 passphrase: String::new(),
59 bootstrap_addresses: None,
60 }
61 }
62}
63
64impl HashiverseBuilder {
65 pub fn new() -> Self {
66 Self::default()
67 }
68
69 pub fn data_dir(mut self, dir: impl Into<PathBuf>) -> Self {
73 let dir = dir.into();
74 let expanded = shellexpand::tilde(&dir.to_string_lossy()).into_owned();
75 self.data_dir = PathBuf::from(expanded);
76 self
77 }
78
79 pub fn passphrase(mut self, passphrase: impl Into<String>) -> Self {
81 self.passphrase = passphrase.into();
82 self
83 }
84
85 pub fn bootstrap(mut self, addresses: Vec<String>) -> Self {
89 self.bootstrap_addresses = Some(addresses);
90 self
91 }
92
93 pub async fn build_with_keyphrase(self, key_phrase: impl Into<String>) -> Result<Hashiverse> {
97 std::fs::create_dir_all(&self.data_dir)?;
98 let key_locker_manager = DiskKeyLockerManager::with_data_dir(self.data_dir.clone(), self.passphrase.clone())?;
99 let key_locker: Arc<DiskKeyLocker> = key_locker_manager.create(key_phrase.into()).await?;
100 self.assemble(key_locker_manager, key_locker).await
101 }
102
103 pub async fn build_from_stored_key(self, client_id_hex: impl Into<String>) -> Result<Hashiverse> {
106 std::fs::create_dir_all(&self.data_dir)?;
107 let key_locker_manager = DiskKeyLockerManager::with_data_dir(self.data_dir.clone(), self.passphrase.clone())?;
108 let key_locker: Arc<DiskKeyLocker> = key_locker_manager.switch(client_id_hex.into()).await?;
109 self.assemble(key_locker_manager, key_locker).await
110 }
111
112 async fn assemble(
113 self,
114 key_locker_manager: Arc<DiskKeyLockerManager>,
115 key_locker: Arc<DiskKeyLocker>,
116 ) -> Result<Hashiverse> {
117 let client_storage_dir = self.data_dir.join("client_storage");
118 std::fs::create_dir_all(&client_storage_dir)?;
119
120 let time_provider = Arc::new(RealTimeProvider);
121 let bootstrap_provider: Arc<dyn BootstrapProvider> = match self.bootstrap_addresses {
122 Some(addresses) => ManualBootstrapProvider::new(addresses),
123 None => Arc::new(DnssecBootstrapProvider::new()),
124 };
125 let transport_factory = Arc::new(PartialHttpsTransportFactory::new(bootstrap_provider));
126 let pow_generator = Arc::new(NativeParallelPowGenerator::new());
127
128 let runtime_services = Arc::new(RuntimeServices {
129 time_provider,
130 transport_factory,
131 pow_generator,
132 });
133
134 let client_storage = SqliteClientStorage::new(client_storage_dir).await?;
135 let hashiverse_client = HashiverseClient::new(
136 runtime_services,
137 client_storage,
138 key_locker,
139 Args::default(),
140 )
141 .await?;
142
143 Ok(Hashiverse {
144 client: Arc::new(hashiverse_client),
145 _key_locker_manager: key_locker_manager,
146 })
147 }
148}
149
150pub struct Hashiverse {
153 client: Arc<HashiverseClient>,
154 _key_locker_manager: Arc<DiskKeyLockerManager>,
155}
156
157impl Hashiverse {
158 pub fn client(&self) -> &Arc<HashiverseClient> {
162 &self.client
163 }
164}
165
166impl std::ops::Deref for Hashiverse {
167 type Target = HashiverseClient;
168 fn deref(&self) -> &HashiverseClient {
169 &self.client
170 }
171}
172
173fn default_data_dir() -> PathBuf {
174 dirs_next::data_dir().unwrap_or_else(std::env::temp_dir).join("hashiverse")
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn builder_defaults_resolve_to_a_data_dir() {
183 let builder = HashiverseBuilder::new();
184 assert!(builder.data_dir.ends_with("hashiverse"), "default data dir should end in 'hashiverse': {:?}", builder.data_dir);
185 assert!(builder.passphrase.is_empty());
186 assert!(builder.bootstrap_addresses.is_none());
187 }
188
189 #[test]
190 fn data_dir_tilde_expansion() {
191 let builder = HashiverseBuilder::new().data_dir("~/hashiverse-test");
192 let expanded = builder.data_dir.to_string_lossy();
193 assert!(!expanded.starts_with("~"), "tilde should be expanded: {}", expanded);
194 }
195}