feat: added update and list flags

This commit is contained in:
Byson94
2025-08-15 22:29:37 +05:30
parent 8b63288da4
commit b712c0b1ff
7 changed files with 239 additions and 99 deletions

View File

@@ -2,26 +2,12 @@ use colored::{Colorize};
use dirs;
use log::{debug, info, trace};
use reqwest::blocking::get;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use serde::{Deserialize};
use std::env;
use std::error::Error;
use std::fs;
use std::process::Command;
const DB_FILE: &str = "./eiipm/installed.toml";
#[derive(Deserialize, Serialize, Debug)]
struct PackageDB {
packages: HashMap<String, InstalledPackage>,
}
#[derive(Deserialize, Serialize, Debug)]
struct InstalledPackage {
repo_path: String,
files: Vec<String>,
pkg_type: String,
}
use super::{InstalledPackage, save_db, load_db};
#[derive(Deserialize, Debug)]
struct PackageRootMeta {
@@ -51,16 +37,16 @@ pub fn install_package(package_name: &str) -> Result<(), Box<dyn Error>> {
let meta = &root_meta.metadata;
let home_dir = dirs::home_dir().ok_or("Failed to get home directory")?;
let eiipm_dir = home_dir.join("./.eiipm");
let eiipm_dir = home_dir.join(".eiipm");
fs::create_dir_all(&eiipm_dir)?;
let repo_name = meta
.src
.split('/')
.last()
.rsplit('/')
.next()
.ok_or("Invalid src URL")?
.strip_suffix(".git")
.unwrap_or_else(|| meta.src.split('/').last().unwrap());
.unwrap_or_else(|| meta.src.rsplit('/').next().unwrap());
let repo_path = eiipm_dir.join(format!("cache/{}", repo_name));
@@ -133,6 +119,7 @@ pub fn install_package(package_name: &str) -> Result<(), Box<dyn Error>> {
repo_path: repo_path.to_string_lossy().to_string(),
files: installed_files,
pkg_type: meta.pkg_type.clone(),
build_command: meta.build.clone(),
},
);
save_db(&db)?;
@@ -141,29 +128,6 @@ pub fn install_package(package_name: &str) -> Result<(), Box<dyn Error>> {
Ok(())
}
fn load_db() -> Result<PackageDB, Box<dyn Error>> {
let home_dir = dirs::home_dir().ok_or("Failed to get home directory")?;
let db_path = home_dir.join(DB_FILE);
if db_path.exists() {
let content = fs::read_to_string(&db_path)?;
let db: PackageDB = toml::from_str(&content)?;
Ok(db)
} else {
Ok(PackageDB { packages: HashMap::new() })
}
}
fn save_db(db: &PackageDB) -> Result<(), Box<dyn Error>> {
let home_dir = dirs::home_dir().ok_or("Failed to get home directory")?;
let db_path = home_dir.join(DB_FILE);
if let Some(parent) = db_path.parent() {
fs::create_dir_all(parent)?;
}
let content = toml::to_string_pretty(db)?;
fs::write(db_path, content)?;
Ok(())
}
fn http_get_string(url: &str) -> Result<String, Box<dyn Error>> {
debug!("Sending GET request to {}", url);
let response = get(url)?;

52
src/functions/list.rs Normal file
View File

@@ -0,0 +1,52 @@
use super::load_db;
use log::{info, error};
use std::error::Error;
use crate::opts::ListArgs;
pub fn list_packages(list_args: ListArgs) -> Result<(), Box<dyn Error>> {
let db = load_db()?;
if list_args.total_count {
info!("Total: {}", db.packages.len());
return Ok(());
}
if let Some(pkg) = list_args.query {
// Find the package in the DB
if let Some(package) = db.packages.get(&pkg) {
if list_args.verbose {
info!(
"{}\n Type: {}\n Repo: {}\n Build: {}\n Files:\n {}",
pkg,
package.pkg_type,
package.repo_path,
package.build_command.clone().unwrap_or_else(|| "None".into()),
package.files.join("\n ")
);
} else {
info!("{}", pkg);
}
} else {
error!("Package '{}' not found", pkg);
}
return Ok(());
}
// If no query, list all packages
for (name, package) in &db.packages {
if list_args.verbose {
info!(
"{}\n Type: {}\n Repo: {}\n Build: {}\n Files:\n {}",
name,
package.pkg_type,
package.repo_path,
package.build_command.clone().unwrap_or_else(|| "None".into()),
package.files.join("\n ")
);
} else {
info!("{}", name);
}
}
Ok(())
}

View File

@@ -1,2 +1,48 @@
pub mod install;
pub mod uninstall;
pub mod uninstall;
pub mod update;
pub mod list;
use std::fs;
use std::error::Error;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use dirs;
pub const DB_FILE: &str = ".local/share/eiipm/installed.toml";
#[derive(Deserialize, Serialize, Debug)]
pub struct PackageDB {
packages: HashMap<String, InstalledPackage>,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct InstalledPackage {
repo_path: String,
files: Vec<String>,
pkg_type: String,
build_command: Option<String>,
}
pub fn load_db() -> Result<PackageDB, Box<dyn Error>> {
let home_dir = dirs::home_dir().ok_or("Failed to get home directory")?;
let db_path = home_dir.join(DB_FILE);
if db_path.exists() {
let content = fs::read_to_string(&db_path)?;
let db: PackageDB = toml::from_str(&content)?;
Ok(db)
} else {
Ok(PackageDB { packages: HashMap::new() })
}
}
pub fn save_db(db: &PackageDB) -> Result<(), Box<dyn Error>> {
let home_dir = dirs::home_dir().ok_or("Failed to get home directory")?;
let db_path = home_dir.join(DB_FILE);
if let Some(parent) = db_path.parent() {
fs::create_dir_all(parent)?;
}
let content = toml::to_string_pretty(db)?;
fs::write(db_path, content)?;
Ok(())
}

View File

@@ -1,25 +1,9 @@
use colored::Colorize;
use dirs;
use log::{info};
use std::collections::HashMap;
use std::error::Error;
use std::fs;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
const DB_FILE: &str = ".config/eiipm/installed.toml";
#[derive(Deserialize, Serialize, Debug)]
struct PackageDB {
packages: HashMap<String, InstalledPackage>,
}
#[derive(Deserialize, Serialize, Debug)]
struct InstalledPackage {
repo_path: String,
files: Vec<String>,
pkg_type: String,
}
use super::{load_db, save_db};
pub fn uninstall_package(package_name: &str) -> Result<(), Box<dyn Error>> {
info!("> Uninstalling package '{}'", package_name.yellow().bold());
@@ -40,27 +24,4 @@ pub fn uninstall_package(package_name: &str) -> Result<(), Box<dyn Error>> {
}
Ok(())
}
fn load_db() -> Result<PackageDB, Box<dyn Error>> {
let home_dir = dirs::home_dir().ok_or("Failed to get home directory")?;
let db_path = home_dir.join(DB_FILE);
if db_path.exists() {
let content = fs::read_to_string(&db_path)?;
let db: PackageDB = toml::from_str(&content)?;
Ok(db)
} else {
Ok(PackageDB { packages: HashMap::new() })
}
}
fn save_db(db: &PackageDB) -> Result<(), Box<dyn Error>> {
let home_dir = dirs::home_dir().ok_or("Failed to get home directory")?;
let db_path = home_dir.join(DB_FILE);
if let Some(parent) = db_path.parent() {
fs::create_dir_all(parent)?;
}
let content = toml::to_string_pretty(db)?;
fs::write(db_path, content)?;
Ok(())
}
}

View File

@@ -0,0 +1,73 @@
use super::{InstalledPackage, save_db, load_db};
use log::info;
use std::error::Error;
use std::path::PathBuf;
use std::fs;
use std::process::Command;
use colored::Colorize;
pub fn update_package(package_name: Option<String>) -> Result<(), Box<dyn Error>> {
let mut db = load_db()?;
if let Some(name) = package_name {
if let Some(pkg) = db.packages.get_mut(&name) {
info!("> Updating package '{}'", name.yellow().bold());
update_file(pkg)?;
info!("Successfully updated '{}'", name.yellow().bold());
} else {
info!("Package '{}' not found in database", name.yellow());
}
} else {
info!("> Updating all packages...");
for (name, pkg) in db.packages.iter_mut() {
info!("Updating '{}'", name.yellow().bold());
update_file(pkg)?;
}
}
save_db(&db)?;
Ok(())
}
fn update_file(pkg: &mut InstalledPackage) -> Result<(), Box<dyn Error>> {
let repo_path = PathBuf::from(&pkg.repo_path);
// Pull latest changes
let output = Command::new("git")
.args(&["-C", repo_path.to_str().unwrap(), "pull"])
.output()?;
if !output.status.success() {
return Err(format!(
"Git pull failed: {}",
String::from_utf8_lossy(&output.stderr)
).into());
}
// Optional build step
if let Some(build_cmd) = &pkg.build_command {
info!("Running build command: {}", build_cmd);
let status = Command::new("sh")
.arg("-c")
.arg(build_cmd)
.current_dir(&repo_path)
.status()?;
if !status.success() {
return Err(format!("Build failed for package '{}'", pkg.repo_path).into());
}
}
// Copy updated files to targets
for file in &pkg.files {
let source = repo_path.join(
PathBuf::from(file)
.file_name()
.ok_or("Invalid filename")?,
);
let target = PathBuf::from(file);
if source.exists() {
fs::copy(&source, &target)?;
}
}
Ok(())
}

View File

@@ -1,30 +1,47 @@
mod opts;
mod functions;
use opts::{Args, Commands};
use functions::install::install_package;
use functions::uninstall::uninstall_package;
use opts::{PMArgs, Commands};
use functions::{
install::install_package,
uninstall::uninstall_package,
update::update_package,
list::list_packages
};
use clap::Parser;
use log::Level;
use log::{info, error, Level};
fn main() {
let args = Args::parse();
let args = PMArgs::parse();
set_debug_levels(args.debug);
if args.debug {
log::info!("Debug logging enabled");
set_debug_levels(true);
info!("Debug logging enabled");
}
match args.command {
Commands::Install { package } => {
if let Err(e) = install_package(&package) {
log::error!("Error installing '{}': {}", package, e);
error!("Error installing '{}': {}", package, e);
}
}
Commands::Uninstall { package } => {
if let Err(e) = uninstall_package(&package) {
log::error!("Error uninstalling '{}': {}", package, e);
error!("Error uninstalling '{}': {}", package, e);
}
}
Commands::Update { package } => {
if Some(&package).is_some() {
let _ = update_package(package);
} else {
let _ = update_package(None);
}
}
Commands::List(list_args) => {
if let Err(e) = list_packages(list_args) {
error!("Error listing packages: {}", e);
}
}
}

View File

@@ -1,11 +1,12 @@
use clap::{Parser, Subcommand};
use clap::{Parser, Subcommand, Args};
/// Eiipm package manager for ewwii.
#[derive(Parser, Debug)]
#[command(version, about)]
pub struct Args {
#[command(author, version, about)]
#[command(arg_required_else_help = true)]
pub struct PMArgs {
/// Show debug logs
#[arg(long)]
#[arg(long, global = true)]
pub debug: bool,
#[command(subcommand)]
@@ -15,13 +16,39 @@ pub struct Args {
#[derive(Subcommand, Debug)]
pub enum Commands {
/// Install a package
#[command(alias = "i")]
Install {
/// Name of the package to install
package: String,
},
/// Uninstall a package
#[command(alias = "rm")]
Uninstall {
/// Name of the package to uninstall
package: String,
},
}
/// Update a package or all packages
#[command(alias = "up")]
Update {
/// Name of the package to update. Updates all if not provided.
package: Option<String>,
},
/// List all installed packages
#[command(alias = "l")]
List(ListArgs),
}
#[derive(Args, Debug)]
pub struct ListArgs {
/// Verbose output
#[arg(long, short = 'v')]
pub verbose: bool,
/// Output just the total package count
#[arg(long, short = 't')]
pub total_count: bool,
/// Query a package
#[arg(short, long)]
pub query: Option<String>,
}