prebuilt-down 0.1.3

A CLI tool for resolve prebuilt binary dependencies
use anyhow::{Context, Result, bail};
use std::fs;
use std::path::PathBuf;
use std::sync::Arc;

use crate::config::Config;
use crate::core::{
    archive::ArchivePack,
    cache::CacheManager,
    download::DownloadManager,
    lock_file::{DEFAULT_LOCKFILE_NAME, LockFile},
    verify,
};
use crate::types::platform::Platform;

pub struct App {
    platform: Platform,
    lock_file: LockFile,
    download_manager: Arc<DownloadManager>,
    cache_manager: Arc<CacheManager>,
}

impl App {
    pub fn new(
        platform: Platform,
        lock_file: LockFile,
        download_manager: Arc<DownloadManager>,
        cache_manager: Arc<CacheManager>,
    ) -> Self {
        Self {
            platform,
            lock_file,
            download_manager,
            cache_manager,
        }
    }

    pub fn process_config(&mut self, config: Config) -> Result<()> {
        let Some(platform_config) = config.platforms.get(&self.platform) else {
            bail!(
                "Warning: platform {} not configured for {}, skipping",
                self.platform,
                config.name
            );
        };

        if self.lock_file.is_locked(self.platform, &config) {
            eprintln!("Config for {} not changed, skipping", config.name);
            return Ok(());
        }

        let downloaded_path = self
            .download_manager
            .download(&platform_config.url, &config.name)
            .with_context(|| format!("Failed to download {}, skipping", config.name))?;

        if let Some(ref hash_config) = platform_config.hash {
            let verify_result = verify::verify_file(&downloaded_path, hash_config)
                .with_context(|| format!("Failed to verify {}, skipping", config.name))?;
            if !verify_result {
                bail!(
                    "Hash verification failed for {}: {} mismatch, skipping",
                    config.name,
                    hash_config.algorithm
                );
            }
        }

        let output_files = if let Some(archive_type) = platform_config.archive {
            let extracted = ArchivePack::new(
                archive_type,
                downloaded_path.clone(),
                platform_config.root.clone(),
            )
            .extract(&config.target)
            .with_context(|| format!("failed to extract {}", downloaded_path.display()))?;
            extracted
        } else {
            let target_path = &config.target;
            fs::create_dir_all(target_path)?;

            let target_file_name = downloaded_path
                .file_name()
                .and_then(|name| name.to_str())
                .ok_or_else(|| anyhow::anyhow!("invalid download file name"))?;
            let target_file_path = target_path.join(target_file_name);
            fs::copy(&downloaded_path, &target_file_path)?;
            vec![PathBuf::from(target_file_name)]
        };

        self.lock_file
            .lock(&config.name, output_files, self.platform, platform_config);

        return Ok(());
    }

    pub fn save(&self) -> Result<()> {
        let lock_file_path = self.cache_manager.path_for(DEFAULT_LOCKFILE_NAME);
        self.lock_file.save(&lock_file_path)?;
        return Ok(());
    }
}