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