2023-01-23 13:19:27 +01:00
|
|
|
//! This module implements the serialisation of derivations into the
|
|
|
|
//! [ATerm][] format used by C++ Nix.
|
|
|
|
//!
|
|
|
|
//! [ATerm]: http://program-transformation.org/Tools/ATermFormat.html
|
|
|
|
|
2023-07-31 15:46:39 +02:00
|
|
|
use crate::aterm::escape_bytes;
|
2023-10-18 12:39:36 +02:00
|
|
|
use crate::derivation::{ca_kind_prefix, output::Output};
|
2024-02-21 12:16:36 +01:00
|
|
|
use crate::nixbase32;
|
|
|
|
use crate::store_path::{StorePath, StorePathRef, STORE_DIR_WITH_SLASH};
|
2023-07-29 21:14:44 +02:00
|
|
|
use bstr::BString;
|
2024-03-14 12:50:56 +01:00
|
|
|
use data_encoding::HEXLOWER;
|
|
|
|
|
2023-07-31 23:12:31 +02:00
|
|
|
use std::{
|
|
|
|
collections::{BTreeMap, BTreeSet},
|
|
|
|
io,
|
|
|
|
io::Error,
|
|
|
|
io::Write,
|
|
|
|
};
|
2023-01-02 21:00:59 +01:00
|
|
|
|
|
|
|
pub const DERIVATION_PREFIX: &str = "Derive";
|
|
|
|
pub const PAREN_OPEN: char = '(';
|
|
|
|
pub const PAREN_CLOSE: char = ')';
|
|
|
|
pub const BRACKET_OPEN: char = '[';
|
|
|
|
pub const BRACKET_CLOSE: char = ']';
|
|
|
|
pub const COMMA: char = ',';
|
|
|
|
pub const QUOTE: char = '"';
|
|
|
|
|
2024-02-21 12:16:36 +01:00
|
|
|
/// Something that can be written as ATerm.
|
|
|
|
///
|
|
|
|
/// Note that we mostly use explicit `write_*` calls
|
|
|
|
/// instead since the serialization of the items depends on
|
|
|
|
/// the context a lot.
|
2024-03-14 12:50:56 +01:00
|
|
|
pub(crate) trait AtermWriteable {
|
2024-02-21 12:16:36 +01:00
|
|
|
fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()>;
|
2024-02-21 08:07:37 +01:00
|
|
|
|
|
|
|
fn aterm_bytes(&self) -> Vec<u8> {
|
|
|
|
let mut bytes = Vec::new();
|
|
|
|
self.aterm_write(&mut bytes)
|
|
|
|
.expect("unexpected write errors to Vec");
|
|
|
|
bytes
|
|
|
|
}
|
2024-02-21 12:16:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl AtermWriteable for StorePathRef<'_> {
|
|
|
|
fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
|
|
|
|
write_char(writer, QUOTE)?;
|
|
|
|
writer.write_all(STORE_DIR_WITH_SLASH.as_bytes())?;
|
|
|
|
writer.write_all(nixbase32::encode(self.digest()).as_bytes())?;
|
|
|
|
write_char(writer, '-')?;
|
|
|
|
writer.write_all(self.name().as_bytes())?;
|
|
|
|
write_char(writer, QUOTE)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AtermWriteable for StorePath {
|
|
|
|
fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
|
|
|
|
let r: StorePathRef = self.into();
|
|
|
|
r.aterm_write(writer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AtermWriteable for String {
|
|
|
|
fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
|
|
|
|
write_field(writer, self, true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-14 12:50:56 +01:00
|
|
|
impl AtermWriteable for [u8; 32] {
|
2024-02-21 12:16:36 +01:00
|
|
|
fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
|
2024-03-14 12:50:56 +01:00
|
|
|
write_field(writer, HEXLOWER.encode(self), false)
|
2024-02-21 12:16:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-29 21:14:44 +02:00
|
|
|
// Writes a character to the writer.
|
|
|
|
pub(crate) fn write_char(writer: &mut impl Write, c: char) -> io::Result<()> {
|
|
|
|
let mut buf = [0; 4];
|
|
|
|
let b = c.encode_utf8(&mut buf).as_bytes();
|
2023-07-31 23:12:31 +02:00
|
|
|
writer.write_all(b)
|
2023-07-29 21:14:44 +02:00
|
|
|
}
|
|
|
|
|
2023-07-30 09:05:28 +02:00
|
|
|
// Write a string `s` as a quoted field to the writer.
|
|
|
|
// The `escape` argument controls whether escaping will be skipped.
|
|
|
|
// This is the case if `s` is known to only contain characters that need no
|
|
|
|
// escaping.
|
|
|
|
pub(crate) fn write_field<S: AsRef<[u8]>>(
|
|
|
|
writer: &mut impl Write,
|
|
|
|
s: S,
|
|
|
|
escape: bool,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
write_char(writer, QUOTE)?;
|
|
|
|
|
|
|
|
if !escape {
|
2023-07-31 23:12:31 +02:00
|
|
|
writer.write_all(s.as_ref())?;
|
2023-07-30 09:05:28 +02:00
|
|
|
} else {
|
2023-08-01 12:21:40 +02:00
|
|
|
writer.write_all(&escape_bytes(s.as_ref()))?;
|
2023-07-30 09:05:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
write_char(writer, QUOTE)?;
|
|
|
|
|
2023-07-29 21:14:44 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-07-30 09:35:34 +02:00
|
|
|
fn write_array_elements<S: AsRef<[u8]>>(
|
|
|
|
writer: &mut impl Write,
|
|
|
|
elements: &[S],
|
|
|
|
) -> Result<(), io::Error> {
|
2023-01-02 21:00:59 +01:00
|
|
|
for (index, element) in elements.iter().enumerate() {
|
|
|
|
if index > 0 {
|
2023-07-29 21:14:44 +02:00
|
|
|
write_char(writer, COMMA)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
}
|
|
|
|
|
2023-07-30 09:05:28 +02:00
|
|
|
write_field(writer, element, true)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-02-19 13:19:39 +01:00
|
|
|
pub(crate) fn write_outputs(
|
2023-01-02 21:00:59 +01:00
|
|
|
writer: &mut impl Write,
|
2023-01-04 12:26:37 +01:00
|
|
|
outputs: &BTreeMap<String, Output>,
|
2023-07-29 21:14:44 +02:00
|
|
|
) -> Result<(), io::Error> {
|
|
|
|
write_char(writer, BRACKET_OPEN)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
for (ii, (output_name, output)) in outputs.iter().enumerate() {
|
|
|
|
if ii > 0 {
|
2023-07-29 21:14:44 +02:00
|
|
|
write_char(writer, COMMA)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
}
|
|
|
|
|
2023-07-30 09:35:34 +02:00
|
|
|
write_char(writer, PAREN_OPEN)?;
|
|
|
|
|
2024-02-21 12:31:35 +01:00
|
|
|
let path_str = output.path_str();
|
|
|
|
let mut elements: Vec<&str> = vec![output_name, &path_str];
|
2023-01-04 13:36:27 +01:00
|
|
|
|
2023-10-18 12:39:36 +02:00
|
|
|
let (mode_and_algo, digest) = match &output.ca_hash {
|
|
|
|
Some(ca_hash) => (
|
2024-02-29 09:31:14 +01:00
|
|
|
format!("{}{}", ca_kind_prefix(ca_hash), ca_hash.hash().algo()),
|
|
|
|
data_encoding::HEXLOWER.encode(ca_hash.hash().digest_as_bytes()),
|
2023-10-14 18:48:16 +02:00
|
|
|
),
|
2023-07-30 09:35:34 +02:00
|
|
|
None => ("".to_string(), "".to_string()),
|
2023-03-14 17:16:05 +01:00
|
|
|
};
|
|
|
|
|
2023-10-14 18:48:16 +02:00
|
|
|
elements.push(&mode_and_algo);
|
|
|
|
elements.push(&digest);
|
2023-07-30 09:05:28 +02:00
|
|
|
|
|
|
|
write_array_elements(writer, &elements)?;
|
|
|
|
|
|
|
|
write_char(writer, PAREN_CLOSE)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
}
|
2023-07-29 21:14:44 +02:00
|
|
|
write_char(writer, BRACKET_CLOSE)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-02-19 13:19:39 +01:00
|
|
|
pub(crate) fn write_input_derivations(
|
2023-01-02 21:00:59 +01:00
|
|
|
writer: &mut impl Write,
|
2024-02-21 12:24:50 +01:00
|
|
|
input_derivations: &BTreeMap<impl AtermWriteable, BTreeSet<String>>,
|
2023-07-29 21:14:44 +02:00
|
|
|
) -> Result<(), io::Error> {
|
|
|
|
write_char(writer, BRACKET_OPEN)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
|
2024-02-21 12:16:36 +01:00
|
|
|
for (ii, (input_derivation_aterm, output_names)) in input_derivations.iter().enumerate() {
|
2023-01-02 21:00:59 +01:00
|
|
|
if ii > 0 {
|
2023-07-29 21:14:44 +02:00
|
|
|
write_char(writer, COMMA)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
}
|
|
|
|
|
2023-07-29 21:14:44 +02:00
|
|
|
write_char(writer, PAREN_OPEN)?;
|
2024-02-21 12:24:50 +01:00
|
|
|
input_derivation_aterm.aterm_write(writer)?;
|
2023-07-29 21:14:44 +02:00
|
|
|
write_char(writer, COMMA)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
|
2023-07-30 09:05:28 +02:00
|
|
|
write_char(writer, BRACKET_OPEN)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
write_array_elements(
|
|
|
|
writer,
|
2023-12-09 11:02:36 +01:00
|
|
|
&output_names
|
2023-07-29 21:14:44 +02:00
|
|
|
.iter()
|
2023-07-31 23:19:15 +02:00
|
|
|
.map(String::as_bytes)
|
|
|
|
.collect::<Vec<_>>(),
|
2023-01-02 21:00:59 +01:00
|
|
|
)?;
|
2023-07-30 09:05:28 +02:00
|
|
|
write_char(writer, BRACKET_CLOSE)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
|
2023-07-29 21:14:44 +02:00
|
|
|
write_char(writer, PAREN_CLOSE)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
}
|
|
|
|
|
2023-07-29 21:14:44 +02:00
|
|
|
write_char(writer, BRACKET_CLOSE)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-02-19 13:19:39 +01:00
|
|
|
pub(crate) fn write_input_sources(
|
2023-01-02 21:00:59 +01:00
|
|
|
writer: &mut impl Write,
|
2024-02-21 08:07:37 +01:00
|
|
|
input_sources: &BTreeSet<StorePath>,
|
2023-07-29 21:14:44 +02:00
|
|
|
) -> Result<(), io::Error> {
|
2023-07-30 09:05:28 +02:00
|
|
|
write_char(writer, BRACKET_OPEN)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
write_array_elements(
|
|
|
|
writer,
|
2024-02-21 08:07:37 +01:00
|
|
|
&input_sources
|
|
|
|
.iter()
|
|
|
|
.map(StorePath::to_absolute_path)
|
|
|
|
.collect::<Vec<_>>(),
|
2023-01-02 21:00:59 +01:00
|
|
|
)?;
|
2023-07-30 09:05:28 +02:00
|
|
|
write_char(writer, BRACKET_CLOSE)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-02-19 13:19:39 +01:00
|
|
|
pub(crate) fn write_system(writer: &mut impl Write, platform: &str) -> Result<(), Error> {
|
2023-07-30 09:05:28 +02:00
|
|
|
write_field(writer, platform, true)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-02-19 13:19:39 +01:00
|
|
|
pub(crate) fn write_builder(writer: &mut impl Write, builder: &str) -> Result<(), Error> {
|
2023-07-30 09:05:28 +02:00
|
|
|
write_field(writer, builder, true)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
Ok(())
|
|
|
|
}
|
2023-07-30 09:05:28 +02:00
|
|
|
|
2024-02-19 13:19:39 +01:00
|
|
|
pub(crate) fn write_arguments(
|
|
|
|
writer: &mut impl Write,
|
|
|
|
arguments: &[String],
|
|
|
|
) -> Result<(), io::Error> {
|
2023-07-30 09:05:28 +02:00
|
|
|
write_char(writer, BRACKET_OPEN)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
write_array_elements(
|
|
|
|
writer,
|
2023-07-29 21:14:44 +02:00
|
|
|
&arguments
|
|
|
|
.iter()
|
|
|
|
.map(|s| s.as_bytes().to_vec().into())
|
|
|
|
.collect::<Vec<BString>>(),
|
2023-01-02 21:00:59 +01:00
|
|
|
)?;
|
2023-07-30 09:05:28 +02:00
|
|
|
write_char(writer, BRACKET_CLOSE)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-02-19 13:19:39 +01:00
|
|
|
pub(crate) fn write_environment<E, K, V>(
|
|
|
|
writer: &mut impl Write,
|
|
|
|
environment: E,
|
|
|
|
) -> Result<(), io::Error>
|
2023-10-15 10:07:21 +02:00
|
|
|
where
|
|
|
|
E: IntoIterator<Item = (K, V)>,
|
|
|
|
K: AsRef<[u8]>,
|
|
|
|
V: AsRef<[u8]>,
|
|
|
|
{
|
2023-07-29 21:14:44 +02:00
|
|
|
write_char(writer, BRACKET_OPEN)?;
|
|
|
|
|
2023-10-15 10:07:21 +02:00
|
|
|
for (i, (k, v)) in environment.into_iter().enumerate() {
|
2023-07-29 21:14:44 +02:00
|
|
|
if i > 0 {
|
|
|
|
write_char(writer, COMMA)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
}
|
|
|
|
|
2023-07-30 09:05:28 +02:00
|
|
|
write_char(writer, PAREN_OPEN)?;
|
|
|
|
write_field(writer, k, false)?;
|
|
|
|
write_char(writer, COMMA)?;
|
|
|
|
write_field(writer, v, true)?;
|
|
|
|
write_char(writer, PAREN_CLOSE)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
}
|
|
|
|
|
2023-07-29 21:14:44 +02:00
|
|
|
write_char(writer, BRACKET_CLOSE)?;
|
2023-01-02 21:00:59 +01:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|