Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ tmp/
.ninja*
.fapt-lists/
.gdb_history

src/*.egg-info
src/*.so
venv/
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ insideout = "0.2"
mailparse = "0.13"
md-5 = "0.10"
nom = "4"
pyo3 = { version = "0.17.2", features = ["anyhow", "chrono", "extension-module"] }
sha2 = "0.10"
tempfile = "3"
tempfile-fast = "0.3"
Expand Down Expand Up @@ -71,3 +72,7 @@ version = "0.3"
[[bin]]
name = "fapt"
required-features = ["binaries"]

[lib]
name = "fapt"
crate-type = ["cdylib", "lib"]
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
venv:
python3 -m venv venv
./venv/bin/pip install -r requirements.txt

test: venv
./venv/bin/pip install -e .
./venv/bin/pytest -v
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pytest
setuptools_rust
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[metadata]
name = fapt
6 changes: 6 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from setuptools import setup
from setuptools_rust import RustExtension

setup(
rust_extensions=[RustExtension("fapt")],
)
5 changes: 5 additions & 0 deletions src/checksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ use hex::FromHex;
use sha2::Digest;
use sha2::Sha256;

use pyo3::prelude::pyclass;

pub type MD5 = [u8; 16];
pub type SHA256 = [u8; 32];

#[derive(Copy, Clone, Hash, PartialEq, Eq)]
#[pyclass]
pub struct Hashes {
#[pyo3(get)]
pub md5: MD5,
#[pyo3(get)]
pub sha256: SHA256,
}

Expand Down
9 changes: 9 additions & 0 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ use crate::system::ListingBlocks;
use crate::system::NamedBlock;
use crate::system::System;

use pyo3::prelude::{pyfunction, wrap_pyfunction, PyModule, PyResult, Python};

/// Use some set of bundled GPG keys.
///
/// These may be sufficient for talking to Debian or Ubuntu mirrors.
/// If you know what keys you actually want, or are using a real system,
/// please use the keys from there instead.
#[pyfunction]
pub fn add_builtin_keys(system: &mut System) {
system
.add_keys_from(io::Cursor::new(distro_keyring::supported_keys()))
Expand Down Expand Up @@ -182,3 +185,9 @@ fn print_ninja_binary(map: &HashMap<&str, Vec<&str>>) -> Result<(), Error> {

Ok(())
}

pub fn py_commands(py: Python<'_>) -> PyResult<&PyModule> {
let mut m = PyModule::new(py, "commands")?;
m.add_function(wrap_pyfunction!(add_builtin_keys, m)?)?;
Ok(m)
}
10 changes: 10 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#[macro_use]
extern crate nom;

use pyo3::prelude::{pymodule, PyModule, PyResult, Python};

mod checksum;
pub mod commands;
mod fetch;
Expand All @@ -15,3 +17,11 @@ pub mod rfc822;
mod signing;
pub mod sources_list;
pub mod system;

#[pymodule]
fn fapt(py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_submodule(commands::py_commands(py)?)?;
m.add_submodule(sources_list::py_sources_list(py)?)?;
m.add_submodule(system::py_system(py)?)?;
Ok(())
}
7 changes: 7 additions & 0 deletions src/lists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ use crate::fetch;
use crate::release::Release;
use crate::release::ReleaseContent;

use pyo3::prelude::pyclass;

#[derive(Debug)]
pub enum Compression {
None,
Expand Down Expand Up @@ -58,10 +60,15 @@ impl DownloadableListing {
// directory: "binary",
// name: "packages"
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
#[pyclass]
pub struct Listing {
#[pyo3(get)]
pub component: String,
#[pyo3(get)]
pub arch: Option<String>,
#[pyo3(get)]
pub directory: String,
#[pyo3(get)]
pub name: String,
}

Expand Down
26 changes: 26 additions & 0 deletions src/release.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,47 +26,73 @@ use crate::rfc822::RfcMapExt;
use crate::signing::GpgClient;
use crate::sources_list::Entry;

use pyo3::prelude::pyclass;

pub struct RequestedReleases {
releases: Vec<(RequestedRelease, Vec<Entry>)>,
}

#[derive(Clone, PartialOrd, Ord, Hash, PartialEq, Eq, Debug)]
#[pyclass]
pub struct RequestedRelease {
mirror: Url,
/// This can also be called "suite" in some places,
/// e.g. "unstable" (suite) == "sid" (codename)
#[pyo3(get)]
codename: String,

#[pyo3(get)]
pub arches: Vec<String>,
}

#[derive(Debug, Clone)]
#[pyclass]
pub struct ReleaseFile {
#[pyo3(get)]
origin: String,
#[pyo3(get)]
label: String,
#[pyo3(get)]
suite: Option<String>,
#[pyo3(get)]
codename: Option<String>,
#[pyo3(get)]
changelogs: Option<String>,
#[pyo3(get)]
date: DateTime<Utc>,
#[pyo3(get)]
valid_until: Option<DateTime<Utc>>,
#[pyo3(get)]
pub acquire_by_hash: bool,
#[pyo3(get)]
pub arches: Vec<String>,
#[pyo3(get)]
components: Vec<String>,
#[pyo3(get)]
description: Option<String>,
#[pyo3(get)]
pub contents: Vec<ReleaseContent>,
}

#[derive(Clone)]
#[pyclass]
pub struct ReleaseContent {
#[pyo3(get)]
pub len: u64,
#[pyo3(get)]
pub name: String,
#[pyo3(get)]
pub hashes: Hashes,
}

#[derive(Debug, Clone)]
#[pyclass]
pub struct Release {
#[pyo3(get)]
pub req: RequestedRelease,
#[pyo3(get)]
pub sources_entries: Vec<Entry>,
#[pyo3(get)]
pub file: ReleaseFile,
}

Expand Down
38 changes: 38 additions & 0 deletions src/sources_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ use anyhow::bail;
use anyhow::Context;
use anyhow::Error;

use pyo3::basic::CompareOp;
use pyo3::prelude::{pyclass, pymethods, IntoPy, Py, PyAny, PyModule, PyResult, Python};

/// Our representation of a classic sources list entry.
#[derive(Debug, PartialEq, Eq, Clone)]
#[pyclass]
pub struct Entry {
pub src: bool,
pub url: String,
Expand All @@ -17,6 +21,34 @@ pub struct Entry {
pub arch: Option<String>,
}

#[pymethods]
impl Entry {
#[new]
fn py_new(
src: bool,
url: String,
suite_codename: String,
components: Vec<String>,
arch: Option<String>,
) -> Self {
Entry {
src,
url,
suite_codename,
components,
arch,
}
}

fn __richcmp__(&self, py: Python<'_>, other: &Self, op: CompareOp) -> Py<PyAny> {
match op {
CompareOp::Eq => (self == other).into_py(py),
CompareOp::Ne => (self != other).into_py(py),
_ => py.NotImplemented(),
}
}
}

fn read_single_line(line: &str) -> Result<Vec<Entry>, Error> {
let line = match line.find('#') {
Some(comment) => &line[..comment],
Expand Down Expand Up @@ -132,3 +164,9 @@ deb-src http://foo bar baz quux
);
}
}

pub fn py_sources_list(py: Python<'_>) -> PyResult<&PyModule> {
let mut m = PyModule::new(py, "sources_list")?;
m.add_class::<Entry>()?;
Ok(m)
}
Loading