pijul nest
guest [sign in]

Some better error handling + implements gzipping packets + corrects bug with architecture name in ubuntu repos

Madx
Feb 4, 2026, 3:06 PM
NJOCBMW63YOWBM72N44C32BLSVKDL6TMOMGOUCZOLDSZQSENQAJAC

Dependencies

  • [2] UWQB743K First working shell (with ocaml code)
  • [3] LIMIMBOU Bootstrapping the Rust part
  • [4] 2GMRXGVB Some better error handling + implements gzipping packets + corrects bug with architecture name in ubuntu repos
  • [5] AJLMC7UM Forwarding log to frontend
  • [6] NT4JHHKX Implementing the handshake call in the protocol
  • [7] ODUDDQRY Adding the OCaml interface

Change contents

  • file deletion: deb.rs~ (----------)
    [2.15][4.0:31](),[4.31][4.32:32]()
    use lazy_static::lazy_static;
    use std::collections::HashMap;
    use std::ffi::OsStr;
    use std::io::{BufRead, BufReader, Read, Seek, SeekFrom};
    use thiserror::*;
    use tracing::*;
    mod index;
    pub use index::*;
    /// A parsed .deb file.
    #[derive(Debug)]
    pub struct Deb {
    md5sums: HashMap<String, String>,
    control_file: String,
    _global: Header,
    _control: Header,
    data: Header,
    data_offset: u64,
    }
    /// A "stanza", i.e. a description of a Debian package.
    #[derive(Debug, Default, Clone)]
    pub struct Stanza<'a> {
    #[allow(missing_docs)]
    pub package: &'a str,
    #[allow(missing_docs)]
    pub version: Version<'a>,
    #[allow(missing_docs)]
    pub architecture: &'a str,
    #[allow(missing_docs)]
    pub depends: Vec<Dep<'a>>,
    #[allow(missing_docs)]
    pub sha256: Option<&'a str>,
    #[allow(missing_docs)]
    pub size: Option<usize>,
    /// Path of the .deb file inside an index.
    pub file_name: Option<&'a str>,
    }
    /// A dependency, which is either a "simple" dependency, or a list of
    /// alternatives.
    #[derive(Debug, Clone)]
    pub enum Dep<'a> {
    /// A simple dependency.
    Simple(SimpleDep<'a>),
    /// A dependency that can be supplied by multiple packages. This
    /// is an alternative mechanism to virtual packages.
    Alternatives {
    #[allow(missing_docs)]
    alt: Vec<SimpleDep<'a>>,
    },
    }
    /// A single dependency.
    #[derive(Debug, Clone)]
    pub struct SimpleDep<'a> {
    /// Name of the dependency.
    pub name: &'a str,
    /// Whether this dependency can have any version.
    pub any: bool,
    /// Constraints on dependencies.
    pub constraints: Vec<(&'a str, Version<'a>)>,
    }
    fn parse_control(s: &str) -> IResult<&str, Stanza> {
    let mut st = Stanza::default();
    for l in s.lines() {
    if l.is_empty() {
    let (_, b) = s.split_at(l.as_ptr() as usize - s.as_ptr() as usize);
    return Ok((b, st));
    }
    if let Some(i) = l.find(':') {
    let (key, val) = l.split_at(i);
    let (_, val) = val.split_at(1);
    debug!("key {:?} {:?}", key, val);
    match key.trim() {
    "Package" => st.package = val.trim(),
    "Version" => st.version = parse_version(val.trim()).unwrap().1,
    "Architecture" => st.architecture = val.trim(),
    "SHA256" => st.sha256 = Some(val.trim()),
    "Filename" => st.file_name = Some(val.trim()),
    "Depends" | "Pre-Depends" => {
    let d = parse_deps(val.trim());
    debug!("{:?}", d);
    if let Ok(("", a)) = d {
    st.depends = a
    } else {
    error!("error {:?}", val.trim());
    return IResult::Err(nom::Err::Error(nom::error::make_error(
    s,
    nom::error::ErrorKind::Tag,
    )));
    }
    }
    _ => {}
    }
    }
    }
    Ok(("", st))
    }
    #[test]
    fn test_grub_common() {
    use tracing_subscriber::prelude::*;
    use tracing_subscriber::util::SubscriberInitExt;
    tracing_subscriber::registry()
    .with(
    tracing_subscriber::EnvFilter::try_from_default_env()
    .unwrap_or_else(|_| String::new().into()),
    )
    .with(tracing_subscriber::fmt::layer())
    .try_init()
    .unwrap();
    let (rest, p) = parse_control("Package: grub-common\nArchitecture: amd64\nVersion: 2.12-1ubuntu7\nBuilt-Using: lzo2 (= 2.10-2build3)\nMulti-Arch: foreign\nPriority: optional\nSection: admin\nSource: grub2\nOrigin: Ubuntu\nMaintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>\nOriginal-Maintainer: GRUB Maintainers <pkg-grub-devel@alioth-lists.debian.net>\nBugs: https://bugs.launchpad.net/ubuntu/+filebug\nInstalled-Size: 12476\nDepends: libc6 (>= 2.38), libdevmapper1.02.1 (>= 2:1.02.36), libefiboot1t64 (>=38), libefivar1t64 (>= 38), libfreetype6 (>= 2.2.1), libfuse3-3 (>= 3.2.3), liblzma5 (>= 5.1.1alpha+20120614), debconf (>= 0.5) | debconf-2.0, gettext-base, lsb-base (>= 3.0-6), python3, python3-apt\nRecommends: os-prober (>= 1.33)\nSuggests: multiboot-doc, grub-emu, mtools, xorriso (>= 0.5.6.pl00), desktop-base (>= 4.0.6), console-setup\nConflicts: init-select\nBreaks: apport (<< 2.1.1), friendly-recovery (<< 0.2.13), lupin-support (<< 0.55), mdadm (<< 2.6.7-2)\nReplaces: grub-coreboot (<< 2.00-4), grub-efi (<< 1.99-1), grub-efi-amd64 (<< 2.00-4), grub-efi-ia32 (<< 2.00-4), grub-efi-ia64 (<< 2.00-4), grub-ieee1275 (<< 2.00-4), grub-linuxbios (<< 1.96+20080831-1), grub-pc (<< 2.00-4), grub-yeeloong (<< 2.00-4), init-select\nFilename: pool/main/g/grub2/grub-common_2.12-1ubuntu7_amd64.deb\nSize: 2119862\nMD5sum: 86e63081ae4ee34d6a4f408ac6dbf0e4\nSHA1: b98f9f88e5480423e041fd647b438ba2f6e6f5ea\nSHA256: d8110b97b6fb6da40449c74b9cb527f02965dffaae8344399a711617f5a69249\nSHA512: 85c63b8d3e6a870df48533ab525df13abe9ec4861ff4b5577def94f65a2ebf6ac44a54a6cd0eca1c825e1c7aa2bd14e4d4ed53f83d381963ac1dc51daa16482a\nHomepage: https://www.gnu.org/software/grub/\nDescription: GRand Unified Bootloader (common files)\nTask: ubuntu-desktop-minimal, ubuntu-desktop, ubuntu-desktop-raspi, kubuntu-desktop, xubuntu-minimal, xubuntu-desktop, lubuntu-desktop, ubuntustudio-desktop-core, ubuntustudio-desktop, ubuntukylin-desktop,ubuntukylin-desktop-minimal, ubuntu-mate-core, ubuntu-mate-desktop, ubuntu-budgie-desktop-minimal, ubuntu-budgie-desktop, ubuntu-budgie-desktop-raspi, ubuntu-unity-desktop, edubuntu-desktop-gnome-minimal, edubuntu-desktop-gnome-raspi, ubuntucinnamon-desktop-minimal, ubuntucinnamon-desktop-raspi\nDescription-md5: 9c75036dc0a0792fedbc58df208ed227").unwrap();
    assert!(p.depends.iter().any(|x| match x {
    Dep::Simple(s) => s.name == "liblzma5",
    _ => false,
    }));
    assert!(rest.is_empty())
    }
    /// A version of a package.
    #[derive(Debug, Clone)]
    pub struct Version<'a> {
    /// Epoch, i.e. a Debian mechanism to reorder upstream versions if
    /// necessary, for example if the upstream versioning scheme
    /// changes.
    pub epoch: u32,
    /// Upstream version.
    pub upstream_version: Upstream<'a>,
    /// Debian-specific suffix to distinguish between patches.
    pub debian: Option<&'a str>,
    }
    impl<'a> Default for Version<'a> {
    fn default() -> Version<'a> {
    Version {
    epoch: 0,
    upstream_version: Upstream(""),
    debian: None,
    }
    }
    }
    fn parse_constraint<'a>(s: &'a str) -> IResult<&'a str, (&'a str, Version<'a>)> {
    let (s, constraint) = tag("<<")
    .or(tag("<="))
    .or(tag("="))
    .or(tag(">="))
    .or(tag(">>"))
    .parse(s)?;
    debug!("parse_constraint {:?}", constraint);
    let (s, _) = space0(s)?;
    let (s, version) = parse_version(s)?;
    debug!("parse_constraint {:?} {:?}", s, (constraint, &version));
    Ok((s, (constraint, version)))
    }
    fn parse_dep<'a>(s0: &'a str) -> IResult<&'a str, Dep<'a>> {
    let (s, name) = parse_name(s0)?;
    let (s, any) = if s.starts_with(":any") {
    let (_, b) = s.split_at(4);
    (b, true)
    } else {
    (s, false)
    };
    let (s, _) = space0(s)?;
    let mut constraints = Vec::new();
    if let Ok((s, _)) = tag::<_, _, ()>("(").parse(s) {
    let (s, version) = if let Some(i) = s.find(')') {
    let (a, b) = s.split_at(i);
    let (_, b) = b.split_at(1);
    (b, a)
    } else {
    return IResult::Err(nom::Err::Error(nom::error::make_error(
    s,
    nom::error::ErrorKind::Tag,
    )));
    };
    let mut v = version;
    while let Ok((v_, c)) = parse_constraint(v) {
    constraints.push(c);
    let (v_, _) = space0(v_)?;
    if let Ok((v_, _)) = tag::<_, _, ()>(",").parse(v_) {
    let (v_, _) = space0(v_)?;
    v = v_;
    } else {
    break;
    }
    }
    Ok((
    s,
    Dep::Simple(SimpleDep {
    name,
    any,
    constraints,
    }),
    ))
    } else {
    Ok((
    s,
    Dep::Simple(SimpleDep {
    name,
    any,
    constraints,
    }),
    ))
    }
    }
    fn parse_name(s: &str) -> IResult<&str, &str> {
    if let Some(i) = s.find(|c| {
    !((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.' || c == '+' || c == '-')
    }) {
    if i > 0 {
    let (a, b) = s.split_at(i);
    return Ok((b, a));
    }
    } else {
    return Ok(("", s));
    }
    error!("ERROR {:?}", s);
    return IResult::Err(nom::Err::Error(nom::error::make_error(
    s,
    nom::error::ErrorKind::Tag,
    )));
    }
    fn parse_deps<'a>(s: &'a str) -> IResult<&'a str, Vec<Dep<'a>>> {
    let mut d = Vec::new();
    let (mut s, _) = space0(s)?;
    let mut alt_is_open = false;
    while let Ok((s_, d_)) = parse_dep(s) {
    let (s_, _) = space0(s_)?;
    if let Ok((s_, _)) = tag::<_, _, ()>("|").parse(s_) {
    let (s_, _) = space0(s_)?;
    s = s_;
    alt_is_open = true;
    if let Dep::Simple(d_) = d_ {
    if let Some(Dep::Alternatives { ref mut alt }) = d.last_mut() {
    alt.push(d_)
    } else {
    d.push(Dep::Alternatives { alt: vec![d_] })
    }
    } else {
    panic!("{:?}", s);
    }
    } else if let Ok((s_, _)) = tag::<_, _, ()>(",").parse(s_) {
    if alt_is_open {
    if let Some(Dep::Alternatives { ref mut alt }) = d.last_mut() {
    if let Dep::Simple(d_) = d_ {
    alt.push(d_)
    } else {
    panic!("{:?}", s);
    }
    }
    } else {
    d.push(d_)
    }
    alt_is_open = false;
    s = s_;
    } else {
    if alt_is_open {
    if let Some(Dep::Alternatives { ref mut alt }) = d.last_mut() {
    if let Dep::Simple(d_) = d_ {
    alt.push(d_)
    } else {
    panic!("{:?}", s);
    }
    }
    } else {
    d.push(d_)
    }
    s = s_;
    break;
    }
    let (s_, _) = space0(s)?;
    s = s_;
    }
    Ok((s, d))
    }
    /// The representation of an upstream version. Will implement `Ord`.
    #[derive(Debug, Clone, PartialEq, Eq)]
    pub struct Upstream<'a>(&'a str);
    impl<'a> std::ops::Deref for Upstream<'a> {
    type Target = str;
    fn deref(&self) -> &str {
    self.0
    }
    }
    impl<'a> PartialOrd for Upstream<'a> {
    /// Following the algorithm described here:
    /// https://www.debian.org/doc/debian-policy/ch-controlfields.html#version
    fn partial_cmp(&self, b: &Upstream<'a>) -> Option<std::cmp::Ordering> {
    fn split_initial(s: &str) -> (&str, &str, &str) {
    // Find `u`, the first digit, and `v > u`, the first non-digit after `u`.
    let mut u = s.len();
    let mut v = s.len();
    for (n, &b) in s.as_bytes().iter().enumerate() {
    if u == s.len() {
    if b >= b'0' && b <= b'9' {
    u = n
    }
    } else if b < b'0' || b > b'9' {
    v = n;
    break;
    }
    }
    let (ab, c) = s.split_at(v);
    let (a, b) = ab.split_at(u);
    (a, b, c)
    }
    let mut a = self.0;
    let mut b = b.0;
    use std::cmp::Ordering;
    loop {
    let (a0, a1, a_) = split_initial(a);
    let (b0, b1, b_) = split_initial(b);
    // Compare a0 / b0 using the modified ASCII ordering described in the Debian manual.
    let mut a0i = a0.as_bytes().iter();
    let mut b0i = b0.as_bytes().iter();
    loop {
    match (a0i.next(), b0i.next()) {
    (None, None) => break,
    (Some(c), Some(d)) if c == d => continue,
    (Some(b'~'), _) => return Some(Ordering::Less),
    (_, Some(b'~')) => return Some(Ordering::Greater),
    (Some(c), Some(d)) if c.is_ascii_alphabetic() && !d.is_ascii_alphabetic() => {
    return Some(Ordering::Less);
    }
    (Some(c), Some(d)) if d.is_ascii_alphabetic() && !c.is_ascii_alphabetic() => {
    return Some(Ordering::Greater);
    }
    (c, d) => return Some(c.cmp(&d)),
    }
    }
    // If we haven't returned, a0 == b0. Compare the digits part.
    let a1 = if a1.is_empty() {
    0
    } else {
    a1.parse::<u64>().unwrap()
    };
    let b1 = if b1.is_empty() {
    0
    } else {
    b1.parse::<u64>().unwrap()
    };
    let cmp1 = a1.cmp(&b1);
    if let Ordering::Equal = cmp1 {
    a = a_;
    b = b_;
    } else {
    return Some(cmp1);
    }
    }
    }
    }
    impl<'a> Ord for Upstream<'a> {
    fn cmp(&self, b: &Upstream<'a>) -> std::cmp::Ordering {
    self.partial_cmp(b).unwrap()
    }
    }
    #[test]
    fn test_upstream_ordering() {
    let mut x = [
    Upstream("~~"),
    Upstream("~~a"),
    Upstream("~"),
    Upstream(""),
    Upstream("a"),
    ];
    let y = x.clone();
    x.sort();
    assert_eq!(x, y,);
    }
    use nom::{
    bytes::tag,
    character::complete::{digit1, newline, space0},
    multi::separated_list1,
    IResult, Parser,
    };
    /// Parse a version in the Debian version format.
    fn parse_version<'a>(s: &'a str) -> IResult<&'a str, Version<'a>> {
    fn epoch(s: &str) -> IResult<&str, u32> {
    let (s, i) = digit1(s)?;
    let (s, _) = tag(":").parse(s)?;
    Ok((s, i.parse().unwrap()))
    }
    let (s, epoch) = epoch(s).unwrap_or((s, 0));
    lazy_static! {
    static ref RE_DASH: regex::Regex =
    regex::Regex::new(r"^[a-z0-9\.~+-]+-([a-z0-9\.~+]+)").unwrap();
    static ref RE: regex::Regex = regex::Regex::new(r"^[a-z0-9\.~+]+").unwrap();
    }
    let (s, v) = if let Some(m) = RE_DASH.find(s) {
    debug!("parse_version RE_DASH {:?}", s);
    let (a, b) = s.split_at(m.end());
    (b, a)
    } else if let Some(m) = RE.find(s) {
    debug!("parse_version RE {:?}", s);
    let (a, b) = s.split_at(m.end());
    (b, a)
    } else {
    debug!("parse_version failed {:?}", s);
    return IResult::Err(nom::Err::Error(nom::error::make_error(
    s,
    nom::error::ErrorKind::Tag,
    )));
    };
    let (upstream_version, debian) = if let Some(i) = v.rfind('-') {
    let (a, b) = v.split_at(i);
    (Upstream(a), Some(b.split_at(1).1))
    } else {
    (Upstream(v), None)
    };
    Ok((
    s,
    Version {
    epoch,
    upstream_version,
    debian,
    },
    ))
    }
    /// Structure of the "headers" in the debian file. No alignment
    /// necessary, the numbers are in decimal. No heap allocation.
    #[derive(Debug)]
    #[repr(C)]
    struct H {
    file_id: [u8; 16],
    timestamp: [u8; 12],
    owner_id: [u8; 6],
    group_id: [u8; 6],
    file_mode: [u8; 8],
    file_size: [u8; 10],
    end_char: [u8; 2],
    }
    /// Parsed version of `H`, still no heap allocation.
    #[allow(dead_code)]
    #[derive(Debug)]
    struct Header {
    file_id: [u8; 16],
    timestamp: u64,
    owner_id: u32,
    group_id: u32,
    file_mode: u32,
    file_size: u64,
    }
    #[allow(missing_docs)]
    /// Possible errors for .deb files.
    #[derive(Debug, Error)]
    pub enum Error {
    #[error("Deb file parse error")]
    ParseError,
    #[error(transparent)]
    Utf8(#[from] std::str::Utf8Error),
    #[error(transparent)]
    Int(#[from] std::num::ParseIntError),
    #[error(transparent)]
    IO(#[from] std::io::Error),
    #[error("Unsupported compression algorithm: {file_id}")]
    UnsupportedCompression { file_id: String },
    #[error("Version parse: {version}")]
    Version { version: String },
    }
    impl Header {
    pub fn file_id(&self) -> &str {
    std::str::from_utf8(&self.file_id).unwrap().trim()
    }
    }
    impl H {
    fn parse(&self) -> Result<Header, Error> {
    std::str::from_utf8(&self.file_id)?;
    Ok(Header {
    file_id: self.file_id,
    timestamp: std::str::from_utf8(&self.timestamp)?.trim().parse()?,
    owner_id: std::str::from_utf8(&self.owner_id)?.trim().parse()?,
    group_id: std::str::from_utf8(&self.group_id)?.trim().parse().unwrap(),
    file_mode: u32::from_str_radix(std::str::from_utf8(&self.file_mode)?.trim(), 8)?,
    file_size: std::str::from_utf8(&self.file_size)?.trim().parse()?,
    })
    }
    }
    fn read_hdr<R: Read>(r: &mut R) -> Result<Header, Error> {
    unsafe {
    assert_eq!(size_of::<H>(), HEADER_SIZE as usize);
    let mut b: H = std::mem::zeroed();
    let pb: *mut H = &mut b;
    r.read_exact(&mut std::slice::from_raw_parts_mut(
    pb as *mut u8,
    size_of::<H>(),
    ))?;
    debug!("{:?}", b);
    b.parse()
    }
    }
    const HEADER_SIZE: u64 = 60;
    impl Deb {
    /// Parse the control part, containing the description (stanza) of this package.
    pub fn parse_control(&self) -> Vec<Stanza> {
    if let Ok(("", st)) = separated_list1(newline, parse_control).parse(&self.control_file) {
    st
    } else {
    Vec::new()
    }
    }
    /// Return the md5sums of each file.
    pub fn md5sums(&self) -> &HashMap<String, String> {
    &self.md5sums
    }
    /// Decompress the data part of the deb file into a directory.
    pub fn decompress<R: BufRead + Seek, P: AsRef<std::path::Path>>(
    &self,
    mut r: R,
    path: P,
    ) -> Result<(), Error> {
    r.seek(SeekFrom::Start(self.data_offset))?;
    debug!("decompress {:?}", self.data.file_id());
    if self.data.file_id().ends_with(".tar.gz") {
    unimplemented!()
    } else if self.data.file_id().ends_with(".tar.zst") {
    let mut ar = tar::Archive::new(
    zstd::stream::read::Decoder::new(r.take(self.data.file_size)).unwrap(),
    );
    for e in ar.entries().unwrap() {
    let mut e = e.unwrap();
    debug!("zstd decompress {:?}", e.path());
    // f(&e.path()?);
    e.unpack_in(&path).unwrap();
    }
    } else if self.data.file_id().ends_with(".tar.xz") {
    let mut ar = tar::Archive::new(xz::read::XzDecoder::new(r.take(self.data.file_size)));
    for e in ar.entries().unwrap() {
    let mut e = e.unwrap();
    debug!("xz decompress {:?}", e.path());
    // f(&e.path()?);
    e.unpack_in(&path).unwrap();
    }
    } else if self.data.file_id().ends_with(".tar") {
    let mut ar = tar::Archive::new(r.take(self.data.file_size));
    for e in ar.entries().unwrap() {
    let mut e = e.unwrap();
    debug!("tar extract {:?}", e.path());
    // f(&e.path()?);
    e.unpack_in(&path).unwrap();
    }
    } else {
    unimplemented!()
    };
    Ok(())
    }
    /// Parse a `.deb` file from a buffered reader.
    pub fn read<R: BufRead + Seek>(mut r: R) -> Result<Deb, Error> {
    let mut b = [0; 8];
    r.read_exact(&mut b)?;
    if &b != b"!<arch>\n" {
    return Err(Error::ParseError);
    }
    let global = read_hdr(&mut r)?;
    trace!("GLOBAL {:?}", global);
    assert_eq!(global.file_size, 4);
    r.seek(SeekFrom::Current(global.file_size as i64))?;
    // r.skip_until(b'c')?; // 'control.…'
    // r.seek(SeekFrom::Current(-1))?;
    let control = read_hdr(&mut r)?;
    trace!("CONTROL {:?}", control);
    // Control files are expected to be short, decompress.
    let mut ctrl = vec![0; control.file_size as usize];
    r.read_exact(&mut ctrl)?;
    // gzip xz zstd none
    let ctrl_tar = if control.file_id().ends_with(".tar.gz") {
    unimplemented!()
    } else if control.file_id().ends_with(".tar.zst") {
    zstd::decode_all(&ctrl[..])?
    } else if control.file_id().ends_with(".tar.xz") {
    let mut decompressor = xz::read::XzDecoder::new(&ctrl[..]);
    let mut result = Vec::new();
    decompressor.read_to_end(&mut result)?;
    result
    } else if control.file_id().ends_with(".tar") {
    ctrl
    } else {
    return Err(Error::UnsupportedCompression {
    file_id: control.file_id().to_string(),
    });
    };
    let mut ctrl_tar = tar::Archive::new(&ctrl_tar[..]);
    let mut control_file = String::new();
    let mut md5sums = HashMap::new();
    for e in ctrl_tar.entries().unwrap() {
    let mut e = e.unwrap();
    if e.path()?.file_name() == Some(OsStr::new("control")) {
    e.read_to_string(&mut control_file)?;
    } else if e.path()?.file_name() == Some(OsStr::new("md5sums")) {
    let mut e = BufReader::new(e);
    let mut s = String::new();
    while e.read_line(&mut s)? > 0 {
    let mut it = s.split(' ').filter(|x| !x.is_empty());
    if let (Some(hash), Some(file)) = (it.next(), it.next()) {
    md5sums.insert(file.trim().to_string(), hash.trim().to_string());
    }
    s.clear();
    }
    }
    }
    if control.file_size % 2 == 1 {
    r.seek(SeekFrom::Current(1))?;
    }
    let data = read_hdr(&mut r)?;
    debug!("data = {:?}", data);
    Ok(Deb {
    md5sums,
    _global: global,
    _control: control,
    control_file,
    data,
    data_offset: r.seek(SeekFrom::Current(0))?,
    })
    }
    }
  • file deletion: main.rs~ (----------)
    [2.15][4.44502:44534](),[4.44534][4.44535:44535]()
    use clap::*;
    use elpe::*;
    use serde_derive::*;
    use std::path::PathBuf;
    use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
    mod server;
    #[derive(Deserialize)]
    pub struct Config {
    pgp_home: PathBuf,
    store_path: PathBuf,
    package_index: String,
    user: String,
    }
    /// Elpe
    #[derive(Parser, Debug)]
    #[command(version, about, long_about = None)]
    struct Args {
    /// Path to the configuration file
    #[arg(short, long)]
    config: PathBuf,
    }
    fn main() {
    tracing_subscriber::registry()
    .with(
    tracing_subscriber::EnvFilter::try_from_default_env()
    .unwrap_or_else(|_| String::new().into()),
    )
    .with(tracing_subscriber::fmt::layer())
    .init();
    let args = Args::parse();
    let config: Config = toml::from_str(&std::fs::read_to_string(&args.config).unwrap()).unwrap();
    let container_channel = crate::container::serve(&config.user, &config.store_path);
    let rt = tokio::runtime::Runtime::new().unwrap();
    rt.block_on(async move {
    privdrop::PrivDrop::default()
    .user(&config.user)
    .apply()
    .unwrap();
    let elpe = server::Elpe::new(
    Client::new(&config.pgp_home, &config.store_path, &config.package_index),
    container_channel,
    );
    let addr = "0.0.0.0:50051".parse().unwrap();
    elpe.serve(addr).await
    })
    }
  • replacement in src/server.rs at line 203
    [3.6824][3.6824:6845]()
    let p = self
    [3.6824]
    [3.6845]
    let download = self
  • replacement in src/server.rs at line 226
    [3.7597][3.7597:8001]()
    .await
    .unwrap();
    Ok(tonic::Response::new(proto::DerivationReply {
    result: Some(proto::derivation_reply::Result::Ok(
    proto::DerivationResult {
    destdir: vec![p.to_str().unwrap().to_string()],
    paths: Vec::new(),
    path_patterns: Vec::new(),
    },
    )),
    }))
    [3.7597]
    [3.8001]
    .await;
    match download {
    Ok(p) => {
    Ok(tonic::Response::new(proto::DerivationReply {
    result: Some(proto::derivation_reply::Result::Ok(
    proto::DerivationResult {
    destdir: vec![p.to_str().unwrap().to_string()],
    paths: Vec::new(),
    path_patterns: Vec::new(),
    },
    )),
    }))
    }
    Err(e) => {
    Ok(tonic::Response::new(proto::DerivationReply {
    result: Some(proto::derivation_reply::Result::Error(
    e.to_string()
    )),
    }))
    }
    }
  • replacement in src/server.rs at line 248
    [3.8007][3.8007:8008]()
    [3.8007]
    [3.8008]
  • replacement in src/server.rs at line 259
    [3.8457][3.8457:8485]()
    1 => "aarch64",
    [3.8457]
    [3.8485]
    1 => "arm64",
  • replacement in src/server.rs at line 266
    [3.8640][3.8640:8663]()
    .unwrap();
    [3.8640]
    [3.8663]
    .expect(&format!("{} {}", &r.repository, &arch));