snix_store/pathinfoservice/
from_addr.rs1use super::PathInfoService;
2
3use crate::composition::REG;
4use snix_castore::Error;
5use snix_castore::composition::{
6 CompositionContext, DeserializeWithRegistry, ServiceBuilder, with_registry,
7};
8use std::sync::Arc;
9use url::Url;
10
11pub async fn from_addr(
34 uri: &str,
35 context: Option<&CompositionContext<'_>>,
36) -> Result<Arc<dyn PathInfoService>, Box<dyn std::error::Error + Send + Sync>> {
37 #[allow(unused_mut)]
38 let mut url =
39 Url::parse(uri).map_err(|e| Error::StorageError(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::blobservice::{BlobService, MemoryBlobServiceConfig};
63 use snix_castore::composition::{Composition, DeserializeWithRegistry, ServiceBuilder};
64 use snix_castore::directoryservice::{DirectoryService, MemoryDirectoryServiceConfig};
65 use std::sync::LazyLock;
66 use tempfile::TempDir;
67
68 static TMPDIR_REDB_1: LazyLock<TempDir> = LazyLock::new(|| TempDir::new().unwrap());
69 static TMPDIR_REDB_2: LazyLock<TempDir> = LazyLock::new(|| TempDir::new().unwrap());
70
71 #[rstest]
74 #[case::unsupported_scheme("http://foo.example/test", false)]
76 #[case::memory_valid("memory://", true)]
78 #[case::memory_invalid_host("memory://foo", false)]
80 #[case::memory_invalid_root_path("memory:///", false)]
82 #[case::memory_invalid_root_path_foo("memory:///foo", false)]
84 #[case::redb_invalid_host_with_valid_path(&format!("redb://foo.example{}", &TMPDIR_REDB_1.path().join("bar").to_str().unwrap()), false)]
86 #[case::redb_invalid_root("redb:///", false)]
88 #[case::redb_valid_path(&format!("redb://{}", &TMPDIR_REDB_2.path().join("foo").to_str().unwrap()), true)]
90 #[case::redb_valid_in_memory("redb://", true)]
92 #[case::correct_nix_https("nix+https://cache.nixos.org", true)]
94 #[case::correct_nix_http("nix+http://cache.nixos.org", true)]
96 #[case::correct_nix_http_with_subpath("nix+http://192.0.2.1/foo", true)]
98 #[case::correct_nix_http_with_subpath_and_port("nix+http://[::1]:8080/foo", true)]
100 #[case::correct_nix_https_with_trusted_public_key(
102 "nix+https://cache.nixos.org?trusted-public-keys=cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=",
103 true
104 )]
105 #[case::correct_nix_https_with_two_trusted_public_keys(
107 "nix+https://cache.nixos.org?trusted-public-keys=cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=%20foo:jp4fCEx9tBEId/L0ZsVJ26k0wC0fu7vJqLjjIGFkup8=",
108 true
109 )]
110 #[case::grpc_valid_unix_socket("grpc+unix:///path/to/somewhere", true)]
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 let mut comp = Composition::new(®);
138 comp.extend(vec![(
139 "root".into(),
140 DeserializeWithRegistry(Box::new(MemoryBlobServiceConfig {})
141 as Box<dyn ServiceBuilder<Output = dyn BlobService>>),
142 )]);
143 comp.extend(vec![(
144 "root".into(),
145 DeserializeWithRegistry(Box::new(MemoryDirectoryServiceConfig {})
146 as Box<dyn ServiceBuilder<Output = dyn DirectoryService>>),
147 )]);
148
149 let resp = from_addr(uri_str, Some(&comp.context())).await;
150
151 if exp_succeed {
152 resp.expect("should succeed");
153 } else {
154 assert!(resp.is_err(), "should fail");
155 }
156 }
157}