nix_compat/nix_daemon/
mod.rs1pub mod worker_protocol;
2
3use std::io::Result;
4
5use tokio::io::AsyncRead;
6use tracing::warn;
7use types::{QueryValidPaths, UnkeyedValidPathInfo, ValidPathInfo};
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 info: ValidPathInfo,
74 reader: &mut R,
75 repair: bool,
76 dont_check_sigs: bool,
77 ) -> impl std::future::Future<Output = Result<()>> + Send
78 where
79 R: AsyncRead + Send + Unpin;
80}
81
82#[cfg(test)]
83mod tests {
84
85 use crate::{
86 nix_daemon::types::{NarHash, QueryValidPaths},
87 store_path::StorePath,
88 };
89
90 use super::{NixDaemonIO, types::UnkeyedValidPathInfo};
91
92 pub struct MockNixDaemonIO {
95 query_path_info_result: Option<UnkeyedValidPathInfo>,
96 }
97
98 impl NixDaemonIO for MockNixDaemonIO {
99 async fn query_path_info(
100 &self,
101 _path: &StorePath<String>,
102 ) -> std::io::Result<Option<UnkeyedValidPathInfo>> {
103 Ok(self.query_path_info_result.clone())
104 }
105
106 async fn query_path_from_hash_part(
107 &self,
108 _hash: &[u8],
109 ) -> std::io::Result<Option<UnkeyedValidPathInfo>> {
110 Ok(None)
111 }
112
113 async fn add_to_store_nar<R>(
114 &self,
115 _info: super::types::ValidPathInfo,
116 _reader: &mut R,
117 _repair: bool,
118 _dont_check_sigs: bool,
119 ) -> std::io::Result<()>
120 where
121 R: tokio::io::AsyncRead + Send + Unpin,
122 {
123 Ok(())
124 }
125 }
126
127 #[tokio::test]
128 async fn test_is_valid_path_returns_true() {
129 let path =
130 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
131 .unwrap();
132 let io = MockNixDaemonIO {
133 query_path_info_result: Some(UnkeyedValidPathInfo {
134 deriver: Some("00000000000000000000000000000000-_.drv".parse().unwrap()),
135 nar_hash: NarHash::from_digest([0u8; 32]),
136 references: Vec::new(),
137 registration_time: 0,
138 nar_size: 0,
139 ultimate: true,
140 signatures: Vec::new(),
141 ca: None,
142 }),
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 true");
150 }
151
152 #[tokio::test]
153 async fn test_is_valid_path_returns_false() {
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 .is_valid_path(&path)
163 .await
164 .expect("expected to get a non-empty response");
165 assert!(!result, "expected to get false");
166 }
167
168 #[tokio::test]
169 async fn test_query_valid_paths_returns_empty_response() {
170 let path =
171 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
172 .unwrap();
173 let io = MockNixDaemonIO {
174 query_path_info_result: None,
175 };
176
177 let result = io
178 .query_valid_paths(&QueryValidPaths {
179 paths: vec![path],
180 substitute: false,
181 })
182 .await
183 .expect("expected to get a non-empty response");
184 assert_eq!(result, vec![], "expected to get empty response");
185 }
186
187 #[tokio::test]
188 async fn test_query_valid_paths_returns_non_empty_response() {
189 let path =
190 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
191 .unwrap();
192 let io = MockNixDaemonIO {
193 query_path_info_result: Some(UnkeyedValidPathInfo {
194 deriver: Some("00000000000000000000000000000000-_.drv".parse().unwrap()),
195 nar_hash: NarHash::from_digest([0u8; 32]),
196 references: Vec::new(),
197 registration_time: 0,
198 nar_size: 0,
199 ultimate: true,
200 signatures: Vec::new(),
201 ca: None,
202 }),
203 };
204
205 let result = io
206 .query_valid_paths(&QueryValidPaths {
207 paths: vec![path.clone()],
208 substitute: false,
209 })
210 .await
211 .expect("expected to get a non-empty response");
212 assert_eq!(result, vec![path], "expected to get non empty response");
213 }
214
215 #[tokio::test]
216 async fn test_query_valid_derivers_returns_empty_response() {
217 let path =
218 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
219 .unwrap();
220 let io = MockNixDaemonIO {
221 query_path_info_result: None,
222 };
223
224 let result = io
225 .query_valid_derivers(&path)
226 .await
227 .expect("expected to get a non-empty response");
228 assert_eq!(result, vec![], "expected to get empty response");
229 }
230
231 #[tokio::test]
232 async fn test_query_valid_derivers_returns_non_empty_response() {
233 let path =
234 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
235 .unwrap();
236 let deriver = StorePath::<String>::from_bytes(
237 "z6r3bn5l51679pwkvh9nalp6c317z34m-hello.drv".as_bytes(),
238 )
239 .unwrap();
240 let io = MockNixDaemonIO {
241 query_path_info_result: Some(UnkeyedValidPathInfo {
242 deriver: Some(deriver.clone()),
243 nar_hash: NarHash::from_digest([0u8; 32]),
244 references: vec![],
245 registration_time: 0,
246 nar_size: 1,
247 ultimate: true,
248 signatures: vec![],
249 ca: None,
250 }),
251 };
252
253 let result = io
254 .query_valid_derivers(&path)
255 .await
256 .expect("expected to get a non-empty response");
257 assert_eq!(result, vec![deriver], "expected to get non empty response");
258 }
259}