1use crate::utils::AsyncIoBridge;
2
3use super::{NarCalculationService, RenderError};
4use count_write::CountWrite;
5use nix_compat::nar::writer::r#async as nar_writer;
6use sha2::{Digest, Sha256};
7use snix_castore::{Node, blobservice::BlobService, directoryservice::DirectoryService};
8use tokio::io::{self, AsyncWrite, BufReader};
9use tonic::async_trait;
10use tracing::instrument;
11
12pub struct SimpleRenderer<BS, DS> {
13 blob_service: BS,
14 directory_service: DS,
15}
16
17impl<BS, DS> SimpleRenderer<BS, DS> {
18 pub fn new(blob_service: BS, directory_service: DS) -> Self {
19 Self {
20 blob_service,
21 directory_service,
22 }
23 }
24}
25
26#[async_trait]
27impl<BS, DS> NarCalculationService for SimpleRenderer<BS, DS>
28where
29 BS: BlobService + Clone,
30 DS: DirectoryService + Clone,
31{
32 async fn calculate_nar(
33 &self,
34 root_node: &Node,
35 ) -> Result<(u64, [u8; 32]), snix_castore::Error> {
36 calculate_size_and_sha256(
37 root_node,
38 self.blob_service.clone(),
39 self.directory_service.clone(),
40 )
41 .await
42 .map_err(|e| snix_castore::Error::StorageError(format!("failed rendering nar: {}", e)))
43 }
44}
45
46#[instrument(skip_all)]
49pub async fn calculate_size_and_sha256<BS, DS>(
50 root_node: &Node,
51 blob_service: BS,
52 directory_service: DS,
53) -> Result<(u64, [u8; 32]), RenderError>
54where
55 BS: BlobService + Send,
56 DS: DirectoryService + Send,
57{
58 let mut h = Sha256::new();
59 let mut cw = CountWrite::from(&mut h);
60
61 write_nar(
62 AsyncIoBridge(&mut cw),
65 root_node,
66 blob_service,
67 directory_service,
68 )
69 .await?;
70
71 Ok((cw.count(), h.finalize().into()))
72}
73
74pub async fn write_nar<W, BS, DS>(
79 mut w: W,
80 root_node: &Node,
81 blob_service: BS,
82 directory_service: DS,
83) -> Result<(), RenderError>
84where
85 W: AsyncWrite + Unpin + Send,
86 BS: BlobService + Send,
87 DS: DirectoryService + Send,
88{
89 let nar_root_node = nar_writer::open(&mut w)
91 .await
92 .map_err(RenderError::NARWriterError)?;
93
94 walk_node(
95 nar_root_node,
96 root_node,
97 b"",
98 blob_service,
99 directory_service,
100 )
101 .await?;
102
103 Ok(())
104}
105
106async fn walk_node<BS, DS>(
109 nar_node: nar_writer::Node<'_, '_>,
110 castore_node: &Node,
111 name: &[u8],
112 blob_service: BS,
113 directory_service: DS,
114) -> Result<(BS, DS), RenderError>
115where
116 BS: BlobService + Send,
117 DS: DirectoryService + Send,
118{
119 match castore_node {
120 Node::Symlink { target, .. } => {
121 nar_node
122 .symlink(target.as_ref())
123 .await
124 .map_err(RenderError::NARWriterError)?;
125 }
126 Node::File {
127 digest,
128 size,
129 executable,
130 } => {
131 let mut blob_reader = match blob_service
132 .open_read(digest)
133 .await
134 .map_err(RenderError::StoreError)?
135 {
136 Some(blob_reader) => Ok(BufReader::new(blob_reader)),
137 None => Err(RenderError::NARWriterError(io::Error::new(
138 io::ErrorKind::NotFound,
139 format!("blob with digest {} not found", &digest),
140 ))),
141 }?;
142
143 nar_node
144 .file(*executable, *size, &mut blob_reader)
145 .await
146 .map_err(RenderError::NARWriterError)?;
147 }
148 Node::Directory { digest, .. } => {
149 match directory_service
151 .get(digest)
152 .await
153 .map_err(|e| RenderError::StoreError(e.into()))?
154 {
155 None => Err(RenderError::DirectoryNotFound(
157 digest.clone(),
158 bytes::Bytes::copy_from_slice(name),
159 ))?,
160 Some(directory) => {
161 let mut nar_node_directory = nar_node
163 .directory()
164 .await
165 .map_err(RenderError::NARWriterError)?;
166
167 let mut blob_service = blob_service;
170 let mut directory_service = directory_service;
171
172 for (name, node) in directory.nodes() {
175 let child_node = nar_node_directory
176 .entry(name.as_ref())
177 .await
178 .map_err(RenderError::NARWriterError)?;
179
180 (blob_service, directory_service) = Box::pin(walk_node(
181 child_node,
182 node,
183 name.as_ref(),
184 blob_service,
185 directory_service,
186 ))
187 .await?;
188 }
189
190 nar_node_directory
192 .close()
193 .await
194 .map_err(RenderError::NARWriterError)?;
195
196 return Ok((blob_service, directory_service));
197 }
198 }
199 }
200 }
201
202 Ok((blob_service, directory_service))
203}