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