snix_castore/
utils.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3use url::Url;
4
5use crate::blobservice::BlobService;
6use crate::composition::{
7    Composition, DeserializeWithRegistry, REG, ServiceBuilder, with_registry,
8};
9use crate::directoryservice::DirectoryService;
10
11#[derive(serde::Deserialize, Default)]
12pub struct CompositionConfigs {
13    pub blobservices:
14        HashMap<String, DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn BlobService>>>>,
15    pub directoryservices: HashMap<
16        String,
17        DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn DirectoryService>>>,
18    >,
19}
20
21/// Provides a set of clap arguments to configure snix-castore services.
22///
23/// This particular variant has defaults tailored for usecases accessing data
24/// directly locally, like the `snix-store daemon` command.
25#[derive(clap::Parser, Clone)]
26#[group(id = "CastoreServiceUrls")]
27pub struct ServiceUrls {
28    #[arg(
29        long,
30        env,
31        default_value = "objectstore+file:///var/lib/snix-store/blobs.object_store"
32    )]
33    pub blob_service_addr: String,
34
35    #[arg(
36        long,
37        env,
38        default_value = "redb:///var/lib/snix-store/directories.redb"
39    )]
40    pub directory_service_addr: String,
41
42    /// Path to a TOML file describing the way the services should be composed
43    /// Experimental because the format is not final.
44    /// If specified, the other service addrs are ignored.
45    #[cfg(feature = "xp-composition-cli")]
46    #[arg(long, env)]
47    experimental_store_composition: Option<String>,
48}
49
50/// Provides a set of clap arguments to configure snix-castore services.
51///
52/// This particular variant has defaults tailored for usecases accessing data
53/// from another running snix daemon, via gRPC.
54#[derive(clap::Parser, Clone)]
55#[group(id = "CastoreServiceUrlsGrpc")]
56pub struct ServiceUrlsGrpc {
57    #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
58    blob_service_addr: String,
59
60    #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
61    directory_service_addr: String,
62
63    #[cfg(feature = "xp-composition-cli")]
64    #[arg(long, env)]
65    experimental_store_composition: Option<String>,
66}
67
68/// Provides a set of clap arguments to configure snix-castore services.
69///
70/// This particular variant has defaults tailored for usecases keeping all data
71/// in memory.
72/// It's currently used in snix-cli, as we don't really care about persistency
73/// there yet, and using something else here might make some perf output harder
74/// to interpret.
75#[derive(clap::Parser, Clone)]
76#[group(id = "CastoreServiceUrlsMemory")]
77pub struct ServiceUrlsMemory {
78    #[arg(long, env, default_value = "memory://")]
79    blob_service_addr: String,
80
81    #[arg(long, env, default_value = "memory://")]
82    directory_service_addr: String,
83
84    #[cfg(feature = "xp-composition-cli")]
85    #[arg(long, env)]
86    experimental_store_composition: Option<String>,
87}
88
89impl From<ServiceUrlsGrpc> for ServiceUrls {
90    fn from(urls: ServiceUrlsGrpc) -> ServiceUrls {
91        ServiceUrls {
92            blob_service_addr: urls.blob_service_addr,
93            directory_service_addr: urls.directory_service_addr,
94            #[cfg(feature = "xp-composition-cli")]
95            experimental_store_composition: urls.experimental_store_composition,
96        }
97    }
98}
99
100impl From<ServiceUrlsMemory> for ServiceUrls {
101    fn from(urls: ServiceUrlsMemory) -> ServiceUrls {
102        ServiceUrls {
103            blob_service_addr: urls.blob_service_addr,
104            directory_service_addr: urls.directory_service_addr,
105            #[cfg(feature = "xp-composition-cli")]
106            experimental_store_composition: urls.experimental_store_composition,
107        }
108    }
109}
110
111/// Deserializes service addresses into composition config, configuring each
112/// service as the single "root".
113/// If the `xp-composition-cli` feature is enabled, and a file specified in the
114/// `--experimental-store-composition` parameter, this is used instead.
115pub async fn addrs_to_configs(
116    urls: impl Into<ServiceUrls>,
117) -> Result<CompositionConfigs, Box<dyn std::error::Error + Send + Sync>> {
118    let urls: ServiceUrls = urls.into();
119
120    #[cfg(feature = "xp-composition-cli")]
121    if let Some(conf_path) = urls.experimental_store_composition {
122        let conf_text = tokio::fs::read_to_string(conf_path).await?;
123        return Ok(with_registry(&REG, || toml::from_str(&conf_text))?);
124    }
125
126    let mut configs: CompositionConfigs = Default::default();
127
128    let blob_service_url = Url::parse(&urls.blob_service_addr)?;
129    let directory_service_url = Url::parse(&urls.directory_service_addr)?;
130    configs.blobservices.insert(
131        "root".into(),
132        with_registry(&REG, || blob_service_url.try_into())?,
133    );
134    configs.directoryservices.insert(
135        "root".into(),
136        with_registry(&REG, || directory_service_url.try_into())?,
137    );
138    Ok(configs)
139}
140
141/// Construct the castore handles from their addrs.
142pub async fn construct_services(
143    urls: impl Into<ServiceUrls>,
144) -> Result<
145    (Arc<dyn BlobService>, Arc<dyn DirectoryService>),
146    Box<dyn std::error::Error + Send + Sync>,
147> {
148    let configs = addrs_to_configs(urls).await?;
149    construct_services_from_configs(configs).await
150}
151
152/// Construct the castore handles from their addrs.
153pub async fn construct_services_from_configs(
154    configs: CompositionConfigs,
155) -> Result<
156    (Arc<dyn BlobService>, Arc<dyn DirectoryService>),
157    Box<dyn std::error::Error + Send + Sync>,
158> {
159    let mut comp = Composition::new(&REG);
160
161    comp.extend(configs.blobservices);
162    comp.extend(configs.directoryservices);
163
164    let blob_service: Arc<dyn BlobService> = comp.build("root").await?;
165    let directory_service: Arc<dyn DirectoryService> = comp.build("root").await?;
166
167    Ok((blob_service, directory_service))
168}