From acf634fd2c59859ca5cf3fc31cc3e63256258526 Mon Sep 17 00:00:00 2001 From: aisuneko icecat Date: Mon, 14 Oct 2024 00:10:05 +0800 Subject: [PATCH] feat: auto-detect distros and packages (#29) --- Cargo.lock | 2 +- src/config/distro_config.rs | 1 + src/config/mod.rs | 1 - src/config/root_config.rs | 32 --------------------- src/main.rs | 31 ++++++++++---------- src/utils.rs | 56 ++++++++++++++++++++++++++++++++++++- 6 files changed, 71 insertions(+), 52 deletions(-) delete mode 100644 src/config/root_config.rs diff --git a/Cargo.lock b/Cargo.lock index e9a6dfe..845b1bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,7 +244,7 @@ dependencies = [ [[package]] name = "lintestor" -version = "0.1.0" +version = "0.1.2" dependencies = [ "clap", "env_logger", diff --git a/src/config/distro_config.rs b/src/config/distro_config.rs index afc6e31..45795ee 100644 --- a/src/config/distro_config.rs +++ b/src/config/distro_config.rs @@ -12,6 +12,7 @@ use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct DistroConfig { + pub enabled: bool, pub testing_type: String, // 'locally' or 'remote' or 'qemu-based-remote' #[serde(rename = "startup_script")] #[serde(default, skip_serializing_if = "is_not_qemu_based_remote")] diff --git a/src/config/mod.rs b/src/config/mod.rs index 44f461b..413ca0b 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,3 +1,2 @@ pub mod connection_config; pub mod distro_config; -pub mod root_config; diff --git a/src/config/root_config.rs b/src/config/root_config.rs deleted file mode 100644 index cdeafd0..0000000 --- a/src/config/root_config.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Represents the root configuration for the application. -/// -/// This struct is used to deserialize the configuration from a file using the `utils::read_toml_from_file` method. -/// It contains two fields: -/// - `distros`: A vector of strings representing the supported distributions. -/// - `packages`: A vector of strings representing the packages to be installed. -/// -/// # Example -/// -/// ``` -/// use lintestor::config::root_config::Config; -/// -/// let config: Config = utils::read_toml_from_file("/path/to/config.toml"); -/// match config { -/// Ok(config) => { -/// // Use the config object -/// println!("{:?}", config); -/// } -/// Err(err) => { -/// // Handle the error -/// eprintln!("Failed to load config: {}", err); -/// } -/// } -/// ``` -use serde::Deserialize; - -#[derive(Debug, Deserialize)] - -pub struct Config { - pub distros: Vec, - pub packages: Vec, -} diff --git a/src/main.rs b/src/main.rs index 425bc71..fb4db43 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ mod testenv_manager; mod testscript_manager; mod utils; -use crate::config::{distro_config::DistroConfig, root_config::Config}; +use crate::config::distro_config::DistroConfig; use crate::test_runner::{local::LocalTestRunner, remote::RemoteTestRunner, TestRunner}; use clap::{Arg, ArgAction, ArgMatches, Command}; use log::{debug, error, info, warn}; @@ -35,27 +35,24 @@ fn main() { let aggr = matches.get_flag("aggr"); let summ = matches.get_flag("summ"); let skip_successful = matches.get_flag("skip-successful"); - let config_file = matches - .get_one::("config") + let cwd = env::current_dir().unwrap(); + let working_directory = matches + .get_one::("directory") .map(|s| s.as_str()) - .unwrap_or("config.toml"); - let base_config: Config = match utils::read_toml_from_file(config_file) { - Ok(base_config) => base_config, - Err(e) => { - error!("Failed to load config from {}: {}", config_file, e); - return; - } - }; + .unwrap_or(cwd.to_str().unwrap()); + let discovered_distros = utils::get_distros(working_directory).unwrap_or_default(); let distros: Vec<&str> = matches .get_one::("distro") .map(|s| s.as_str().split(',').collect::>()) - .unwrap_or(base_config.distros.iter().map(|s| &**s).collect()); + .unwrap_or(discovered_distros.iter().map(|s| s.as_str()).collect()); debug!("Distros: {:?}", distros); + let discovered_packages = + utils::get_all_packages(&distros, working_directory).unwrap_or_default(); let packages: Vec<&str> = matches .get_one::("package") .map(|s| s.as_str().split(',').collect::>()) - .unwrap_or(base_config.packages.iter().map(|s| &**s).collect()); + .unwrap_or(discovered_packages.iter().map(|s| s.as_str()).collect()); debug!("Packages: {:?}", packages); if test { @@ -104,10 +101,10 @@ fn parse_args() -> ArgMatches { .help("Generate a summary report"), ) .arg( - Arg::new("config") - .long("config") - .value_name("Config file name") - .help("Specify a different base configuration file"), + Arg::new("directory") + .long("directory") + .value_name("Working directory") + .help("Specify working directory with preconfigured test files"), ) .arg( Arg::new("distro") diff --git a/src/utils.rs b/src/utils.rs index da903c0..7f3a220 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,8 +3,11 @@ //! This module provides common structures and utilities used across the project, //! including report structures, temporary file management, and command output handling. +use log::debug; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::{error::Error, fs}; +use std::{collections::HashSet, error::Error, fs, path::Path}; + +use crate::config::distro_config::DistroConfig; /// The remote temporary directory used for operations. pub static REMOTE_TMP_DIR: &str = "/tmp/lintestor"; @@ -115,3 +118,54 @@ where let config: T = toml::de::from_str(&content)?; Ok(config) } + +pub fn get_distros(dir: &str) -> Result, Box> { + let directory = Path::new(dir); + let mut distros = Vec::new(); + for subdir in directory.read_dir()? { + let distro = subdir?; + let distro_dir_path = distro.path(); + if distro_dir_path.is_dir() { + let distro_dir_name = distro.file_name().into_string().unwrap(); + let distro_config_path = format!("{}/config.toml", distro_dir_path.display()); + let distro_config: DistroConfig = match read_toml_from_file(&distro_config_path) { + Ok(config) => { + debug!("Discovered distro directory {}", distro_dir_path.display()); + config + } + Err(_) => { + continue; + } + }; + if distro_config.enabled { + distros.push(distro_dir_name); + } + } + } + Ok(distros) +} + +pub fn get_packages(distro: &str, dir: &str) -> Result, Box> { + let directory = Path::new(dir).join(distro); + let mut packages = Vec::new(); + for subdir in directory.read_dir()? { + let package = subdir?; + let package_dir_path = package.path(); + if package_dir_path.is_dir() { + let package_dir_name = package.file_name().into_string().unwrap(); + packages.push(package_dir_name); + } + } + Ok(packages) +} + +pub fn get_all_packages(distros: &[&str], dir: &str) -> Result, Box> { + let mut packages = HashSet::new(); + for distro in distros { + let current_packages = get_packages(distro, dir).unwrap_or_default(); + packages.extend(current_packages); + } + let mut packages_vec: Vec = packages.into_iter().collect(); + packages_vec.sort(); // do we really need sorting? + Ok(packages_vec) +}