about summary refs log tree commit diff
path: root/tvix/store/src/nar.rs
blob: efcaf652e1389da2d6ef957b99dd607f15827df3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//! This provides some common "client-side" libraries to interact with a tvix-
//! store, in this case to render NAR.
use crate::{
    client::StoreClient,
    proto::{self, NamedNode},
};
use anyhow::Result;
use nix_compat::nar;

/// Consumes a [proto::node::Node] pointing to the root of a (store) path,
/// and writes the contents in NAR serialization to the passed
/// [std::io::Write].
///
/// It uses a [StoreClient] to do the necessary lookups as it traverses the
/// structure.
pub fn write_nar<W: std::io::Write, SC: StoreClient>(
    w: &mut W,
    proto_root_node: proto::node::Node,
    store_client: &mut SC,
) -> Result<()> {
    // Initialize NAR writer
    let nar_root_node = nar::writer::open(w)?;

    walk_node(nar_root_node, proto_root_node, store_client)
}

/// Process an intermediate node in the structure.
/// This consumes the node.
fn walk_node<SC: StoreClient>(
    nar_node: nar::writer::Node,
    proto_node: proto::node::Node,
    store_client: &mut SC,
) -> Result<()> {
    match proto_node {
        proto::node::Node::Symlink(proto_symlink_node) => {
            nar_node.symlink(&proto_symlink_node.target)?;
        }
        proto::node::Node::File(proto_file_node) => {
            nar_node.file(
                proto_file_node.executable,
                proto_file_node.size.into(),
                &mut store_client.open_blob(proto_file_node.digest)?,
            )?;
        }
        proto::node::Node::Directory(proto_directory_node) => {
            // look up that node from the store client
            let proto_directory = store_client.get_directory(proto_directory_node.digest)?;

            // if it's None, that's an error!
            if proto_directory.is_none() {
                // TODO: proper error handling
                panic!("not found!")
            }

            // start a directory node
            let mut nar_node_directory = nar_node.directory()?;

            // for each node in the directory, create a new entry with its name,
            // and then invoke walk_node on that entry.
            for proto_node in proto_directory.unwrap().nodes() {
                let child_node = nar_node_directory.entry(proto_node.get_name())?;
                walk_node(child_node, proto_node, store_client)?;
            }

            // close the directory
            nar_node_directory.close()?;
        }
    }
    Ok(())
}