narinfo-rs/src/nix_cache_info.rs

118 lines
5.1 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use crate::error::{ParsingError, ParsingResult};
use alloc::borrow::Cow;
use core::fmt;
use core::fmt::Write;
use derive_builder::Builder;
/// Struct representing the nix-cache-info file fetched from a nix substituter, describing the
/// substituter
/// Based on the [unofficial spec](https://fzakaria.github.io/nix-http-binary-cache-api-spec)
/// and the [libstore narinfo parsing code.](https://github.com/NixOS/nix/blob/af4e8b00fb986acf32d7e4cd4fff7218b38958df/src/libstore/binary-cache-store.cc#L37)
#[derive(Builder, Eq, PartialEq, Debug)]
#[builder(no_std)]
#[builder(build_fn(error = "crate::error::ParsingError<'static>"))]
#[builder(setter(into))]
pub struct NixCacheInfo<'a> {
/// The path of the Nix store to which this binary cache applies. Binaries are not relocatable — a binary built for `/nix/store` wont generally work in `/home/alice/store` — so to prevent binaries from being used in a wrong store, a binary cache is only used if its StoreDir matches the local Nix configuration. The default path on nixos is `/nix/store`.
pub store_dir: Cow<'a, str>,
/// Query operations such as `nix-env -qas` can cause thousands of cache queries, and thus thousands of HTTP requests, to determine which packages are available in binary form. While these requests are small, not every server may appreciate a potential onslaught of queries. If WantMassQuery is set to 0 (default), “mass queries” such as `nix-env -qas` will skip this cache. Thus a package may appear not to have a binary substitute. However, the binary will still be used when you actually install the package. If WantMassQuery is set to 1, mass queries will use this cache.
#[builder(default)]
pub wants_mass_query: bool,
/// Each binary cache has a priority (defaulting to 50). Binary caches are checked for binaries in order of ascending priority; thus a higher number denotes a lower priority. The binary cache cache.nixos.org has priority 40.
#[builder(default = "50")]
pub priority: usize,
}
impl<'a> NixCacheInfo<'a> {
/// Parses the contents of a narinfo file from str.
/// This function is also used to implement the [TryFrom] trait for &'a str.
///
/// For duplicate keys the last value is used(same behaviour as libstore).
/// Unknown keys return an error(unlike libstore where they're simply ignored).
///
/// ```
/// # fn http_get_str(_url: &str) -> &'static str { include_str!("../nix-cache-info.sample") }
/// use narinfo::NixCacheInfo;
/// let data: &str =
/// http_get_str("https://cache.nixos.org/nix-cache-info");
///
/// let parsed = NixCacheInfo::parse(data).unwrap();
/// assert_eq!(parsed.store_dir, "/nix/store");
/// ```
pub fn parse(value: &'a str) -> ParsingResult<Self> {
let mut builder = NixCacheInfoBuilder::default();
for line in value.lines() {
let (key, value) = line
.split_once(':')
.map(|(k, v)| (k, v.trim()))
.ok_or(ParsingError::InvalidLine { line })?;
match key {
"StoreDir" => builder.store_dir(value),
"WantMassQuery" => {
let wants_mass_query: usize = ParsingError::try_parse_int(value, line)?;
builder.wants_mass_query(wants_mass_query >= 1)
}
"Priority" => builder.priority(ParsingError::try_parse_int(value, line)?),
_ => return Err(ParsingError::UnknownKey { key }),
};
}
builder.build()
}
/// Serializes the narinfo struct into the text format
/// ```
/// use narinfo::NixCacheInfo;
///
/// let info = NixCacheInfo::parse(include_str!("../nix-cache-info.sample")).unwrap();
/// let mut serialized = String::new();
/// info.serialize_into(&mut serialized).unwrap();
/// ```
pub fn serialize_into<T: Write>(&self, w: &mut T) -> fmt::Result {
write!(w, "StoreDir: {}", self.store_dir)?;
write!(w, "\nPriority: {}", self.priority)?;
let mass_query_int = if self.wants_mass_query { 1 } else { 0 };
write!(w, "\nWantMassQuery: {}", mass_query_int)?;
Ok(())
}
/// Get the builder for this struct.
/// The builder is generated by [derive_builder](https://lib.rs/crates/derive_builder)
// The builder is is used internally by the parsing code so we might as well expose it.
// If we ever decide to not use derive_builder we can simply hide it behind an optional feature flag
pub fn builder() -> NixCacheInfoBuilder<'a> {
NixCacheInfoBuilder::default()
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::String;
static SAMPLE_NIX_CACHE_INFO: &str = include_str!("../nix-cache-info.sample");
#[test]
fn parses_sample_nix_cache_info() {
let info = NixCacheInfo::parse(SAMPLE_NIX_CACHE_INFO).unwrap();
assert_eq!(info.store_dir, "/nix/store");
assert_eq!(info.priority, 40);
assert!(info.wants_mass_query);
}
#[test]
fn serializes_then_deserializes_into_same_result() {
let info = NixCacheInfoBuilder::default()
.store_dir("/home/alice/nix")
.wants_mass_query(true)
.priority(69_usize)
.build()
.unwrap();
let mut serialized = String::new();
info.serialize_into(&mut serialized).unwrap();
let deserialized = NixCacheInfo::parse(&serialized).unwrap();
assert_eq!(deserialized.store_dir, "/home/alice/nix");
assert_eq!(deserialized.priority, 69);
assert!(deserialized.wants_mass_query);
}
}