diff options
author | Jürgen Hahn <juergen_hahn@gmx.net> | 2022-12-28T09·28+0100 |
---|---|---|
committer | jrhahn <mail.jhahn@gmail.com> | 2022-12-29T14·38+0000 |
commit | bb185b2c6eb8c4e035cddb61be772169df1b0139 (patch) | |
tree | 0598b8a1e26d98274c93f52d01b53291e59adb91 /tvix/derivation/src/lib.rs | |
parent | 42fe3941c2a02b4b1eb55dad4f83941a2708f77e (diff) |
feat(tvix/derivation): serialize Nix Derivation r/5536
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
Diffstat (limited to 'tvix/derivation/src/lib.rs')
-rw-r--r-- | tvix/derivation/src/lib.rs | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/tvix/derivation/src/lib.rs b/tvix/derivation/src/lib.rs new file mode 100644 index 000000000000..a0ddee410b80 --- /dev/null +++ b/tvix/derivation/src/lib.rs @@ -0,0 +1,216 @@ +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(()); +} |