1use crate::aterm::write_escaped;
7use crate::derivation::{DerivationBuilder, OutputName, Outputs, OutputsBuilder};
8use crate::nixhash::Sha256;
9use crate::store_path::{StorePath, StorePathRef};
10use data_encoding::HEXLOWER;
11use smol_str::format_smolstr;
12
13use std::{collections::BTreeSet, io, io::Error, io::Write};
14
15pub const DERIVATION_PREFIX: &str = "Derive";
16pub const PAREN_OPEN: char = '(';
17pub const PAREN_CLOSE: char = ')';
18pub const BRACKET_OPEN: char = '[';
19pub const BRACKET_CLOSE: char = ']';
20pub const COMMA: char = ',';
21pub const QUOTE: char = '"';
22
23pub(super) trait AtermWriteable {
29 fn aterm_write(&self, writer: &mut impl io::Write) -> std::io::Result<()>;
30}
31impl<F: AtermWriteable> AtermWriteable for &F {
32 fn aterm_write(&self, writer: &mut impl io::Write) -> std::io::Result<()> {
33 (**self).aterm_write(writer)
34 }
35}
36
37impl<'a> AtermWriteable for StorePathRef<'a> {
38 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
39 write_char(writer, QUOTE)?;
40 write!(writer, "{}", self.as_absolute_path_fmt())?;
41 write_char(writer, QUOTE)?;
42 Ok(())
43 }
44}
45
46impl AtermWriteable for StorePath {
47 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
48 self.as_ref().aterm_write(writer)
49 }
50}
51
52impl AtermWriteable for String {
53 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
54 write_field(writer, self, true)
55 }
56}
57
58impl AtermWriteable for &str {
59 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
60 write_field(writer, self, true)
61 }
62}
63
64impl AtermWriteable for [u8] {
65 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
66 write_field(writer, HEXLOWER.encode(self), false)
67 }
68}
69
70impl AtermWriteable for Sha256 {
71 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
72 write_field(writer, format_smolstr!("{self:x}").as_bytes(), false)
73 }
74}
75
76impl AtermWriteable for OutputName {
77 fn aterm_write(&self, writer: &mut impl io::Write) -> std::io::Result<()> {
78 write_field(writer, self.as_ref(), false)
79 }
80}
81
82impl AtermWriteable for OutputsBuilder {
83 fn aterm_write(&self, writer: &mut impl io::Write) -> std::io::Result<()> {
84 write_char(writer, BRACKET_OPEN)?;
85 match self {
86 OutputsBuilder::Fixed(output_hash) => {
87 write_char(writer, PAREN_OPEN)?;
88
89 write_array_elements(
90 writer,
91 [
92 OutputName::out().as_str(),
93 "",
94 output_hash.as_mode_and_algo_str(),
95 &data_encoding::HEXLOWER.encode(output_hash.hash.digest_as_bytes()),
96 ],
97 )?;
98
99 write_char(writer, PAREN_CLOSE)?;
100 }
101 OutputsBuilder::InputAddressed(output_names) => {
102 for (ii, output_name) in output_names.iter().enumerate() {
103 if ii > 0 {
104 write_char(writer, COMMA)?;
105 }
106
107 write_char(writer, PAREN_OPEN)?;
108 write_array_elements(writer, [output_name.as_str(), "", "", ""])?;
109 write_char(writer, PAREN_CLOSE)?;
110 }
111 }
112 }
113 write_char(writer, BRACKET_CLOSE)?;
114
115 Ok(())
116 }
117}
118
119impl AtermWriteable for Outputs {
120 fn aterm_write(&self, writer: &mut impl io::Write) -> std::io::Result<()> {
121 write_char(writer, BRACKET_OPEN)?;
122 if let Some((output_hash, store_path)) = self.as_fixed_output() {
123 let path_str = store_path.to_absolute_path();
124
125 write_char(writer, PAREN_OPEN)?;
126 write_array_elements(
127 writer,
128 [
129 OutputName::out().as_str(),
130 &path_str,
131 output_hash.as_mode_and_algo_str(),
132 &data_encoding::HEXLOWER.encode(output_hash.hash.digest_as_bytes()),
133 ],
134 )?;
135 write_char(writer, PAREN_CLOSE)?;
136 } else {
137 for (ii, (output_name, store_path)) in self.iter().enumerate() {
138 let path_str = store_path.to_absolute_path();
139 if ii > 0 {
140 write_char(writer, COMMA)?;
141 }
142
143 write_char(writer, PAREN_OPEN)?;
144 write_array_elements(writer, [output_name.as_str(), &path_str, "", ""])?;
145 write_char(writer, PAREN_CLOSE)?;
146 }
147 }
148 write_char(writer, BRACKET_CLOSE)?;
149
150 Ok(())
151 }
152}
153
154impl DerivationBuilder {
155 pub(super) fn serialize_with_replacements<'a, E, EK, EV, O, I, ID>(
162 &self,
163 writer: &mut impl std::io::Write,
164 environment: E,
165 outputs: O,
166 input_derivations_sorted: I,
167 ) -> Result<(), io::Error>
168 where
169 E: IntoIterator<Item = (EK, EV)>,
170 EK: AsRef<[u8]>,
171 EV: AsRef<[u8]>,
172 O: AtermWriteable,
173 I: Iterator<Item = (ID, &'a BTreeSet<OutputName>)>,
174 ID: AtermWriteable,
175 {
176 writer.write_all(DERIVATION_PREFIX.as_bytes())?;
177 write_char(writer, PAREN_OPEN)?;
178
179 outputs.aterm_write(writer)?;
180 write_char(writer, COMMA)?;
181
182 write_input_derivations(writer, input_derivations_sorted)?;
183 write_char(writer, COMMA)?;
184
185 write_input_sources(writer, &self.input_sources)?;
186 write_char(writer, COMMA)?;
187
188 write_system(writer, &self.system)?;
189 write_char(writer, COMMA)?;
190
191 write_builder(writer, &self.command)?;
192 write_char(writer, COMMA)?;
193
194 write_arguments(writer, &self.arguments)?;
195 write_char(writer, COMMA)?;
196
197 write_environment(writer, environment)?;
198
199 write_char(writer, PAREN_CLOSE)?;
200
201 Ok(())
202 }
203}
204
205pub(crate) fn write_char(writer: &mut impl Write, c: char) -> io::Result<()> {
207 let mut buf = [0; 4];
208 let b = c.encode_utf8(&mut buf).as_bytes();
209 writer.write_all(b)
210}
211
212pub(crate) fn write_field<S: AsRef<[u8]>>(
217 writer: &mut impl Write,
218 s: S,
219 escape: bool,
220) -> io::Result<()> {
221 write_char(writer, QUOTE)?;
222
223 if !escape {
224 writer.write_all(s.as_ref())?;
225 } else {
226 write_escaped(s, writer)?;
227 }
228
229 write_char(writer, QUOTE)?;
230
231 Ok(())
232}
233
234fn write_array_elements<S>(
235 writer: &mut impl Write,
236 elements: impl IntoIterator<Item = S>,
237) -> Result<(), io::Error>
238where
239 S: AtermWriteable,
240{
241 for (index, element) in elements.into_iter().enumerate() {
242 if index > 0 {
243 write_char(writer, COMMA)?;
244 }
245
246 element.aterm_write(writer)?;
247 }
248
249 Ok(())
250}
251
252fn write_input_derivations<'a, I, K>(
253 writer: &mut impl Write,
254 input_derivations_sorted: I,
255) -> Result<(), io::Error>
256where
257 I: Iterator<Item = (K, &'a BTreeSet<OutputName>)>,
258 K: AtermWriteable,
259{
260 write_char(writer, BRACKET_OPEN)?;
261
262 for (ii, (k, output_names)) in input_derivations_sorted.enumerate() {
263 if ii > 0 {
264 write_char(writer, COMMA)?;
265 }
266
267 write_char(writer, PAREN_OPEN)?;
268 k.aterm_write(writer)?;
269 write_char(writer, COMMA)?;
270
271 write_char(writer, BRACKET_OPEN)?;
272 write_array_elements(writer, output_names)?;
273 write_char(writer, BRACKET_CLOSE)?;
274
275 write_char(writer, PAREN_CLOSE)?;
276 }
277
278 write_char(writer, BRACKET_CLOSE)?;
279
280 Ok(())
281}
282
283fn write_input_sources(
284 writer: &mut impl Write,
285 input_sources: &BTreeSet<StorePath>,
286) -> Result<(), io::Error> {
287 write_char(writer, BRACKET_OPEN)?;
288 write_array_elements(writer, input_sources)?;
289 write_char(writer, BRACKET_CLOSE)?;
290
291 Ok(())
292}
293
294fn write_system(writer: &mut impl Write, platform: &str) -> Result<(), Error> {
295 write_field(writer, platform, true)?;
296 Ok(())
297}
298
299fn write_builder(writer: &mut impl Write, builder: &str) -> Result<(), Error> {
300 write_field(writer, builder, true)?;
301 Ok(())
302}
303
304fn write_arguments(writer: &mut impl Write, arguments: &[String]) -> Result<(), io::Error> {
305 write_char(writer, BRACKET_OPEN)?;
306 write_array_elements(writer, arguments)?;
307 write_char(writer, BRACKET_CLOSE)?;
308
309 Ok(())
310}
311
312fn write_environment<E, K, V>(writer: &mut impl Write, environment: E) -> Result<(), io::Error>
313where
314 E: IntoIterator<Item = (K, V)>,
315 K: AsRef<[u8]>,
316 V: AsRef<[u8]>,
317{
318 write_char(writer, BRACKET_OPEN)?;
319
320 for (i, (k, v)) in environment.into_iter().enumerate() {
321 if i > 0 {
322 write_char(writer, COMMA)?;
323 }
324
325 write_char(writer, PAREN_OPEN)?;
326 write_field(writer, k, false)?;
327 write_char(writer, COMMA)?;
328 write_field(writer, v, true)?;
329 write_char(writer, PAREN_CLOSE)?;
330 }
331
332 write_char(writer, BRACKET_CLOSE)?;
333
334 Ok(())
335}
336
337pub(super) fn shadow<A, B, K, V>(main: A, shadow: B) -> ShadowIter<A::IntoIter, B::IntoIter>
345where
346 K: Ord,
347 A: IntoIterator<Item = (K, V)>,
348 B: IntoIterator<Item = (K, V)>,
349{
350 ShadowIter {
351 main: main.into_iter().peekable(),
352 shadow: shadow.into_iter().peekable(),
353 }
354}
355
356pub(super) struct ShadowIter<A: Iterator, B: Iterator> {
358 main: std::iter::Peekable<A>,
359 shadow: std::iter::Peekable<B>,
360}
361
362impl<A, B, K, V> Iterator for ShadowIter<A, B>
363where
364 K: Ord,
365 A: Iterator<Item = (K, V)>,
366 B: Iterator<Item = (K, V)>,
367{
368 type Item = (K, V);
369
370 fn next(&mut self) -> Option<Self::Item> {
371 match (self.main.peek(), self.shadow.peek()) {
372 (Some((main_k, _)), Some((shadow_k, _))) if main_k < shadow_k => self.main.next(),
373 (Some((main_k, _)), Some((shadow_k, _))) if main_k == shadow_k => {
374 self.main.next();
375 self.shadow.next()
376 }
377 (_, Some(_)) => self.shadow.next(),
378 (Some(_), None) => self.main.next(),
379 (None, None) => None,
380 }
381 }
382}