nix_compat/nar/
copy_async.rs

1use std::io;
2
3use tokio::io::{AsyncBufRead, AsyncWrite};
4
5use super::reader::r#async as reader;
6use super::writer::r#async as writer;
7
8/// Reads through the entire NAR, and writes it back to a writer.
9/// This verifies its syntactical correctness.
10pub async fn copy<R, W>(mut r: R, mut w: W) -> io::Result<()>
11where
12    R: AsyncBufRead + Send + Unpin,
13    W: AsyncWrite + Send + Unpin,
14{
15    let node_r = reader::open(&mut r).await?;
16    let node_w = writer::open(&mut w).await?;
17
18    copy_node(node_r, node_w).await
19}
20
21async fn copy_node(node_r: reader::Node<'_, '_>, node_w: writer::Node<'_, '_>) -> io::Result<()> {
22    match node_r {
23        reader::Node::Symlink { target } => node_w.symlink(&target).await?,
24        reader::Node::File {
25            executable,
26            mut reader,
27        } => node_w.file(executable, reader.len(), &mut reader).await?,
28        reader::Node::Directory(mut dir_reader) => {
29            let mut directory_w = node_w.directory().await?;
30            while let Some(entry) = dir_reader.next().await? {
31                let node_w = directory_w.entry(entry.name).await?;
32                Box::pin(copy_node(entry.node, node_w)).await?;
33            }
34
35            directory_w.close().await?;
36        }
37    }
38
39    Ok(())
40}
41
42#[cfg(test)]
43mod tests {
44    use rstest::rstest;
45    use std::path::PathBuf;
46
47    #[rstest]
48    #[tokio::test]
49    async fn roundtrip(#[files("src/nar/tests/*.nar")] path: PathBuf) {
50        let nar_src = std::fs::read(path).expect("must succeed");
51
52        let mut out_buf = Vec::new();
53
54        assert!(
55            super::copy(&mut std::io::Cursor::new(&nar_src), &mut out_buf)
56                .await
57                .is_ok()
58        );
59        assert_eq!(nar_src, out_buf, "must roundtrip");
60    }
61}