nix_compat/nix_daemon/
mod.rs1pub mod worker_protocol;
2
3use std::io::Result;
4
5use tokio::io::AsyncRead;
6use tracing::warn;
7use types::{AddToStoreNarRequest, QueryValidPaths, UnkeyedValidPathInfo};
8
9use crate::store_path::StorePath;
10
11pub mod framing;
12pub mod handler;
13pub mod types;
14
15#[cfg(test)]
16use mockall::automock;
17
18#[cfg_attr(test, automock)]
20pub trait NixDaemonIO: Sync {
21 fn is_valid_path(
22 &self,
23 path: &StorePath<String>,
24 ) -> impl std::future::Future<Output = Result<bool>> + Send {
25 async move { Ok(self.query_path_info(path).await?.is_some()) }
26 }
27
28 fn query_path_info(
29 &self,
30 path: &StorePath<String>,
31 ) -> impl std::future::Future<Output = Result<Option<UnkeyedValidPathInfo>>> + Send;
32
33 fn query_path_from_hash_part(
34 &self,
35 hash: &[u8],
36 ) -> impl std::future::Future<Output = Result<Option<UnkeyedValidPathInfo>>> + Send;
37
38 fn query_valid_paths(
39 &self,
40 request: &QueryValidPaths,
41 ) -> impl std::future::Future<Output = Result<Vec<StorePath<String>>>> + Send {
42 async move {
43 if request.substitute {
44 warn!("snix does not yet support substitution, ignoring the 'substitute' flag...");
45 }
46
47 let mut results: Vec<StorePath<String>> = Vec::with_capacity(request.paths.len());
48
49 for path in request.paths.iter() {
50 if self.is_valid_path(path).await? {
51 results.push(path.clone());
52 }
53 }
54
55 Ok(results)
56 }
57 }
58
59 fn query_valid_derivers(
60 &self,
61 path: &StorePath<String>,
62 ) -> impl std::future::Future<Output = Result<Vec<StorePath<String>>>> + Send {
63 async move {
64 let result = self.query_path_info(path).await?;
65 let result: Vec<_> = result.into_iter().filter_map(|info| info.deriver).collect();
66 Ok(result)
67 }
68 }
69
70 #[cfg_attr(test, mockall::concretize)]
71 fn add_to_store_nar<R>(
72 &self,
73 request: AddToStoreNarRequest,
74 reader: &mut R,
75 ) -> impl std::future::Future<Output = Result<()>> + Send
76 where
77 R: AsyncRead + Send + Unpin;
78}
79
80#[cfg(test)]
81mod tests {
82
83 use crate::{nix_daemon::types::QueryValidPaths, store_path::StorePath};
84
85 use super::{types::UnkeyedValidPathInfo, NixDaemonIO};
86
87 pub struct MockNixDaemonIO {
90 query_path_info_result: Option<UnkeyedValidPathInfo>,
91 }
92
93 impl NixDaemonIO for MockNixDaemonIO {
94 async fn query_path_info(
95 &self,
96 _path: &StorePath<String>,
97 ) -> std::io::Result<Option<UnkeyedValidPathInfo>> {
98 Ok(self.query_path_info_result.clone())
99 }
100
101 async fn query_path_from_hash_part(
102 &self,
103 _hash: &[u8],
104 ) -> std::io::Result<Option<UnkeyedValidPathInfo>> {
105 Ok(None)
106 }
107
108 async fn add_to_store_nar<R>(
109 &self,
110 _request: super::types::AddToStoreNarRequest,
111 _reader: &mut R,
112 ) -> std::io::Result<()>
113 where
114 R: tokio::io::AsyncRead + Send + Unpin,
115 {
116 Ok(())
117 }
118 }
119
120 #[tokio::test]
121 async fn test_is_valid_path_returns_true() {
122 let path =
123 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
124 .unwrap();
125 let io = MockNixDaemonIO {
126 query_path_info_result: Some(UnkeyedValidPathInfo::default()),
127 };
128
129 let result = io
130 .is_valid_path(&path)
131 .await
132 .expect("expected to get a non-empty response");
133 assert!(result, "expected to get true");
134 }
135
136 #[tokio::test]
137 async fn test_is_valid_path_returns_false() {
138 let path =
139 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
140 .unwrap();
141 let io = MockNixDaemonIO {
142 query_path_info_result: None,
143 };
144
145 let result = io
146 .is_valid_path(&path)
147 .await
148 .expect("expected to get a non-empty response");
149 assert!(!result, "expected to get false");
150 }
151
152 #[tokio::test]
153 async fn test_query_valid_paths_returns_empty_response() {
154 let path =
155 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
156 .unwrap();
157 let io = MockNixDaemonIO {
158 query_path_info_result: None,
159 };
160
161 let result = io
162 .query_valid_paths(&QueryValidPaths {
163 paths: vec![path],
164 substitute: false,
165 })
166 .await
167 .expect("expected to get a non-empty response");
168 assert_eq!(result, vec![], "expected to get empty response");
169 }
170
171 #[tokio::test]
172 async fn test_query_valid_paths_returns_non_empty_response() {
173 let path =
174 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
175 .unwrap();
176 let io = MockNixDaemonIO {
177 query_path_info_result: Some(UnkeyedValidPathInfo::default()),
178 };
179
180 let result = io
181 .query_valid_paths(&QueryValidPaths {
182 paths: vec![path.clone()],
183 substitute: false,
184 })
185 .await
186 .expect("expected to get a non-empty response");
187 assert_eq!(result, vec![path], "expected to get non empty response");
188 }
189
190 #[tokio::test]
191 async fn test_query_valid_derivers_returns_empty_response() {
192 let path =
193 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
194 .unwrap();
195 let io = MockNixDaemonIO {
196 query_path_info_result: None,
197 };
198
199 let result = io
200 .query_valid_derivers(&path)
201 .await
202 .expect("expected to get a non-empty response");
203 assert_eq!(result, vec![], "expected to get empty response");
204 }
205
206 #[tokio::test]
207 async fn test_query_valid_derivers_returns_non_empty_response() {
208 let path =
209 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
210 .unwrap();
211 let deriver = StorePath::<String>::from_bytes(
212 "z6r3bn5l51679pwkvh9nalp6c317z34m-hello.drv".as_bytes(),
213 )
214 .unwrap();
215 let io = MockNixDaemonIO {
216 query_path_info_result: Some(UnkeyedValidPathInfo {
217 deriver: Some(deriver.clone()),
218 nar_hash: "".to_owned(),
219 references: vec![],
220 registration_time: 0,
221 nar_size: 1,
222 ultimate: true,
223 signatures: vec![],
224 ca: None,
225 }),
226 };
227
228 let result = io
229 .query_valid_derivers(&path)
230 .await
231 .expect("expected to get a non-empty response");
232 assert_eq!(result, vec![deriver], "expected to get non empty response");
233 }
234}