feat: added update and list flags
This commit is contained in:
@@ -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
52
src/functions/list.rs
Normal 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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
35
src/main.rs
35
src/main.rs
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
37
src/opts.rs
37
src/opts.rs
@@ -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>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user