1use super::output::Output;
7use crate::derivation::OutputName;
8use crate::nixhash::Sha256;
9use crate::store_path::{StorePath, StorePathRef};
10use crate::{aterm::write_escaped, derivation::Derivation};
11use data_encoding::HEXLOWER;
12use smol_str::format_smolstr;
13
14use std::{collections::BTreeSet, io, io::Error, io::Write};
15
16pub const DERIVATION_PREFIX: &str = "Derive";
17pub const PAREN_OPEN: char = '(';
18pub const PAREN_CLOSE: char = ')';
19pub const BRACKET_OPEN: char = '[';
20pub const BRACKET_CLOSE: char = ']';
21pub const COMMA: char = ',';
22pub const QUOTE: char = '"';
23
24pub(super) trait AtermWriteable {
30 fn aterm_write(&self, writer: &mut impl io::Write) -> std::io::Result<()>;
31}
32
33impl<'a> AtermWriteable for &StorePathRef<'a> {
34 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
35 write_char(writer, QUOTE)?;
36 write!(writer, "{}", self.as_absolute_path_fmt())?;
37 write_char(writer, QUOTE)?;
38 Ok(())
39 }
40}
41
42impl AtermWriteable for &StorePath {
43 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
44 (&self.as_ref()).aterm_write(writer)
45 }
46}
47
48impl AtermWriteable for &String {
49 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
50 write_field(writer, self, true)
51 }
52}
53impl AtermWriteable for &str {
54 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
55 write_field(writer, self, true)
56 }
57}
58
59impl AtermWriteable for &[u8] {
60 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
61 write_field(writer, HEXLOWER.encode(self), false)
62 }
63}
64
65impl AtermWriteable for [u8] {
66 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
67 write_field(writer, HEXLOWER.encode(self), false)
68 }
69}
70
71impl AtermWriteable for Sha256 {
72 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
73 write_field(writer, format_smolstr!("{self:x}").as_bytes(), false)
74 }
75}
76
77impl AtermWriteable for &OutputName {
78 fn aterm_write(&self, writer: &mut impl io::Write) -> std::io::Result<()> {
79 write_field(writer, self.as_ref(), false)
80 }
81}
82
83impl Derivation {
84 pub(super) fn serialize_with_replacements<'a, K, I>(
91 &self,
92 writer: &mut impl std::io::Write,
93 input_derivations_sorted: I,
94 ) -> Result<(), io::Error>
95 where
96 I: Iterator<Item = (K, &'a BTreeSet<OutputName>)>,
97 K: AtermWriteable,
98 {
99 writer.write_all(DERIVATION_PREFIX.as_bytes())?;
100 write_char(writer, PAREN_OPEN)?;
101
102 write_outputs(writer, &self.outputs)?;
103 write_char(writer, COMMA)?;
104
105 write_input_derivations(writer, input_derivations_sorted)?;
106 write_char(writer, COMMA)?;
107
108 write_input_sources(writer, &self.input_sources)?;
109 write_char(writer, COMMA)?;
110
111 write_system(writer, &self.system)?;
112 write_char(writer, COMMA)?;
113
114 write_builder(writer, &self.builder)?;
115 write_char(writer, COMMA)?;
116
117 write_arguments(writer, &self.arguments)?;
118 write_char(writer, COMMA)?;
119
120 write_environment(writer, &self.environment)?;
121
122 write_char(writer, PAREN_CLOSE)?;
123
124 Ok(())
125 }
126}
127
128pub(crate) fn write_char(writer: &mut impl Write, c: char) -> io::Result<()> {
130 let mut buf = [0; 4];
131 let b = c.encode_utf8(&mut buf).as_bytes();
132 writer.write_all(b)
133}
134
135pub(crate) fn write_field<S: AsRef<[u8]>>(
140 writer: &mut impl Write,
141 s: S,
142 escape: bool,
143) -> io::Result<()> {
144 write_char(writer, QUOTE)?;
145
146 if !escape {
147 writer.write_all(s.as_ref())?;
148 } else {
149 write_escaped(s, writer)?;
150 }
151
152 write_char(writer, QUOTE)?;
153
154 Ok(())
155}
156
157fn write_array_elements<S>(
158 writer: &mut impl Write,
159 elements: impl IntoIterator<Item = S>,
160) -> Result<(), io::Error>
161where
162 S: AtermWriteable,
163{
164 for (index, element) in elements.into_iter().enumerate() {
165 if index > 0 {
166 write_char(writer, COMMA)?;
167 }
168
169 element.aterm_write(writer)?;
170 }
171
172 Ok(())
173}
174
175fn write_outputs<'i, I>(writer: &mut impl Write, outputs: I) -> Result<(), io::Error>
176where
177 I: IntoIterator<Item = (&'i OutputName, &'i Output)>,
178{
179 write_char(writer, BRACKET_OPEN)?;
180 for (ii, (output_name, output)) in outputs.into_iter().enumerate() {
181 if ii > 0 {
182 write_char(writer, COMMA)?;
183 }
184
185 write_char(writer, PAREN_OPEN)?;
186
187 let path_str = output
188 .path
189 .as_ref()
190 .map(|sp| sp.to_absolute_path())
191 .unwrap_or_default();
192
193 if let Some(output_hash) = &output.output_hash {
194 write_array_elements(
195 writer,
196 [
197 output_name.as_str(),
198 &path_str,
199 output_hash.as_mode_and_algo_str(),
200 &data_encoding::HEXLOWER.encode(output_hash.hash.digest_as_bytes()),
201 ],
202 )?;
203 } else {
204 write_array_elements(writer, [output_name.as_str(), &path_str, "", ""])?;
205 };
206
207 write_char(writer, PAREN_CLOSE)?;
208 }
209 write_char(writer, BRACKET_CLOSE)?;
210
211 Ok(())
212}
213
214fn write_input_derivations<'a, I, K>(
215 writer: &mut impl Write,
216 input_derivations_sorted: I,
217) -> Result<(), io::Error>
218where
219 I: Iterator<Item = (K, &'a BTreeSet<OutputName>)>,
220 K: AtermWriteable,
221{
222 write_char(writer, BRACKET_OPEN)?;
223
224 for (ii, (k, output_names)) in input_derivations_sorted.enumerate() {
225 if ii > 0 {
226 write_char(writer, COMMA)?;
227 }
228
229 write_char(writer, PAREN_OPEN)?;
230 k.aterm_write(writer)?;
231 write_char(writer, COMMA)?;
232
233 write_char(writer, BRACKET_OPEN)?;
234 write_array_elements(writer, output_names)?;
235 write_char(writer, BRACKET_CLOSE)?;
236
237 write_char(writer, PAREN_CLOSE)?;
238 }
239
240 write_char(writer, BRACKET_CLOSE)?;
241
242 Ok(())
243}
244
245fn write_input_sources(
246 writer: &mut impl Write,
247 input_sources: &BTreeSet<StorePath>,
248) -> Result<(), io::Error> {
249 write_char(writer, BRACKET_OPEN)?;
250 write_array_elements(writer, input_sources)?;
251 write_char(writer, BRACKET_CLOSE)?;
252
253 Ok(())
254}
255
256fn write_system(writer: &mut impl Write, platform: &str) -> Result<(), Error> {
257 write_field(writer, platform, true)?;
258 Ok(())
259}
260
261fn write_builder(writer: &mut impl Write, builder: &str) -> Result<(), Error> {
262 write_field(writer, builder, true)?;
263 Ok(())
264}
265
266fn write_arguments(writer: &mut impl Write, arguments: &[String]) -> Result<(), io::Error> {
267 write_char(writer, BRACKET_OPEN)?;
268 write_array_elements(writer, arguments)?;
269 write_char(writer, BRACKET_CLOSE)?;
270
271 Ok(())
272}
273
274fn write_environment<E, K, V>(writer: &mut impl Write, environment: E) -> Result<(), io::Error>
275where
276 E: IntoIterator<Item = (K, V)>,
277 K: AsRef<[u8]>,
278 V: AsRef<[u8]>,
279{
280 write_char(writer, BRACKET_OPEN)?;
281
282 for (i, (k, v)) in environment.into_iter().enumerate() {
283 if i > 0 {
284 write_char(writer, COMMA)?;
285 }
286
287 write_char(writer, PAREN_OPEN)?;
288 write_field(writer, k, false)?;
289 write_char(writer, COMMA)?;
290 write_field(writer, v, true)?;
291 write_char(writer, PAREN_CLOSE)?;
292 }
293
294 write_char(writer, BRACKET_CLOSE)?;
295
296 Ok(())
297}