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_valid_path(&format!("redb://{}", &TMPDIR_REDB_1.path().join("foo").to_str().unwrap()), true)]
80 #[case::redb_invalid_host_with_valid_path(&format!("redb://foo.example{}", &TMPDIR_REDB_2.path().join("bar").to_str().unwrap()), false)]
82 #[case::redb_memory_valid("redb+memory:", true)]
84 #[case::redb_memory_invalid_path("redb+memory:/foo/bar", false)]
86 #[case::redb_memory_invalid_authority("redb+memory://", false)]
88 #[case::redb_memory_invalid_authority_path("redb+memory:///foo/bar", false)]
90 #[case::nix_http(
91 "nix+https://cache.nixos.org?trusted_public_keys[0]=cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=",
92 true
93 )]
94 #[case::grpc_valid_unix_socket("grpc+unix:///path/to/somewhere", true)]
96 #[case::grpc_invalid_unix_socket_and_host("grpc+unix://host.example/path/to/somewhere", false)]
98 #[case::grpc_valid_ipv6_localhost_port_12345("grpc+http://[::1]:12345", true)]
100 #[case::grpc_valid_http_host_without_port("grpc+http://localhost", true)]
102 #[case::grpc_valid_https_host_without_port("grpc+https://localhost", true)]
104 #[case::grpc_invalid_host_and_path("grpc+http://localhost/some-path", false)]
106 #[cfg_attr(
108 all(feature = "cloud", feature = "integration"),
109 case::bigtable_valid(
110 "bigtable://instance-1?project_id=project-1&table_name=table-1&family_name=cf1",
111 true
112 )
113 )]
114 #[cfg_attr(
116 all(feature = "cloud", feature = "integration"),
117 case::bigtable_invalid_missing_fields("bigtable://instance-1", false)
118 )]
119 #[tokio::test]
120 async fn test_from_addr_tokio(#[case] uri_str: &str, #[case] exp_succeed: bool) {
121 use snix_castore::directoryservice::RedbDirectoryServiceConfig;
122
123 let mut comp = Composition::new(®);
124 comp.extend(vec![(
125 "root".into(),
126 DeserializeWithRegistry(Box::new(MemoryBlobServiceConfig {})
127 as Box<dyn ServiceBuilder<Output = dyn BlobService>>),
128 )]);
129 comp.extend(vec![(
130 "root".into(),
131 DeserializeWithRegistry(Box::new(RedbDirectoryServiceConfig::default())
132 as Box<dyn ServiceBuilder<Output = dyn DirectoryService>>),
133 )]);
134
135 let resp = from_addr(uri_str, Some(&comp.context())).await;
136
137 if exp_succeed {
138 resp.expect("should succeed");
139 } else {
140 assert!(resp.is_err(), "should fail");
141 }
142 }
143}