snix_store/nar/renderer/
simple.rs1use nix_compat::nar::writer::r#async as nar_writer;
2use snix_castore::{Node, blobservice::BlobService, directoryservice::DirectoryService};
3use tokio::io::{self, AsyncWrite, BufReader};
4
5use crate::nar::RenderError;
6
7pub async fn write_nar<W, BS, DS>(
16 mut w: W,
17 root_node: &Node,
18 blob_service: BS,
19 directory_service: DS,
20) -> Result<(), RenderError>
21where
22 W: AsyncWrite + Unpin + Send,
23 BS: BlobService,
24 DS: DirectoryService,
25{
26 let nar_root_node = nar_writer::open(&mut w)
28 .await
29 .map_err(RenderError::NARWriterError)?;
30
31 walk_node(
32 nar_root_node,
33 root_node,
34 b"",
35 blob_service,
36 directory_service,
37 )
38 .await?;
39
40 Ok(())
41}
42
43async fn walk_node<BS, DS>(
46 nar_node: nar_writer::Node<'_, '_>,
47 castore_node: &Node,
48 name: &[u8],
49 blob_service: BS,
50 directory_service: DS,
51) -> Result<(BS, DS), RenderError>
52where
53 BS: BlobService + Send,
54 DS: DirectoryService + Send,
55{
56 match castore_node {
57 Node::Symlink { target, .. } => {
58 nar_node
59 .symlink(target.as_ref())
60 .await
61 .map_err(RenderError::NARWriterError)?;
62 }
63 Node::File {
64 digest,
65 size,
66 executable,
67 } => {
68 let mut blob_reader = match blob_service
69 .open_read(digest)
70 .await
71 .map_err(RenderError::BlobService)?
72 {
73 Some(blob_reader) => Ok(BufReader::new(blob_reader)),
74 None => Err(RenderError::NARWriterError(io::Error::new(
75 io::ErrorKind::NotFound,
76 format!("blob with digest {} not found", &digest),
77 ))),
78 }?;
79
80 nar_node
81 .file(*executable, *size, &mut blob_reader)
82 .await
83 .map_err(|err| match err.kind() {
84 io::ErrorKind::UnexpectedEof => {
85 io::Error::new(io::ErrorKind::InvalidData, "blob short read")
86 }
87 io::ErrorKind::InvalidInput => io::Error::new(
88 io::ErrorKind::InvalidData,
89 "blob continued to yield data beyond end",
90 ),
91 _ => err,
92 })
93 .map_err(RenderError::NARWriterError)?;
94 }
95 Node::Directory { digest, .. } => {
96 let directory = directory_service
98 .get(digest)
99 .await
100 .map_err(RenderError::DirectoryService)?
101 .ok_or_else(|| {
102 RenderError::DirectoryNotFound(*digest, bytes::Bytes::copy_from_slice(name))
103 })?;
104
105 let mut nar_node_directory = nar_node
107 .directory()
108 .await
109 .map_err(RenderError::NARWriterError)?;
110
111 let mut blob_service = blob_service;
114 let mut directory_service = directory_service;
115
116 for (name, node) in directory.nodes() {
119 let child_node = nar_node_directory
120 .entry(name.as_ref())
121 .await
122 .map_err(RenderError::NARWriterError)?;
123
124 (blob_service, directory_service) = Box::pin(walk_node(
125 child_node,
126 node,
127 name.as_ref(),
128 blob_service,
129 directory_service,
130 ))
131 .await?;
132 }
133
134 nar_node_directory
136 .close()
137 .await
138 .map_err(RenderError::NARWriterError)?;
139
140 return Ok((blob_service, directory_service));
141 }
142 }
143
144 Ok((blob_service, directory_service))
145}