1use std::collections::HashMap;
2use std::sync::Arc;
3use url::Url;
4
5use crate::blob_engine::{self, BlobEngine};
6use crate::blobstore::BlobStore;
7use crate::chunkstore::ChunkStore;
8use crate::composition::{
9 Composition, DeserializeWithRegistry, REG, ServiceBuilder, with_registry,
10};
11use crate::directoryservice::DirectoryService;
12
13#[derive(serde::Deserialize, Default)]
14pub struct CompositionConfigs {
15 pub blob_stores:
16 HashMap<String, DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn BlobStore>>>>,
17 pub chunk_stores:
18 HashMap<String, DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn ChunkStore>>>>,
19 pub directoryservices: HashMap<
20 String,
21 DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn DirectoryService>>>,
22 >,
23}
24
25#[derive(clap::Parser, Clone)]
30#[group(id = "CastoreServiceUrls")]
31pub struct ServiceUrls {
32 #[arg(
33 long,
34 env,
35 default_value = "objectstore+file:/var/lib/snix-castore/blobs"
36 )]
37 pub blob_store_addr: Url,
38
39 #[arg(
40 long,
41 env,
42 default_value = "objectstore+file:/var/lib/snix-castore/blobs"
43 )]
44 pub chunk_store_addr: Url,
45
46 #[arg(
47 long,
48 env,
49 default_value = "redb:/var/lib/snix-castore/directories.redb"
50 )]
51 pub directory_service_addr: Url,
52
53 #[cfg(feature = "xp-composition-cli")]
57 #[arg(long, env)]
58 pub experimental_store_composition: Option<String>,
59}
60
61#[derive(clap::Parser, Clone)]
66#[group(id = "CastoreServiceUrlsGrpc")]
67pub struct ServiceUrlsGrpc {
68 #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
69 blob_store_addr: Url,
70
71 #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
72 chunk_store_addr: Url,
73
74 #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
75 directory_service_addr: Url,
76
77 #[cfg(feature = "xp-composition-cli")]
78 #[arg(long, env)]
79 experimental_store_composition: Option<String>,
80}
81
82#[derive(clap::Parser, Clone)]
90#[group(id = "CastoreServiceUrlsMemory")]
91pub struct ServiceUrlsMemory {
92 #[arg(long, env, default_value = "memory:")]
93 blob_store_addr: Url,
94
95 #[arg(long, env, default_value = "memory:")]
96 chunk_store_addr: Url,
97
98 #[arg(long, env, default_value = "redb+memory:")]
99 directory_service_addr: Url,
100
101 #[cfg(feature = "xp-composition-cli")]
102 #[arg(long, env)]
103 experimental_store_composition: Option<String>,
104}
105
106impl From<ServiceUrlsGrpc> for ServiceUrls {
107 fn from(
108 ServiceUrlsGrpc {
109 blob_store_addr,
110 chunk_store_addr,
111 directory_service_addr,
112 #[cfg(feature = "xp-composition-cli")]
113 experimental_store_composition,
114 }: ServiceUrlsGrpc,
115 ) -> ServiceUrls {
116 ServiceUrls {
117 blob_store_addr,
118 chunk_store_addr,
119 directory_service_addr,
120 #[cfg(feature = "xp-composition-cli")]
121 experimental_store_composition,
122 }
123 }
124}
125
126impl From<ServiceUrlsMemory> for ServiceUrls {
127 fn from(
128 ServiceUrlsMemory {
129 blob_store_addr,
130 chunk_store_addr,
131 directory_service_addr,
132 #[cfg(feature = "xp-composition-cli")]
133 experimental_store_composition,
134 }: ServiceUrlsMemory,
135 ) -> ServiceUrls {
136 ServiceUrls {
137 blob_store_addr,
138 chunk_store_addr,
139 directory_service_addr,
140 #[cfg(feature = "xp-composition-cli")]
141 experimental_store_composition,
142 }
143 }
144}
145
146pub async fn addrs_to_configs(
151 urls: impl Into<ServiceUrls>,
152) -> Result<CompositionConfigs, Box<dyn std::error::Error + Send + Sync>> {
153 let ServiceUrls {
154 blob_store_addr,
155 chunk_store_addr,
156 directory_service_addr,
157 #[cfg(feature = "xp-composition-cli")]
158 experimental_store_composition,
159 } = urls.into();
160
161 #[cfg(feature = "xp-composition-cli")]
162 if let Some(conf_path) = experimental_store_composition {
163 let conf_text = tokio::fs::read_to_string(conf_path).await?;
164 return Ok(with_registry(®, || toml::from_str(&conf_text))?);
165 }
166
167 let mut configs: CompositionConfigs = Default::default();
168
169 configs.blob_stores.insert(
170 "root".into(),
171 with_registry(®, || blob_store_addr.try_into())?,
172 );
173 configs.chunk_stores.insert(
174 "root".into(),
175 with_registry(®, || chunk_store_addr.try_into())?,
176 );
177 configs.directoryservices.insert(
178 "root".into(),
179 with_registry(®, || directory_service_addr.try_into())?,
180 );
181 Ok(configs)
182}
183
184pub async fn construct_services(
186 urls: impl Into<ServiceUrls>,
187) -> Result<
188 (
189 Arc<dyn BlobStore>,
190 Arc<dyn ChunkStore>,
191 Arc<dyn BlobEngine>,
192 Arc<dyn DirectoryService>,
193 ),
194 Box<dyn std::error::Error + Send + Sync>,
195> {
196 let configs = addrs_to_configs(urls).await?;
197 construct_services_from_configs(configs).await
198}
199
200pub async fn construct_services_from_configs(
202 configs: CompositionConfigs,
203) -> Result<
204 (
205 Arc<dyn BlobStore>,
206 Arc<dyn ChunkStore>,
207 Arc<dyn BlobEngine>,
208 Arc<dyn DirectoryService>,
209 ),
210 Box<dyn std::error::Error + Send + Sync>,
211> {
212 let mut comp = Composition::new(®);
213
214 comp.extend(configs.blob_stores);
215 comp.extend(configs.chunk_stores);
216 comp.extend(configs.directoryservices);
217
218 let blob_store: Arc<dyn BlobStore> = comp.build("root").await?;
219 let chunk_store: Arc<dyn ChunkStore> = comp.build("root").await?;
220 let blob_engine: Arc<dyn BlobEngine> = Arc::new(
221 blob_engine::ChunkingBlobEngine::builder()
222 .blob_store(blob_store.clone())
223 .chunk_store(chunk_store.clone())
224 .build(),
225 );
226 let directory_service: Arc<dyn DirectoryService> = comp.build("root").await?;
227
228 Ok((blob_store, chunk_store, blob_engine, directory_service))
229}
230
231#[cfg(any(test, feature = "mocks"))]
233pub fn gen_test_directory_service() -> impl DirectoryService + Clone {
234 crate::directoryservice::RedbDirectoryService::new_temporary("test".to_string())
235}
236
237#[cfg(any(test, feature = "mocks"))]
245pub fn gen_test_blob_engine()
246-> Arc<crate::blob_engine::ChunkingBlobEngine<Arc<dyn BlobStore>, Arc<dyn ChunkStore>>> {
247 use crate::{
248 blob_engine::ChunkingBlobEngine, blobstore::memory::MemoryBlobStore,
249 chunkstore::memory::MemoryChunkStore,
250 };
251
252 let blob_store = Arc::new(MemoryBlobStore::default()) as Arc<dyn BlobStore>;
253 let chunk_store = Arc::new(MemoryChunkStore::default()) as Arc<dyn ChunkStore>;
254
255 let blob_engine = ChunkingBlobEngine::builder()
256 .blob_store(blob_store)
257 .chunk_store(chunk_store)
258 .avg_chunk_size(512)
260 .build();
261
262 Arc::new(blob_engine)
263}