1use std::{collections::HashMap, sync::Arc};
2
3use snix_castore::utils as castore_utils;
4use snix_castore::{blobservice::BlobService, directoryservice::DirectoryService};
5use url::Url;
6
7use crate::composition::REG;
8use crate::nar::NarCalculationService;
9use crate::pathinfoservice::PathInfoService;
10use snix_castore::composition::{
11 Composition, DeserializeWithRegistry, ServiceBuilder, with_registry,
12};
13
14#[derive(serde::Deserialize, Default)]
15pub struct CompositionConfigs {
16 pub blobservices:
17 HashMap<String, DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn BlobService>>>>,
18 pub directoryservices: HashMap<
19 String,
20 DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn DirectoryService>>>,
21 >,
22 pub pathinfoservices: HashMap<
23 String,
24 DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn PathInfoService>>>,
25 >,
26}
27
28#[derive(clap::Parser, Clone)]
33#[group(id = "StoreServiceUrls")]
34pub struct ServiceUrls {
35 #[clap(flatten)]
36 pub castore_service_addrs: castore_utils::ServiceUrls,
37
38 #[arg(long, env, default_value = "redb:/var/lib/snix-store/pathinfo.redb")]
39 pub path_info_service_addr: String,
40}
41
42#[derive(clap::Parser, Clone)]
47#[group(id = "StoreServiceUrlsGrpc")]
48pub struct ServiceUrlsGrpc {
49 #[clap(flatten)]
50 castore_service_addrs: castore_utils::ServiceUrlsGrpc,
51
52 #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
53 path_info_service_addr: String,
54}
55
56#[derive(clap::Parser, Clone)]
64#[group(id = "StoreServiceUrlsMemory")]
65pub struct ServiceUrlsMemory {
66 #[clap(flatten)]
67 castore_service_addrs: castore_utils::ServiceUrlsMemory,
68
69 #[arg(long, env, default_value = "redb+memory:")]
70 path_info_service_addr: String,
71}
72
73impl From<ServiceUrlsGrpc> for ServiceUrls {
74 fn from(urls: ServiceUrlsGrpc) -> ServiceUrls {
75 ServiceUrls {
76 castore_service_addrs: urls.castore_service_addrs.into(),
77 path_info_service_addr: urls.path_info_service_addr,
78 }
79 }
80}
81
82impl From<ServiceUrlsMemory> for ServiceUrls {
83 fn from(urls: ServiceUrlsMemory) -> ServiceUrls {
84 ServiceUrls {
85 castore_service_addrs: urls.castore_service_addrs.into(),
86 path_info_service_addr: urls.path_info_service_addr,
87 }
88 }
89}
90
91pub async fn addrs_to_configs(
96 urls: impl Into<ServiceUrls>,
97) -> Result<CompositionConfigs, Box<dyn std::error::Error + Send + Sync>> {
98 let urls: ServiceUrls = urls.into();
99
100 #[cfg(feature = "xp-composition-cli")]
101 if let Some(conf_path) = urls.castore_service_addrs.experimental_store_composition {
102 let conf_text = tokio::fs::read_to_string(conf_path).await?;
103 return Ok(with_registry(®, || toml::from_str(&conf_text))?);
104 }
105
106 let mut configs: CompositionConfigs = Default::default();
107
108 let blob_service_url = Url::parse(&urls.castore_service_addrs.blob_service_addr)?;
109 let directory_service_url = Url::parse(&urls.castore_service_addrs.directory_service_addr)?;
110 let path_info_service_url = Url::parse(&urls.path_info_service_addr)?;
111
112 configs.blobservices.insert(
113 "root".into(),
114 with_registry(®, || blob_service_url.try_into())?,
115 );
116 configs.directoryservices.insert(
117 "root".into(),
118 with_registry(®, || directory_service_url.try_into())?,
119 );
120 configs.pathinfoservices.insert(
121 "root".into(),
122 with_registry(®, || path_info_service_url.try_into())?,
123 );
124
125 Ok(configs)
126}
127
128pub async fn construct_services(
130 urls: impl Into<ServiceUrls>,
131) -> Result<
132 (
133 Arc<dyn BlobService>,
134 Arc<dyn DirectoryService>,
135 Arc<dyn PathInfoService>,
136 Arc<dyn NarCalculationService>,
137 ),
138 Box<dyn std::error::Error + Send + Sync>,
139> {
140 let configs = addrs_to_configs(urls).await?;
141 construct_services_from_configs(configs).await
142}
143
144pub async fn construct_services_from_configs(
146 configs: CompositionConfigs,
147) -> Result<
148 (
149 Arc<dyn BlobService>,
150 Arc<dyn DirectoryService>,
151 Arc<dyn PathInfoService>,
152 Arc<dyn NarCalculationService>,
153 ),
154 Box<dyn std::error::Error + Send + Sync>,
155> {
156 let mut comp = Composition::new(®);
157
158 comp.extend(configs.blobservices);
159 comp.extend(configs.directoryservices);
160 comp.extend(configs.pathinfoservices);
161
162 let blob_service: Arc<dyn BlobService> = comp.build("root").await?;
163 let directory_service: Arc<dyn DirectoryService> = comp.build("root").await?;
164 let path_info_service: Arc<dyn PathInfoService> = comp.build("root").await?;
165
166 let nar_calculation_service: Arc<dyn NarCalculationService> = path_info_service
170 .nar_calculation_service()
171 .unwrap_or_else(|| {
172 Arc::new(crate::nar::Renderer::new(
173 blob_service.clone(),
174 directory_service.clone(),
175 ))
176 });
177
178 Ok((
179 blob_service,
180 directory_service,
181 path_info_service,
182 nar_calculation_service,
183 ))
184}
185
186pub fn gen_test_pathinfo_service() -> impl PathInfoService + Clone {
188 crate::pathinfoservice::RedbPathInfoService::new_temporary(
189 "test".to_string(),
190 crate::pathinfoservice::RedbPathInfoServiceConfig::default(),
191 )
192 .expect("creating pathinfoservice to succeed")
193}