use clap::{Parser, Subcommand}; use libips::actions::{File, Manifest}; use anyhow::Result; use std::collections::HashMap; use std::fs::{read_dir}; use std::path::Path; use userland::Makefile; use userland::repology::{find_newest_version}; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] #[clap(propagate_version = true)] struct App { #[clap(subcommand)] command: Commands } #[derive(Subcommand, Debug)] enum Commands { DiffComponent{ component: String, }, ShowComponent{ component: String, }, } fn main() -> Result<()> { let cli = App::parse(); match &cli.command { Commands::ShowComponent{ component } => { show_component_info(component) } Commands::DiffComponent{component} => { diff_component(component) } } } fn diff_component(component_path: impl AsRef) -> Result<()> { let files = read_dir(&component_path)?; let manifest_files: Vec = files .filter_map(std::result::Result::ok) .filter(|d| if let Some(e) = d.path().extension() { e == "p5m" } else {false}) .map(|e| e.path().into_os_string().into_string().unwrap()).collect(); let sample_manifest_file = &component_path.as_ref().join("/manifests/sample-manifest.p5m"); let manifests_res: Result> = manifest_files.iter().map(|f|{ Manifest::parse_file(f.to_string()) }).collect(); let sample_manifest = Manifest::parse_file(sample_manifest_file)?; let manifests: Vec = manifests_res.unwrap(); let missing_files = find_files_missing_in_manifests(&sample_manifest, manifests.clone())?; for f in missing_files { println!("file {} is missing in the manifests", f.path); } let removed_files = find_removed_files(&sample_manifest, manifests.clone(), &component_path)?; for f in removed_files { println!("file path={} has been removed from the sample-manifest", f.path); } Ok(()) } fn show_component_info>(component_path: P) -> Result<()> { let makefile_path = component_path.as_ref().join("Makefile"); let initial_makefile = Makefile::parse_single_file(&makefile_path)?; let makefile = initial_makefile.parse_all()?; let mut name = String::new(); if let Some(var) = makefile.get("COMPONENT_NAME") { println!("Name: {}", var.replace("\n", "\n\t")); if let Some(component_name) = makefile.get_first_value_of_variable_by_name("COMPONENT_NAME") { name = component_name.clone(); } } if let Some(var) = makefile.get("COMPONENT_VERSION") { println!("Version: {}", var.replace("\n", "\n\t")); let latest_version = find_newest_version(&name); if latest_version.is_ok() { println!("Latest Version: {}", latest_version?); } else { println!("Error: Could not get latest version info: {:?}", latest_version.unwrap_err()) } } if let Some(var) = makefile.get("BUILD_BITS") { println!("Build bits: {}", var.replace("\n", "\n\t")); } if let Some(var) = makefile.get("COMPONENT_BUILD_ACTION") { println!("Build action: {}", var.replace("\n", "\n\t")); } if let Some(var) = makefile.get("COMPONENT_PROJECT_URL") { println!("Project URl: {}", var.replace("\n", "\n\t")); } if let Some(var) = makefile.get("COMPONENT_ARCHIVE_URL") { println!("Source URl: {}", var.replace("\n", "\n\t")); } if let Some(var) = makefile.get("COMPONENT_ARCHIVE_HASH") { println!("Source Archive File Hash: {}", var.replace("\n", "\n\t")); } if let Some(var) = makefile.get("CONFIGURE_ENV") { println!("Configure Environment: {}", var.replace("\n", "\n\t")); } if let Some(var) = makefile.get("CONFIGURE_OPTIONS") { println!("./configure {}", var.replace("\n", "\n\t")); } if let Some(var) = makefile.get("REQUIRED_PACKAGES") { println!("Dependencies:\n\t{}", var.replace("\n", "\n\t")); } if let Some(var) = makefile.get("COMPONENT_INSTALL_ACTION") { println!("Install Action:\n\t{}", var); } Ok(()) } // Show all files that have been removed in the sample-manifest fn find_removed_files>(sample_manifest: &Manifest, manifests: Vec, component_path: P) -> Result> { let f_map = make_file_map(sample_manifest.files.clone()); let all_files: Vec = manifests.iter().map(|m| m.files.clone()).flatten().collect(); let mut removed_files: Vec = Vec::new(); for f in all_files { match f.get_original_path() { Some(path) => { if !f_map.contains_key(path.as_str()) { if !component_path.as_ref().join(path).exists() { removed_files.push(f) } } }, None => { if !f_map.contains_key(f.path.as_str()) { removed_files.push(f) } } } } Ok(removed_files) } // Show all files missing in the manifests that are in sample_manifest fn find_files_missing_in_manifests(sample_manifest: &Manifest, manifests: Vec) -> Result> { let all_files: Vec = manifests.iter().map(|m| m.files.clone()).flatten().collect(); let f_map = make_file_map(all_files); let mut missing_files: Vec = Vec::new(); for f in sample_manifest.files.clone() { match f.get_original_path() { Some(path) => { if !f_map.contains_key(path.as_str()) { missing_files.push(f) } }, None => { if !f_map.contains_key(f.path.as_str()) { missing_files.push(f) } } } } Ok(missing_files) } fn make_file_map(files: Vec) -> HashMap { files.iter().map(|f| { let orig_path_opt = f.get_original_path(); if orig_path_opt == None { return (f.path.clone(), f.clone()); } (orig_path_opt.unwrap(), f.clone()) }).collect() }