use super::config::parse_local_config;
use crate::commands::ui::{ASCII, BAR, RESET};
use crate::engine::config::metadata_config_structure::MetadataInfo;
use crate::engine::config::pkg_config_structure::PackageInfo;
use crate::engine::msgx::info;
use anyhow::anyhow;
use anyhow::{Context, Result};
use colored::Colorize;
use columns::Columns;
use sha2::{Digest, Sha256};
use std::fs;
use std::io::{self, Write};
use std::process::Command;
use tokio::{fs::File, io::AsyncReadExt};
#[inline]
fn format_field(field: &str) -> String {
if field.is_empty() {
"Not available".to_string()
} else {
field.to_string()
}
}
#[inline]
pub fn remove_trailing_slash(mut path: String) -> String {
if path.ends_with('/') {
path.pop();
}
path
}
#[inline]
pub async fn read_file_content(file_path: &str) -> Result<String> {
let mut file = File::open(file_path)
.await
.with_context(|| format!("Failed to open file '{}'", file_path))?;
let mut contents = String::new();
file.read_to_string(&mut contents)
.await
.with_context(|| format!("Failed to read file '{}'", file_path))?;
Ok(contents)
}
#[inline]
pub fn get_arch() -> String {
let architecture = std::env::consts::ARCH.to_string();
match architecture.as_str() {
"x86_64" => "x86_64".to_string(),
"aarch64" => "aarch64".to_string(),
_ => {
eprintln!("Error: Unsupported architecture");
std::process::exit(1);
}
}
}
#[inline]
pub fn prompt_yn(prompt: String) -> bool {
loop {
print!("{}", prompt);
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Failed to read line");
let trimmed = input.trim().to_lowercase();
match trimmed.as_str() {
"yes" | "y" => {
info("Proceeding ...", colored::Color::Green);
return true;
}
"no" | "n" => {
info("Exitting .. ", colored::Color::Red);
std::process::exit(1);
}
_ => {
println!("Please enter 'y|es' or 'n|o'.");
}
}
}
}
#[inline]
pub fn print_package_info(pkginfo: PackageInfo) {
let pkg_bin_name = &pkginfo.bin.name.bold().bright_red();
let pkg_architecture = &pkginfo.package.architecture.green();
let pkg_hash = &pkginfo.package.sha.bold().cyan();
let pkg_desc = format_field(&pkginfo.package.description).bold().blue();
let pkg_author = format_field(&pkginfo.package.author).cyan();
let pkg_stars = format_field(&pkginfo.package.stars).yellow();
let pkg_version = format_field(&pkginfo.package.version).green();
let pkg_size = format_field(&pkginfo.package.size).bold().cyan();
let pkg_license = format_field(&pkginfo.package.license).blue();
let pkg_language = format_field(&pkginfo.package.language).cyan();
let package_information = Columns::from(vec![
ASCII.split('\n').collect::<Vec<&str>>(),
vec![
&format!("Package: {pkg_bin_name}"),
&format!("Architecture: {pkg_architecture}"),
&format!("Stars: {pkg_stars}"),
&format!("Version: {pkg_version}"),
&format!("Author: {pkg_author}"),
&format!("Size: {pkg_size}"),
&format!("Desc: {pkg_desc}"),
&format!("Hash: {pkg_hash}"),
&format!("Language: {pkg_language}"),
&format!("License: {pkg_license}"),
],
])
.set_tabsize(15)
.make_columns();
println!("{}", RESET); println!("{}", BAR.purple());
println!("{}", package_information);
println!("{}", BAR.purple());
}
#[inline]
pub fn print_metadata_package_info(metadatainfo: MetadataInfo, package_name: &str) {
if let Some(package) = metadatainfo
.packages
.iter()
.find(|p| p.name == package_name)
{
let pkg_bin_name = &package.name;
let pkg_architecture = &package.architecture;
let pkg_hash = &package.sha.bold().cyan();
let pkg_desc = format_field(&package.description).bold().blue();
let pkg_author = format_field(&package.author).cyan();
let pkg_stars = format_field(&package.stars).yellow();
let pkg_version = format_field(&package.version).green();
let pkg_size = format_field(&package.size).bold().cyan();
let pkg_license = format_field(&package.license).blue();
let pkg_language = format_field(&package.language).cyan();
let package_information = Columns::from(vec![
ASCII.split('\n').collect::<Vec<&str>>(),
vec![
&format!("Package: {}", pkg_bin_name),
&format!("Architecture: {}", pkg_architecture),
&format!("Stars: {}", pkg_stars),
&format!("Version: {}", pkg_version),
&format!("Author: {}", pkg_author),
&format!("Size: {}", pkg_size),
&format!("Desc: {}", pkg_desc),
&format!("Hash: {}", pkg_hash),
&format!("Language: {}", pkg_language),
&format!("License: {}", pkg_license),
],
])
.set_tabsize(15)
.make_columns();
println!("{}", RESET);
println!("{}", BAR.purple());
println!("{}", package_information);
println!("{}", BAR.purple());
} else {
info(
&format!("No such package found as: {}", package_name),
colored::Color::Cyan,
);
}
}
#[inline]
pub async fn local_config() -> Result<(String, String, String, String, String)> {
let hysp_config = parse_local_config().await?;
let hysp_remote = remove_trailing_slash(
hysp_config
.source
.remote
.ok_or_else(|| anyhow!("Couldn't get data directory"))?
.to_string(),
);
let hysp_data_dir = remove_trailing_slash(
hysp_config
.local
.data
.ok_or_else(|| anyhow!("Couldn't get data directory"))?
.to_string_lossy()
.to_string(),
);
let hysp_bin_dir = remove_trailing_slash(
hysp_config
.local
.bin
.ok_or_else(|| anyhow!("Couldn't get binary directory"))?
.to_string_lossy()
.to_string(),
);
let hysp_metadata = remove_trailing_slash(
hysp_config
.source
.metadata
.ok_or_else(|| anyhow!("Couldn't get metadata link"))?
.to_string(),
);
let system_arch = hysp_config
.source
.aarch
.ok_or_else(|| anyhow!("Couldn't get aarch"))?
.to_string();
Ok((
hysp_remote,
hysp_data_dir,
hysp_bin_dir,
hysp_metadata,
system_arch,
))
}
#[inline]
pub fn create_directory_if_not_exists(path: &str) {
if let Err(err) = std::fs::create_dir_all(path) {
eprintln!("Error creating directory '{}': {}", path, err);
}
}
#[inline]
pub async fn check_hash(filename: String, expected_hash: String) -> Result<bool, io::Error> {
let mut file = File::open(filename).await?;
let mut hasher = Sha256::new();
let mut buffer = [0; 4096];
loop {
let bytes_read = file.read(&mut buffer).await?;
if bytes_read == 0 {
break;
}
hasher.update(&buffer[..bytes_read]);
}
let actual_hash = format!("{:x}", hasher.finalize());
Ok(actual_hash == expected_hash)
}
#[inline]
pub fn is_pkg_installed(pkg_name: &str) -> bool {
let output = Command::new("which")
.arg(pkg_name)
.output()
.expect("Failed to execute 'which' command");
output.status.success()
}
#[inline]
pub fn remove_and_print(file_path: &str) {
if let Err(err) = fs::remove_file(file_path) {
eprintln!("Error removing {}: {}", file_path, err);
} else {
info(&format!("Removed : {}", file_path), colored::Color::Cyan);
}
}