about summary refs log blame commit diff
path: root/tvix/nix-compat/src/nar/listing/mod.rs
blob: c1119a0199f4bdee07c6b19a8e980239c6bca400 (plain) (tree)
































































                                                                                                      
//! Parser for the Nix archive listing format, aka .ls.
//!
//! LS files are produced by the C++ Nix implementation via `write-nar-listing=1` query parameter
//! passed to a store implementation when transferring store paths.
//!
//! Listing files contains metadata about a file and its offset in the corresponding NAR.
//!
//! NOTE: LS entries does not offer any integrity field to validate the retrieved file at the provided
//! offset. Validating the contents is the caller's responsibility.

use std::collections::HashMap;

use serde::Deserialize;

#[cfg(test)]
mod test;

#[derive(Debug, Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum ListingEntry {
    Regular {
        size: u64,
        #[serde(default)]
        executable: bool,
        #[serde(rename = "narOffset")]
        nar_offset: u64,
    },
    Directory {
        entries: HashMap<String, ListingEntry>,
    },
    Symlink {
        target: String,
    },
}

#[derive(Debug)]
pub struct ListingVersion<const V: u8>;

#[derive(Debug, thiserror::Error)]
#[error("Invalid version: {0}")]
struct ListingVersionError(u8);

impl<'de, const V: u8> Deserialize<'de> for ListingVersion<V> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let value = u8::deserialize(deserializer)?;
        if value == V {
            Ok(ListingVersion::<V>)
        } else {
            Err(serde::de::Error::custom(ListingVersionError(value)))
        }
    }
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
#[non_exhaustive]
pub enum Listing {
    V1 {
        root: ListingEntry,
        version: ListingVersion<1>,
    },
}