snix_store/pathinfoservice/
from_addr.rs1use super::PathInfoService;
2
3use crate::composition::REG;
4use snix_castore::composition::{
5 CompositionContext, DeserializeWithRegistry, ServiceBuilder, with_registry,
6};
7use std::sync::Arc;
8use url::Url;
9
10pub async fn from_addr(
35 uri: &str,
36 context: Option<&CompositionContext<'_>>,
37) -> Result<Arc<dyn PathInfoService>, Box<dyn std::error::Error + Send + Sync>> {
38 #[allow(unused_mut)]
39 let mut url = Url::parse(uri).map_err(|e| format!("unable to parse url: {e}"))?;
40
41 let path_info_service_config = with_registry(®, || {
42 <DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn PathInfoService>>>>::try_from(
43 url,
44 )
45 })?
46 .0;
47 let path_info_service = path_info_service_config
48 .build(
49 "anonymous",
50 context.unwrap_or(&CompositionContext::blank(®)),
51 )
52 .await?;
53
54 Ok(path_info_service)
55}
56
57#[cfg(test)]
58mod tests {
59 use super::from_addr;
60 use crate::composition::REG;
61 use rstest::rstest;
62 use snix_castore::{
63 blobstore::{BlobStore, memory::MemoryBlobStoreConfig},
64 chunkstore::{ChunkStore, memory::MemoryChunkStoreConfig},
65 composition::{Composition, DeserializeWithRegistry, ServiceBuilder},
66 directoryservice::DirectoryService,
67 };
68
69 use std::sync::LazyLock;
70 use tempfile::TempDir;
71
72 static TMPDIR_REDB_1: LazyLock<TempDir> = LazyLock::new(|| TempDir::new().unwrap());
73 static TMPDIR_REDB_2: LazyLock<TempDir> = LazyLock::new(|| TempDir::new().unwrap());
74
75 #[rstest]
78 #[case::unsupported_scheme("http://foo.example/test", false)]
80 #[case::redb_invalid_missing_path("redb://", false)]
82 #[case::redb_invalid_root("redb:///", false)]
84 #[case::redb_invalid_host("redb://foo.example", false)]
86 #[case::redb_invalid_path_authority(&format!("redb://{}", TMPDIR_REDB_1.path().join("foo").to_str().unwrap()), false)]
88 #[case::redb_valid_path(&format!("redb:{}", TMPDIR_REDB_1.path().join("foo").to_str().unwrap()), true)]
90 #[case::redb_invalid_host_with_valid_path(&format!("redb://foo.example{}", TMPDIR_REDB_2.path().join("bar").to_str().unwrap()), false)]
92 #[case::redb_memory_valid("redb+memory:", true)]
94 #[case::redb_memory_invalid_path("redb+memory:/foo/bar", false)]
96 #[case::redb_memory_invalid_authority("redb+memory://", false)]
98 #[case::redb_memory_invalid_authority_path("redb+memory:///foo/bar", false)]
100 #[case::nix_http(
101 "nix+https://cache.nixos.org?trusted_public_keys[0]=cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=",
102 true
103 )]
104 #[case::nix_http_force_download_nar(
105 "nix+https://cache.nixos.org?trusted_public_keys[0]=cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=&force_download_nar=true",
106 true
107 )]
108 #[case::grpc_valid_unix_socket("grpc+unix:/path/to/somewhere", true)]
110 #[case::grpc_invalid_unix_socket_authority("grpc+unix:///path/to/somewhere", false)]
112 #[case::grpc_invalid_unix_socket_and_host("grpc+unix://host.example/path/to/somewhere", false)]
114 #[case::grpc_valid_ipv6_localhost_port_12345("grpc+http://[::1]:12345", true)]
116 #[case::grpc_valid_http_host_without_port("grpc+http://localhost", true)]
118 #[case::grpc_valid_https_host_without_port("grpc+https://localhost", true)]
120 #[case::grpc_invalid_host_and_path("grpc+http://localhost/some-path", false)]
122 #[cfg_attr(
124 all(feature = "cloud", feature = "integration"),
125 case::bigtable_valid(
126 "bigtable://instance-1?project_id=project-1&table_name=table-1&family_name=cf1",
127 true
128 )
129 )]
130 #[cfg_attr(
132 all(feature = "cloud", feature = "integration"),
133 case::bigtable_invalid_missing_fields("bigtable://instance-1", false)
134 )]
135 #[tokio::test]
136 async fn test_from_addr_tokio(#[case] uri_str: &str, #[case] exp_succeed: bool) {
137 use snix_castore::directoryservice::RedbDirectoryServiceConfig;
138
139 let mut comp = Composition::new(®);
140 comp.extend(vec![(
141 "root".into(),
142 DeserializeWithRegistry(Box::new(MemoryBlobStoreConfig {})
143 as Box<dyn ServiceBuilder<Output = dyn BlobStore>>),
144 )]);
145 comp.extend(vec![(
146 "root".into(),
147 DeserializeWithRegistry(Box::new(MemoryChunkStoreConfig {})
148 as Box<dyn ServiceBuilder<Output = dyn ChunkStore>>),
149 )]);
150 comp.extend(vec![(
151 "root".into(),
152 DeserializeWithRegistry(Box::new(RedbDirectoryServiceConfig::default())
153 as Box<dyn ServiceBuilder<Output = dyn DirectoryService>>),
154 )]);
155
156 let resp = from_addr(uri_str, Some(&comp.context())).await;
157
158 if exp_succeed {
159 resp.expect("should succeed");
160 } else {
161 assert!(resp.is_err(), "should fail");
162 }
163 }
164}