This adds a Derivation structure and allows to write it to a structure that implements std::fmt:Write. The implementation is based on the go-nix version. Change-Id: Ib54e1202b5c67f5d206b21bc109a751e971064cf Reviewed-on: https://cl.tvl.fyi/c/depot/+/7659 Reviewed-by: flokli <flokli@flokli.de> Reviewed-by: tazjin <tazjin@tvl.su> Tested-by: BuildkiteCI
216 lines
5.2 KiB
Rust
216 lines
5.2 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
use std::{collections::BTreeMap, fmt, fmt::Write};
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|
|
|
|
const DERIVATION_PREFIX: &str = "Derive";
|
|
const PAREN_OPEN: char = '(';
|
|
const PAREN_CLOSE: char = ')';
|
|
const BRACKET_OPEN: char = '[';
|
|
const BRACKET_CLOSE: char = ']';
|
|
const COMMA: char = ',';
|
|
const QUOTE: char = '"';
|
|
|
|
const STRING_ESCAPER: [(char, &str); 5] = [
|
|
('\\', "\\\\"),
|
|
('\n', "\\n"),
|
|
('\r', "\\r"),
|
|
('\t', "\\t"),
|
|
('\"', "\\\""),
|
|
];
|
|
|
|
fn default_resource() -> String {
|
|
"".to_string()
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
pub struct Output {
|
|
path: String,
|
|
#[serde(default = "default_resource")]
|
|
hash_algorithm: String,
|
|
#[serde(default = "default_resource")]
|
|
hash: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
pub struct Derivation {
|
|
outputs: BTreeMap<String, Output>,
|
|
input_sources: Vec<String>,
|
|
input_derivations: BTreeMap<String, Vec<String>>,
|
|
platform: String,
|
|
builder: String,
|
|
arguments: Vec<String>,
|
|
environment: BTreeMap<String, String>,
|
|
}
|
|
|
|
fn escape_string(s: &String) -> String {
|
|
let mut s_replaced = s.clone();
|
|
|
|
for escape_sequence in STRING_ESCAPER {
|
|
s_replaced = s_replaced.replace(escape_sequence.0, escape_sequence.1);
|
|
}
|
|
|
|
return format!("\"{}\"", s_replaced);
|
|
}
|
|
|
|
fn write_array_elements(
|
|
writer: &mut impl Write,
|
|
quote: bool,
|
|
open: &str,
|
|
closing: &str,
|
|
elements: Vec<&String>,
|
|
) -> Result<(), fmt::Error> {
|
|
writer.write_str(open)?;
|
|
|
|
for (index, element) in elements.iter().enumerate() {
|
|
if index > 0 {
|
|
writer.write_char(COMMA)?;
|
|
}
|
|
|
|
if quote {
|
|
writer.write_char(QUOTE)?;
|
|
}
|
|
|
|
writer.write_str(element)?;
|
|
|
|
if quote {
|
|
writer.write_char(QUOTE)?;
|
|
}
|
|
}
|
|
|
|
writer.write_str(closing)?;
|
|
|
|
return Ok(());
|
|
}
|
|
|
|
pub fn serialize_derivation(
|
|
derivation: Derivation,
|
|
writer: &mut impl Write,
|
|
) -> Result<(), fmt::Error> {
|
|
writer.write_str(DERIVATION_PREFIX)?;
|
|
writer.write_char(PAREN_OPEN)?;
|
|
|
|
// Step 1: Write outputs
|
|
{
|
|
writer.write_char(BRACKET_OPEN)?;
|
|
for (ii, (output_name, output)) in derivation.outputs.iter().enumerate() {
|
|
if ii > 0 {
|
|
writer.write_char(COMMA)?;
|
|
}
|
|
|
|
// TODO(jrhahn) option to strip output
|
|
let elements = vec![
|
|
output_name,
|
|
&output.path,
|
|
&output.hash_algorithm,
|
|
&output.hash,
|
|
];
|
|
|
|
write_array_elements(
|
|
writer,
|
|
true,
|
|
&PAREN_OPEN.to_string(),
|
|
&PAREN_CLOSE.to_string(),
|
|
elements,
|
|
)?
|
|
}
|
|
writer.write_char(BRACKET_CLOSE)?;
|
|
}
|
|
|
|
// Step 2: Write input_derivations
|
|
{
|
|
writer.write_char(COMMA)?;
|
|
writer.write_char(BRACKET_OPEN)?;
|
|
|
|
for (ii, (input_derivation_path, input_derivation)) in
|
|
derivation.input_derivations.iter().enumerate()
|
|
{
|
|
if ii > 0 {
|
|
writer.write_char(COMMA)?;
|
|
}
|
|
|
|
writer.write_char(PAREN_OPEN)?;
|
|
writer.write_char(QUOTE)?;
|
|
writer.write_str(input_derivation_path.as_str())?;
|
|
writer.write_char(QUOTE)?;
|
|
writer.write_char(COMMA)?;
|
|
|
|
write_array_elements(
|
|
writer,
|
|
true,
|
|
&BRACKET_OPEN.to_string(),
|
|
&BRACKET_CLOSE.to_string(),
|
|
input_derivation.iter().map(|s| s).collect(),
|
|
)?;
|
|
|
|
writer.write_char(PAREN_CLOSE)?;
|
|
}
|
|
|
|
writer.write_char(BRACKET_CLOSE)?;
|
|
}
|
|
|
|
// Step 3: Write input_sources
|
|
{
|
|
writer.write_char(COMMA)?;
|
|
write_array_elements(
|
|
writer,
|
|
true,
|
|
&BRACKET_OPEN.to_string(),
|
|
&BRACKET_CLOSE.to_string(),
|
|
derivation.input_sources.iter().map(|s| s).collect(),
|
|
)?;
|
|
}
|
|
|
|
// Step 4: Write platform
|
|
{
|
|
writer.write_char(COMMA)?;
|
|
writer.write_str(&escape_string(&derivation.platform).as_str())?;
|
|
}
|
|
|
|
// Step 5: Write builder
|
|
{
|
|
writer.write_char(COMMA)?;
|
|
writer.write_str(&escape_string(&derivation.builder).as_str())?;
|
|
}
|
|
|
|
// Step 6: Write arguments
|
|
{
|
|
writer.write_char(COMMA)?;
|
|
write_array_elements(
|
|
writer,
|
|
true,
|
|
&BRACKET_OPEN.to_string(),
|
|
&BRACKET_CLOSE.to_string(),
|
|
derivation.arguments.iter().map(|s| s).collect(),
|
|
)?;
|
|
}
|
|
|
|
// Step 7: Write env
|
|
{
|
|
writer.write_char(COMMA)?;
|
|
writer.write_char(BRACKET_OPEN)?;
|
|
|
|
for (ii, (key, environment)) in derivation.environment.iter().enumerate() {
|
|
if ii > 0 {
|
|
writer.write_char(COMMA)?;
|
|
}
|
|
|
|
// TODO(jrhahn) add strip option
|
|
write_array_elements(
|
|
writer,
|
|
false,
|
|
&PAREN_OPEN.to_string(),
|
|
&PAREN_CLOSE.to_string(),
|
|
vec![&escape_string(key), &escape_string(&environment)],
|
|
)?;
|
|
}
|
|
|
|
writer.write_char(BRACKET_CLOSE)?;
|
|
}
|
|
|
|
// Step 8: Close Derive call
|
|
writer.write_char(PAREN_CLOSE)?;
|
|
|
|
return Ok(());
|
|
}
|