feat(nix-compat/nar): add copy functions
This allows piping NAR data through a reader, and writing it back out to a writer. It can be used to validate a NAR to be syntactically correct, or to read exactly to the end of a NAR file if the size is not given externally. Change-Id: I0fc8d58e68783400d1cfee75c860138915974f3d Reviewed-on: https://cl.snix.dev/c/snix/+/30423 Tested-by: besadii Reviewed-by: edef <edef@edef.eu> Autosubmit: Florian Klink <flokli@flokli.de>
This commit is contained in:
parent
9caaa09765
commit
759f15390c
3 changed files with 127 additions and 0 deletions
58
snix/nix-compat/src/nar/copy.rs
Normal file
58
snix/nix-compat/src/nar/copy.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
use std::io;
|
||||
|
||||
use super::reader;
|
||||
use super::reader::Reader;
|
||||
use super::writer;
|
||||
|
||||
/// Reads through the entire NAR, and writes it back to a writer.
|
||||
/// This verifies its syntactical correctness.
|
||||
pub fn copy<W>(r: &mut Reader<'_>, w: &mut W) -> io::Result<()>
|
||||
where
|
||||
W: std::io::Write,
|
||||
{
|
||||
let node_r = reader::open(r)?;
|
||||
let node_w = writer::open(w)?;
|
||||
|
||||
copy_node(node_r, node_w)
|
||||
}
|
||||
|
||||
fn copy_node<W>(node_r: reader::Node<'_, '_>, node_w: writer::Node<'_, W>) -> io::Result<()>
|
||||
where
|
||||
W: std::io::Write,
|
||||
{
|
||||
match node_r {
|
||||
reader::Node::Symlink { target } => node_w.symlink(&target)?,
|
||||
reader::Node::File { executable, reader } => node_w.file(
|
||||
executable,
|
||||
reader.len(),
|
||||
&mut std::io::BufReader::new(reader),
|
||||
)?,
|
||||
reader::Node::Directory(mut dir_reader) => {
|
||||
let mut directory_w = node_w.directory()?;
|
||||
while let Some(entry) = dir_reader.next()? {
|
||||
let node_w = directory_w.entry(entry.name)?;
|
||||
copy_node(entry.node, node_w)?;
|
||||
}
|
||||
|
||||
directory_w.close()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rstest::rstest;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[rstest]
|
||||
fn roundtrip(#[files("src/nar/tests/*.nar")] path: PathBuf) {
|
||||
let nar_src = std::fs::read(path).expect("must succeed");
|
||||
|
||||
let mut out_buf = Vec::new();
|
||||
|
||||
assert!(super::copy(&mut std::io::Cursor::new(&nar_src), &mut out_buf).is_ok());
|
||||
assert_eq!(nar_src, out_buf, "must roundtrip");
|
||||
}
|
||||
}
|
||||
61
snix/nix-compat/src/nar/copy_async.rs
Normal file
61
snix/nix-compat/src/nar/copy_async.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
use std::io;
|
||||
|
||||
use tokio::io::{AsyncBufRead, AsyncWrite};
|
||||
|
||||
use super::reader::r#async as reader;
|
||||
use super::writer::r#async as writer;
|
||||
|
||||
/// Reads through the entire NAR, and writes it back to a writer.
|
||||
/// This verifies its syntactical correctness.
|
||||
pub async fn copy<R, W>(mut r: R, mut w: W) -> io::Result<()>
|
||||
where
|
||||
R: AsyncBufRead + Send + Unpin,
|
||||
W: AsyncWrite + Send + Unpin,
|
||||
{
|
||||
let node_r = reader::open(&mut r).await?;
|
||||
let node_w = writer::open(&mut w).await?;
|
||||
|
||||
copy_node(node_r, node_w).await
|
||||
}
|
||||
|
||||
async fn copy_node(node_r: reader::Node<'_, '_>, node_w: writer::Node<'_, '_>) -> io::Result<()> {
|
||||
match node_r {
|
||||
reader::Node::Symlink { target } => node_w.symlink(&target).await?,
|
||||
reader::Node::File {
|
||||
executable,
|
||||
mut reader,
|
||||
} => node_w.file(executable, reader.len(), &mut reader).await?,
|
||||
reader::Node::Directory(mut dir_reader) => {
|
||||
let mut directory_w = node_w.directory().await?;
|
||||
while let Some(entry) = dir_reader.next().await? {
|
||||
let node_w = directory_w.entry(entry.name).await?;
|
||||
Box::pin(copy_node(entry.node, node_w)).await?;
|
||||
}
|
||||
|
||||
directory_w.close().await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rstest::rstest;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn roundtrip(#[files("src/nar/tests/*.nar")] path: PathBuf) {
|
||||
let nar_src = std::fs::read(path).expect("must succeed");
|
||||
|
||||
let mut out_buf = Vec::new();
|
||||
|
||||
assert!(
|
||||
super::copy(&mut std::io::Cursor::new(&nar_src), &mut out_buf)
|
||||
.await
|
||||
.is_ok()
|
||||
);
|
||||
assert_eq!(nar_src, out_buf, "must roundtrip");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,13 @@
|
|||
pub(crate) mod wire;
|
||||
|
||||
mod copy;
|
||||
pub mod listing;
|
||||
pub mod reader;
|
||||
pub mod writer;
|
||||
|
||||
#[cfg(all(feature = "async", feature = "wire"))]
|
||||
mod copy_async;
|
||||
|
||||
pub use copy::copy;
|
||||
#[cfg(all(feature = "async", feature = "wire"))]
|
||||
pub use copy_async::copy as copy_async;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue