about summary refs log tree commit diff
path: root/tvix/derivation/src/lib.rs
diff options
context:
space:
mode:
authorJürgen Hahn <juergen_hahn@gmx.net>2022-12-28T09·28+0100
committerjrhahn <mail.jhahn@gmail.com>2022-12-29T14·38+0000
commitbb185b2c6eb8c4e035cddb61be772169df1b0139 (patch)
tree0598b8a1e26d98274c93f52d01b53291e59adb91 /tvix/derivation/src/lib.rs
parent42fe3941c2a02b4b1eb55dad4f83941a2708f77e (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.rs216
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 0000000000..a0ddee410b
--- /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(());
+}