Skip to content

Commit

Permalink
Added package tracking and list command
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanpoland committed Jan 1, 2025
1 parent 89ec7b1 commit d46c5ec
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 86 deletions.
10 changes: 5 additions & 5 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
use clap::{Parser, Subcommand};

#[derive(Debug, Parser,Clone)]
#[derive(Debug, Parser)]
#[command(author, version, about = "GitHub Release Package Manager")]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
#[arg(short,long)]
pub debug: bool
}

#[derive(Debug, Subcommand,Clone)]
#[derive(Debug, Subcommand)]
pub enum Commands {
/// Install a package
Install {
Expand All @@ -29,9 +27,11 @@ pub enum Commands {
#[command(subcommand)]
cmd: RegistryCommands,
},
/// List installed packages
List,
}

#[derive(Debug, Subcommand,Clone)]
#[derive(Debug, Subcommand)]
pub enum RegistryCommands {
/// Add a new registry
Add {
Expand Down
1 change: 1 addition & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use crate::error::Result;

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down
163 changes: 107 additions & 56 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,75 @@ mod path;
mod registry;
mod utils;

use std::sync::Arc;
use std::fs::rename;
use std::collections::HashMap;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};

use clap::Parser;
use cli::{Cli, Commands, RegistryCommands};
use colored::Colorize;
use config::Config;
use dialoguer::Select;
use error::Result;
use lazy_static::lazy_static;
use registry::RegistryManager;
use tokio::fs::{set_permissions, File};

#[derive(Serialize, Deserialize, Debug)]
pub struct InstalledPackage {
pub version: String,
pub install_path: PathBuf,
pub executable_path: Option<PathBuf>,
}

#[derive(Serialize, Deserialize, Debug, Default)]
pub struct PackageState {
packages: HashMap<String, InstalledPackage>,
}

impl PackageState {
pub fn load(data_dir: &PathBuf) -> Result<Self> {
let state_file = data_dir.join("package_state.json");
if state_file.exists() {
let content = std::fs::read_to_string(state_file)?;
Ok(serde_json::from_str(&content)?)
} else {
Ok(Self::default())
}
}

pub fn save(&self, data_dir: &PathBuf) -> Result<()> {
let state_file = data_dir.join("package_state.json");
let content = serde_json::to_string_pretty(self)?;
std::fs::write(state_file, content)?;
Ok(())
}

pub fn add_package(&mut self, name: String, version: String, install_path: PathBuf, executable_path: Option<PathBuf>) {
self.packages.insert(name, InstalledPackage {
version,
install_path,
executable_path,
});
}

pub fn remove_package(&mut self, name: &str) -> Option<InstalledPackage> {
self.packages.remove(name)
}

pub fn get_package(&self, name: &str) -> Option<&InstalledPackage> {
self.packages.get(name)
}

pub fn list_packages(&self) -> Vec<(&String, &InstalledPackage)> {
self.packages.iter().collect()
}
}

struct Grip {
config: Config,
registry_manager: RegistryManager,
}
lazy_static! {
pub static ref CLI: Arc<Cli> = Arc::new(Cli::parse());
package_state: PackageState,
}

impl Grip {
Expand All @@ -34,23 +86,24 @@ impl Grip {
std::fs::create_dir_all(&data_dir)?;

let config = Config::load()?;
let registry_manager = RegistryManager::new(data_dir);
let registry_manager = RegistryManager::new(data_dir.clone());
let package_state = PackageState::load(&data_dir)?;

Ok(Self {
config,
registry_manager,
package_state,
})
}

async fn install(
&self,
&mut self,
package_name: &str,
version: Option<String>,
asset: Option<String>,
) -> Result<()> {
println!("{} Looking up package {}", "→".blue(), package_name.cyan());

// Find package in registry
let package = self
.registry_manager
.find_package(&self.config.registries, package_name)
Expand All @@ -62,7 +115,6 @@ impl Grip {
package.info.repository.cyan()
);

// Get releases from GitHub
let releases = self
.registry_manager
.get_releases(&package.info.repository)
Expand All @@ -72,7 +124,6 @@ impl Grip {
anyhow::bail!("No releases found for package '{}'", package_name);
}

// Select release version
let release = match version {
Some(ref v) => releases
.iter()
Expand All @@ -95,7 +146,6 @@ impl Grip {
}
};

// Select asset
let assets = release["assets"]
.as_array()
.ok_or_else(|| anyhow::anyhow!("No assets found in release"))?;
Expand Down Expand Up @@ -128,68 +178,56 @@ impl Grip {
.as_str()
.ok_or_else(|| anyhow::anyhow!("Invalid asset name"))?;

// Download and install
let target_dir = self
.registry_manager
.data_dir
.join("packages")
.join(package_name)
.join(release["tag_name"].as_str().unwrap_or("unknown"));

let downloaded_file = self
let mut downloaded_file = self
.registry_manager
.download_asset(download_url, filename, &target_dir)
.await?;
let mut binary_name: Option<String> = None;
#[cfg(target_os = "windows")]
if package.info.executable_name.is_some() {

let original_name = asset_obj["name"].to_string();
let path = std::path::Path::new(&original_name);
let extension = path.extension().unwrap_or_default().to_string_lossy();
binary_name = Some(format!("{}.{}", package_name, extension));
}
// Handle archive extraction if needed
if filename.ends_with(".zip") || filename.ends_with(".tar.gz") || filename.ends_with(".tgz")
{

if filename.ends_with(".zip") || filename.ends_with(".tar.gz") || filename.ends_with(".tgz") {
println!("{} Extracting archive...", "→".blue());
utils::extract_archive(&downloaded_file, &target_dir).await?;
utils::extract_archive(&downloaded_file, &target_dir);
println!("{} Extracted to {:?}", "✓".green(), target_dir);
if package.info.executable_name.is_some() {
println!(" tesdafsdfasgsgeg3544ttwrergtdhyrwraesd{}",package.info.executable_name.clone().unwrap());
std::fs::rename(&downloaded_file, target_dir.join(package.info.executable_name.clone().unwrap()))?;
} else {
println!("no exe name")
}
// Clean up archive after extraction
std::fs::remove_file(downloaded_file)?;
} else {
if package.info.executable_name.is_some() {
println!(" tesdafsdfasgsgeg3544ttwrergtdhyrwraesd{}",package.info.executable_name.clone().unwrap());
let path = downloaded_file.parent().unwrap();
let name = binary_name.unwrap();
let name = name.trim_matches('"');
let bin_path = path.join(name);
println!("renaming {} to {}", downloaded_file.display(), bin_path.display());
std::fs::copy(downloaded_file, bin_path)?;
} else {
println!("no zip and no exe name")
if let Some(executable_name) = package.info.executable_name.clone() {
let mut new_pathbuf = downloaded_file.clone();
new_pathbuf.set_file_name(executable_name);
new_pathbuf.set_extension(downloaded_file.extension().unwrap());
rename(downloaded_file, new_pathbuf)?;
}
}

// Add to PATH if needed
path::add_to_path(&target_dir).await?;

let executable_path = if let Some(executable_name) = package.info.executable_name {
Some(target_dir.join(executable_name))
} else {
None
};

self.package_state.add_package(
package_name.to_string(),
release["tag_name"].as_str().unwrap_or("unknown").to_string(),
target_dir.clone(),
executable_path,
);

self.package_state.save(&self.registry_manager.data_dir)?;

println!("{} Installation complete!", "✓".green());
Ok(())
}

async fn handle_registry_command(&mut self, cmd: RegistryCommands) -> Result<()> {
match cmd {
RegistryCommands::Add {
name,
url,
priority,
} => {
RegistryCommands::Add { name, url, priority } => {
if self.config.registries.iter().any(|r| r.name == name) {
anyhow::bail!("Registry '{}' already exists", name);
}
Expand Down Expand Up @@ -217,7 +255,6 @@ impl Grip {

self.config.save()?;

// Remove cached registry
let registry_path = self
.registry_manager
.data_dir
Expand Down Expand Up @@ -257,16 +294,27 @@ impl Grip {
println!("{} Created grip.json", "✓".green());
Ok(())
}

async fn list_packages(&self) -> Result<()> {
println!("{} Installed packages:", "→".blue());
for (name, package) in self.package_state.list_packages() {
println!(
" {} {} (version: {})",
"→".blue(),
name.cyan(),
package.version
);
}
Ok(())
}
}

#[tokio::main]
async fn main() -> Result<()> {
let cli = Arc::clone(&CLI);

print_debug!("Cli.debug == true");
let cli = Cli::parse();
let mut grip = Grip::new().await?;
let command = cli.command.clone();
match command {

match cli.command {
Commands::Install {
package,
version,
Expand All @@ -280,7 +328,10 @@ async fn main() -> Result<()> {
Commands::Init => {
grip.init().await?;
}
Commands::List => {
grip.list_packages().await?;
}
}

Ok(())
}
}
6 changes: 6 additions & 0 deletions src/path.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use crate::error::Result;
use colored::Colorize;
use std::path::Path;

#[cfg(windows)]
pub async fn add_to_path(path: &Path) -> Result<()> {
use winreg::enums::*;
use winreg::RegKey;

println!("{} Adding packages directory to PATH...", "→".blue());

// Open the environment key
Expand All @@ -28,6 +30,7 @@ pub async fn add_to_path(path: &Path) -> Result<()> {

environment.set_value("Path", &new_path)
.map_err(|e| anyhow::anyhow!("Failed to update PATH: {}", e))?;

// Notify Windows of the environment change
unsafe {
use winapi::um::winuser::{HWND_BROADCAST, WM_SETTINGCHANGE, SMTO_ABORTIFHUNG, SendMessageTimeoutW};
Expand All @@ -53,6 +56,7 @@ pub async fn add_to_path(path: &Path) -> Result<()> {

Ok(())
}

#[cfg(unix)]
pub async fn add_to_path(path: &Path) -> Result<()> {
use std::env;
Expand All @@ -70,6 +74,7 @@ pub async fn add_to_path(path: &Path) -> Result<()> {
} else {
format!("{}/.profile", home)
};

let export_line = format!("\nexport PATH=\"{}:$PATH\"", path.to_string_lossy());

let rc_content = std::fs::read_to_string(&shell_rc)
Expand All @@ -81,6 +86,7 @@ pub async fn add_to_path(path: &Path) -> Result<()> {
.create(true)
.open(&shell_rc)?
.write_all(export_line.as_bytes())?;

println!("{} Added to PATH in {}", "✓".green(), shell_rc);
println!("{} Run 'source {}' or restart your terminal for changes to take effect", "!".yellow(), shell_rc);
} else {
Expand Down
Loading

0 comments on commit d46c5ec

Please sign in to comment.