inapt 0.1.0

A minimal Debian/Ubuntu APT repository proxy written in Rust. Exposes a valid APT repo structure over HTTP, sourcing .deb packages from GitHub Releases.
Documentation
use std::{marker::PhantomData, sync::Arc};

use anyhow::Context;

use crate::{adapter_deb::DebReader, domain::AptRepositoryService};

mod adapter_deb;
mod adapter_github;
mod adapter_http_server;
mod adapter_storage;
mod adapter_worker;
mod domain;

fn maybe_env(name: &str) -> Option<String> {
    std::env::var(name).ok()
}

fn with_env_as_or<T>(name: &str, default_value: T) -> anyhow::Result<T>
where
    T: std::str::FromStr,
    <T as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static,
{
    let Ok(value) = std::env::var(name) else {
        return Ok(default_value);
    };
    value
        .parse::<T>()
        .with_context(|| format!("unable to parse value from {name:?}"))
}

pub struct Config {
    github: adapter_github::Config,
    http_server: adapter_http_server::Config,
    worker: adapter_worker::Config,
}

impl Config {
    pub fn from_env() -> anyhow::Result<Config> {
        Ok(Self {
            github: adapter_github::Config::from_env()?,
            http_server: adapter_http_server::Config::from_env()?,
            worker: adapter_worker::Config::from_env()?,
        })
    }

    pub fn build(self) -> anyhow::Result<Application> {
        let release_storage = crate::adapter_storage::MemoryStorage::default();
        let github = self.github.build()?;
        let apt_repository_service = AptRepositoryService {
            package_source: github.clone(),
            release_storage,
            config: Arc::from(crate::domain::Config {
                origin: "GitHub".into(),
                label: "Debian".into(),
                suite: "stable".into(),
                version: "0.1.0".into(),
                codename: "cucumber".into(),
                description: "GitHub releases proxy".into(),
                repositories: vec!["jdrouet/mrml".to_string(), "helix-editor/helix".to_string()],
            }),
            clock: PhantomData::<chrono::Utc>,
            deb_extractor: DebReader,
        };
        Ok(Application {
            github,
            http_server: self
                .http_server
                .builder()?
                .with_apt_repository(apt_repository_service.clone())
                .build()?,
            worker: self
                .worker
                .builder()
                .with_apt_repository(apt_repository_service)
                .build()?,
        })
    }
}

pub struct Application {
    #[allow(unused, reason = "preparation")]
    github: adapter_github::Client,
    http_server: adapter_http_server::Server,
    worker: adapter_worker::Worker,
}

impl Application {
    pub async fn run(self) -> anyhow::Result<()> {
        self.http_server.run().await?;
        self.worker.shutdown().await
    }
}