1use crate::aterm::escape_bytes;
7use crate::derivation::{ca_kind_prefix, output::Output};
8use crate::nixbase32;
9use crate::store_path::{StorePath, STORE_DIR_WITH_SLASH};
10use bstr::BString;
11use data_encoding::HEXLOWER;
12
13use std::{
14 collections::{BTreeMap, BTreeSet},
15 io,
16 io::Error,
17 io::Write,
18};
19
20pub const DERIVATION_PREFIX: &str = "Derive";
21pub const PAREN_OPEN: char = '(';
22pub const PAREN_CLOSE: char = ')';
23pub const BRACKET_OPEN: char = '[';
24pub const BRACKET_CLOSE: char = ']';
25pub const COMMA: char = ',';
26pub const QUOTE: char = '"';
27
28pub(crate) trait AtermWriteable {
34 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()>;
35}
36
37impl<S> AtermWriteable for StorePath<S>
38where
39 S: AsRef<str>,
40{
41 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
42 write_char(writer, QUOTE)?;
43 writer.write_all(STORE_DIR_WITH_SLASH.as_bytes())?;
44 writer.write_all(nixbase32::encode(self.digest()).as_bytes())?;
45 write_char(writer, '-')?;
46 writer.write_all(self.name().as_ref().as_bytes())?;
47 write_char(writer, QUOTE)?;
48 Ok(())
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 [u8; 32] {
59 fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
60 write_field(writer, HEXLOWER.encode(self), false)
61 }
62}
63
64pub(crate) fn write_char(writer: &mut impl Write, c: char) -> io::Result<()> {
66 let mut buf = [0; 4];
67 let b = c.encode_utf8(&mut buf).as_bytes();
68 writer.write_all(b)
69}
70
71pub(crate) fn write_field<S: AsRef<[u8]>>(
76 writer: &mut impl Write,
77 s: S,
78 escape: bool,
79) -> io::Result<()> {
80 write_char(writer, QUOTE)?;
81
82 if !escape {
83 writer.write_all(s.as_ref())?;
84 } else {
85 writer.write_all(&escape_bytes(s.as_ref()))?;
86 }
87
88 write_char(writer, QUOTE)?;
89
90 Ok(())
91}
92
93fn write_array_elements<S: AsRef<[u8]>>(
94 writer: &mut impl Write,
95 elements: &[S],
96) -> Result<(), io::Error> {
97 for (index, element) in elements.iter().enumerate() {
98 if index > 0 {
99 write_char(writer, COMMA)?;
100 }
101
102 write_field(writer, element, true)?;
103 }
104
105 Ok(())
106}
107
108pub(crate) fn write_outputs(
109 writer: &mut impl Write,
110 outputs: &BTreeMap<String, Output>,
111) -> Result<(), io::Error> {
112 write_char(writer, BRACKET_OPEN)?;
113 for (ii, (output_name, output)) in outputs.iter().enumerate() {
114 if ii > 0 {
115 write_char(writer, COMMA)?;
116 }
117
118 write_char(writer, PAREN_OPEN)?;
119
120 let path_str = output.path_str();
121 let mut elements: Vec<&str> = vec![output_name, &path_str];
122
123 let (mode_and_algo, digest) = match &output.ca_hash {
124 Some(ca_hash) => (
125 format!("{}{}", ca_kind_prefix(ca_hash), ca_hash.hash().algo()),
126 data_encoding::HEXLOWER.encode(ca_hash.hash().digest_as_bytes()),
127 ),
128 None => ("".to_string(), "".to_string()),
129 };
130
131 elements.push(&mode_and_algo);
132 elements.push(&digest);
133
134 write_array_elements(writer, &elements)?;
135
136 write_char(writer, PAREN_CLOSE)?;
137 }
138 write_char(writer, BRACKET_CLOSE)?;
139
140 Ok(())
141}
142
143pub(crate) fn write_input_derivations(
144 writer: &mut impl Write,
145 input_derivations: &BTreeMap<impl AtermWriteable, BTreeSet<String>>,
146) -> Result<(), io::Error> {
147 write_char(writer, BRACKET_OPEN)?;
148
149 for (ii, (input_derivation_aterm, output_names)) in input_derivations.iter().enumerate() {
150 if ii > 0 {
151 write_char(writer, COMMA)?;
152 }
153
154 write_char(writer, PAREN_OPEN)?;
155 input_derivation_aterm.aterm_write(writer)?;
156 write_char(writer, COMMA)?;
157
158 write_char(writer, BRACKET_OPEN)?;
159 write_array_elements(
160 writer,
161 &output_names
162 .iter()
163 .map(String::as_bytes)
164 .collect::<Vec<_>>(),
165 )?;
166 write_char(writer, BRACKET_CLOSE)?;
167
168 write_char(writer, PAREN_CLOSE)?;
169 }
170
171 write_char(writer, BRACKET_CLOSE)?;
172
173 Ok(())
174}
175
176pub(crate) fn write_input_sources(
177 writer: &mut impl Write,
178 input_sources: &BTreeSet<StorePath<String>>,
179) -> Result<(), io::Error> {
180 write_char(writer, BRACKET_OPEN)?;
181 write_array_elements(
182 writer,
183 &input_sources
184 .iter()
185 .map(StorePath::to_absolute_path)
186 .collect::<Vec<_>>(),
187 )?;
188 write_char(writer, BRACKET_CLOSE)?;
189
190 Ok(())
191}
192
193pub(crate) fn write_system(writer: &mut impl Write, platform: &str) -> Result<(), Error> {
194 write_field(writer, platform, true)?;
195 Ok(())
196}
197
198pub(crate) fn write_builder(writer: &mut impl Write, builder: &str) -> Result<(), Error> {
199 write_field(writer, builder, true)?;
200 Ok(())
201}
202
203pub(crate) fn write_arguments(
204 writer: &mut impl Write,
205 arguments: &[String],
206) -> Result<(), io::Error> {
207 write_char(writer, BRACKET_OPEN)?;
208 write_array_elements(
209 writer,
210 &arguments
211 .iter()
212 .map(|s| s.as_bytes().to_vec().into())
213 .collect::<Vec<BString>>(),
214 )?;
215 write_char(writer, BRACKET_CLOSE)?;
216
217 Ok(())
218}
219
220pub(crate) fn write_environment<E, K, V>(
221 writer: &mut impl Write,
222 environment: E,
223) -> Result<(), io::Error>
224where
225 E: IntoIterator<Item = (K, V)>,
226 K: AsRef<[u8]>,
227 V: AsRef<[u8]>,
228{
229 write_char(writer, BRACKET_OPEN)?;
230
231 for (i, (k, v)) in environment.into_iter().enumerate() {
232 if i > 0 {
233 write_char(writer, COMMA)?;
234 }
235
236 write_char(writer, PAREN_OPEN)?;
237 write_field(writer, k, false)?;
238 write_char(writer, COMMA)?;
239 write_field(writer, v, true)?;
240 write_char(writer, PAREN_CLOSE)?;
241 }
242
243 write_char(writer, BRACKET_CLOSE)?;
244
245 Ok(())
246}