//! This module implements the serialisation of derivations into the //! [ATerm][] format used by C++ Nix. //! //! [ATerm]: http://program-transformation.org/Tools/ATermFormat.html use crate::derivation::escape::escape_bstr; use crate::derivation::output::Output; use bstr::BString; use std::collections::BTreeSet; use std::io::Cursor; use std::{collections::BTreeMap, io, io::Error, io::Write}; 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 = '"'; // 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(); io::copy(&mut Cursor::new(b), writer)?; Ok(()) } // Writes a string to the writer (as unicode) pub(crate) fn write_str(writer: &mut impl Write, s: &str) -> io::Result<()> { io::copy(&mut Cursor::new(s.as_bytes()), writer)?; Ok(()) } fn write_array_elements( writer: &mut impl Write, quote: bool, open: &str, closing: &str, elements: &[BString], ) -> Result<(), io::Error> { write_str(writer, open)?; for (index, element) in elements.iter().enumerate() { if index > 0 { write_char(writer, COMMA)?; } if quote { write_char(writer, QUOTE)?; } io::copy(&mut Cursor::new(element), writer)?; if quote { write_char(writer, QUOTE)?; } } write_str(writer, closing)?; Ok(()) } pub fn write_outputs( writer: &mut impl Write, outputs: &BTreeMap, ) -> Result<(), io::Error> { write_char(writer, BRACKET_OPEN)?; for (ii, (output_name, output)) in outputs.iter().enumerate() { if ii > 0 { write_char(writer, COMMA)?; } let mut elements: Vec = vec![ output_name.as_bytes().to_vec().into(), output.path.as_bytes().to_vec().into(), ]; let (e2, e3) = match &output.hash_with_mode { Some(hash) => match hash { crate::nixhash::NixHashWithMode::Flat(h) => ( h.algo.to_string().as_bytes().to_vec(), data_encoding::HEXLOWER.encode(&h.digest).as_bytes().into(), ), crate::nixhash::NixHashWithMode::Recursive(h) => ( format!("r:{}", h.algo).as_bytes().to_vec(), data_encoding::HEXLOWER.encode(&h.digest).as_bytes().into(), ), }, None => (vec![], vec![]), }; elements.push(e2.into()); elements.push(e3.into()); write_array_elements( writer, true, &PAREN_OPEN.to_string(), &PAREN_CLOSE.to_string(), &elements, )? } write_char(writer, BRACKET_CLOSE)?; Ok(()) } pub fn write_input_derivations( writer: &mut impl Write, input_derivations: &BTreeMap>, ) -> Result<(), io::Error> { write_char(writer, COMMA)?; write_char(writer, BRACKET_OPEN)?; for (ii, (input_derivation_path, input_derivation)) in input_derivations.into_iter().enumerate() { if ii > 0 { write_char(writer, COMMA)?; } write_char(writer, PAREN_OPEN)?; write_char(writer, QUOTE)?; write_str(writer, input_derivation_path.as_str())?; write_char(writer, QUOTE)?; write_char(writer, COMMA)?; write_array_elements( writer, true, &BRACKET_OPEN.to_string(), &BRACKET_CLOSE.to_string(), &input_derivation .iter() .map(|s| s.as_bytes().to_vec().into()) .collect::>(), )?; write_char(writer, PAREN_CLOSE)?; } write_char(writer, BRACKET_CLOSE)?; Ok(()) } pub fn write_input_sources( writer: &mut impl Write, input_sources: &BTreeSet, ) -> Result<(), io::Error> { write_char(writer, COMMA)?; write_array_elements( writer, true, &BRACKET_OPEN.to_string(), &BRACKET_CLOSE.to_string(), &input_sources .iter() .map(|s| s.as_bytes().to_vec().into()) .collect::>(), )?; Ok(()) } pub fn write_system(writer: &mut impl Write, platform: &str) -> Result<(), Error> { write_char(writer, COMMA)?; io::copy(&mut Cursor::new(escape_bstr(platform.as_bytes())), writer)?; Ok(()) } pub fn write_builder(writer: &mut impl Write, builder: &str) -> Result<(), Error> { write_char(writer, COMMA)?; io::copy(&mut Cursor::new(escape_bstr(builder.as_bytes())), writer)?; Ok(()) } pub fn write_arguments(writer: &mut impl Write, arguments: &[String]) -> Result<(), io::Error> { write_char(writer, COMMA)?; write_array_elements( writer, true, &BRACKET_OPEN.to_string(), &BRACKET_CLOSE.to_string(), &arguments .iter() .map(|s| s.as_bytes().to_vec().into()) .collect::>(), )?; Ok(()) } pub fn write_enviroment( writer: &mut impl Write, environment: &BTreeMap, ) -> Result<(), io::Error> { write_char(writer, COMMA)?; write_char(writer, BRACKET_OPEN)?; for (i, (k, v)) in environment.into_iter().enumerate() { if i > 0 { write_char(writer, COMMA)?; } write_array_elements( writer, false, &PAREN_OPEN.to_string(), &PAREN_CLOSE.to_string(), &[escape_bstr(k.as_bytes()), escape_bstr(v)], )?; } write_char(writer, BRACKET_CLOSE)?; Ok(()) }