feat: added command functionality to install

This commit is contained in:
Strix 2023-10-14 22:39:43 +02:00
parent 3a21d12c07
commit 092c616ca4
No known key found for this signature in database
GPG key ID: 49B2E37B8915B774
20 changed files with 464 additions and 76 deletions

View file

@ -1,14 +1,25 @@
[package]
name = "pkgr"
description = "A package manager and build tool."
version = "0.1.0"
edition = "2021"
authors = [
"Didier <dev@faulty.nl>"
]
license-file = "../LICENSE"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
manifest = { path = "../manifest" }
pkgfile = { path = "../pkgfile" }
fern = "0.6.2"
log = "0.4.19"
regex = "1.9.1"
manifest = { path = "../manifest" }
clap = { version = "4.3.12", features = ["derive"] }
colored = "2.0.4"
toml = "0.7.6"
serde = { version = "1.0.171", features = ["derive"] }
libc = "0.2.80"
reqwest = { version = "0.11.18", features = ["blocking"] }
tar = "0.4.39"

View file

@ -1,6 +1,10 @@
use std::process::exit;
use crate::package::identifier::PackageIdentifier;
use clap::{Parser, Subcommand};
use log::error;
use log::{debug, error, info, trace, warn};
use manifest::Manifest;
use crate::package::builder::{InstallType, PackageInstaller};
use crate::package::fetch::fetch_package;
#[derive(Parser, Debug)]
#[clap(name = "pkgr", version)]
@ -33,6 +37,8 @@ pub enum Command {
/// Update packages on the system
Update,
#[command(hide = true)]
Debug,
#[command(hide = true)]
None,
}
@ -41,20 +47,47 @@ impl Command {
match self {
Command::Install {
build,
package_identifier: _,
package_identifier,
} => {
if *build {
error!("Build is not yet implemented.");
} else {
error!("Install is not yet implemented.");
warn!("Installer does not run in isolation.");
let start = std::time::Instant::now();
let unix_start = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap();
trace!("Fetching package: {}", package_identifier);
let pkgfile = fetch_package(package_identifier.clone()).unwrap();
debug!("size: manifest({}kb) data({}kb)", pkgfile.manifest.len() / 1024, pkgfile.data.len() / 1024);
trace!("parsing manifest");
let manifest = Manifest::try_from(pkgfile.manifest.clone()).unwrap();
debug!("manifest pkg name: {}", manifest.package.name);
trace!("creating installer");
let installer = PackageInstaller::new(manifest.clone(), pkgfile, if *build { InstallType::Build } else { InstallType::Bin });
trace!("starting install");
match installer.install() {
Ok(_) => {
info!("Sucessfully installed: {}", &manifest.package.name);
()
},
Err(e) => {
error!("{}", e.to_string())
}
}
let unix_end = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap();
let end = std::time::Instant::now();
// float ms
let duration = (end - start).as_nanos() as f64;
info!("Install took: {}ms", (duration / 1_000_000.0));
}
Command::Remove { package_identifier: _, index } => {
if *index {
error!("Index removal is not yet implemented.");
} else {
error!("Remove is not yet implemented.");
Command::Remove { package_identifier, index } => {
if let PackageIdentifier::URI(_) = package_identifier {
error!("URI is unsupported when removing applications.");
exit(1);
}
info!("Index: {}", index);
info!("Package identifier: {}", package_identifier);
}
Command::List { installed: _ } => {
error!("List is not yet implemented.");
@ -62,6 +95,19 @@ impl Command {
Command::Update => {
error!("Update is not yet implemented.");
}
Command::Debug => {
trace!("Trace message.");
debug!("Debug message.");
info!("Info message.");
warn!("Warning message.");
error!("Error message.");
info!("");
info!("PKGR VERSION: {}", env!("CARGO_PKG_VERSION"));
info!("PKGR AUTHORS: {}", env!("CARGO_PKG_AUTHORS"));
info!("PKGR DESCRIPTION: {}", env!("CARGO_PKG_DESCRIPTION"));
info!("");
info!("PKGR_LOG_LEVEL: {}", std::env::var("PKGR_LOG_LEVEL").unwrap_or_else(|_| "info".to_string()));
}
Command::None => {
error!("No command was specified.");
}

View file

@ -1,25 +1,35 @@
use clap::Parser;
use colored::Colorize;
use crate::commands::Cli;
use log::{debug, info, SetLoggerError, trace};
use log::{SetLoggerError, trace};
use std::env;
mod commands;
mod package;
mod tmp;
fn main() {
setup_logger().expect("Unable to setup logger.");
#[cfg(not(debug_assertions))]
{
#[cfg(target_family = "unix")]
if unsafe { libc::getuid() } != 0 {
use log::error;
error!("pkgr must be run as root.");
std::process::exit(1);
}
}
trace!("Parsing command line arguments...");
let c = Cli::parse();
debug!("Command line arguments parsed.");
trace!("Command line arguments: {:?}", c);
trace!("Executing command...");
c.command.execute();
debug!("Command executed.");
trace!("Command executed.");
trace!("Exiting...");
info!("Done.");
}
fn setup_logger() -> Result<(), SetLoggerError> {
@ -27,16 +37,30 @@ fn setup_logger() -> Result<(), SetLoggerError> {
.format(|out, message, record| {
out.finish(format_args!(
"{} {}",
match record.level().to_string().chars().nth(0).unwrap_or('I') {
'T' => "##".cyan(),
'D' => "::".yellow(),
'E' | 'W' => "!!".red(),
_ => "**".blue(),
},
// Some logic so messages look nice
if message.to_string().len() > 0 {
match record
.level()
.to_string()
.chars()
.nth(0)
.unwrap_or('T')
{
'T' => "[TRACE]".cyan(),
'D' => "??".green(),
'I' => "=>".blue(),
'W' => "##".yellow(),
'E' => "!!".red(),
_ => "**".blue(),
}.to_string()
} else { "".to_string() },
message.to_string().bright_white()
))
})
.level(env::var("PKGR_LOG_LEVEL").unwrap_or_else(|_| "info".to_string()).parse().unwrap_or(log::LevelFilter::Info))
.level(env::var("PKGR_LOG_LEVEL")
.unwrap_or_else(|_| "info".to_string())
.parse()
.unwrap_or(log::LevelFilter::Info))
.chain(std::io::stdout())
.apply()
}

View file

@ -0,0 +1,71 @@
use libc::fork;
use log::{debug, trace};
use manifest::Manifest;
use pkgfile::PKGFile;
use crate::tmp::TempDir;
#[derive(Debug)]
pub enum InstallType {
Build,
Bin
}
#[derive(Debug)]
pub enum InstallError {
BuildError(String),
BinError(String),
InstallError
}
impl ToString for InstallError {
fn to_string(&self) -> String {
match self {
InstallError::BuildError(e) => format!("Build error: {}", e),
InstallError::BinError(e) => format!("Bin error: {}", e),
InstallError::InstallError => "Install error".to_string(),
}
}
}
#[derive(Debug)]
pub struct PackageInstaller {
manifest: Manifest,
pkgfile: PKGFile,
install_type: InstallType
}
impl PackageInstaller {
pub fn new(m: Manifest, p: PKGFile, i: InstallType) -> PackageInstaller {
PackageInstaller {
manifest: m,
pkgfile: p,
install_type: i
}
}
fn extract_to<S: Into<String>>(&self, path: S) -> Result<(), String> {
tar::Archive::new(self.pkgfile.data.as_slice())
.unpack(path.into())
.map_err(|e| e.to_string())
}
fn bin(&self) -> Result<(), String> {
let mut tmpdir = TempDir::default();
tmpdir.push(&self.manifest.package.name);
trace!("extracting package into: {}", tmpdir.to_string());
self.extract_to(tmpdir.to_string())?;
debug!("extracted package in: {}", tmpdir.to_string());
Ok(())
}
fn build(&self) -> Result<(), String> {
Ok(())
}
pub fn install(&self) -> Result<(), InstallError> {
match self.install_type {
InstallType::Bin => self.bin().map_err(|e| InstallError::BinError(e)),
InstallType::Build => self.build().map_err(|e| InstallError::BuildError(e))
}
}
}

59
pkgr/src/package/fetch.rs Normal file
View file

@ -0,0 +1,59 @@
use std::error::Error;
use std::fmt::Display;
use std::fs::File;
use std::io::Read;
use log::warn;
use pkgfile::PKGFile;
use crate::package::identifier::PackageIdentifier;
use reqwest::blocking::get;
#[derive(Debug)]
pub enum FetchError {
HTTPError(reqwest::Error),
IOError(std::io::Error),
ParseError,
}
impl Display for FetchError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
FetchError::HTTPError(e) => write!(f, "HTTP Error: {}", e),
FetchError::IOError(e) => write!(f, "IO Error: {}", e),
FetchError::ParseError => write!(f, "Parse Error"),
}
}
}
impl Error for FetchError {}
pub fn fetch_package(package_identifier: PackageIdentifier) -> Result<PKGFile, FetchError> {
match package_identifier {
PackageIdentifier::Path(path) => {
std::fs::read(path)
.map_err(|e| FetchError::IOError(e)).and_then(|bytes| {
PKGFile::try_from(bytes).map_err(|_| FetchError::ParseError)
})
}
PackageIdentifier::URI(uri) => {
// get file contents as bytes
let mut bytes = Vec::new();
match get(&uri) {
Ok(mut response) => {
match response.take(1024 * 1024).read_to_end(&mut bytes) {
Ok(_) => (),
Err(e) => return Err(FetchError::IOError(e)),
};
}
Err(e) => return Err(FetchError::HTTPError(e)),
};
// parse bytes as PKGFile
match PKGFile::try_from(bytes) {
Ok(pkgfile) => Ok(pkgfile),
Err(e) => Err(FetchError::ParseError),
}
}
PackageIdentifier::PackageLocator(package_locator) => {
Ok(PKGFile::default())
}
}
}

View file

@ -26,6 +26,7 @@ impl Error for PackageIdentifierError {}
pub enum PackageIdentifier {
PackageLocator(PackageLocator),
URI(String),
Path(String),
}
impl std::fmt::Display for PackageIdentifier {
@ -33,6 +34,7 @@ impl std::fmt::Display for PackageIdentifier {
match self {
PackageIdentifier::PackageLocator(pl) => write!(f, "{}", pl),
PackageIdentifier::URI(uri) => write!(f, "{}", uri),
PackageIdentifier::Path(path) => write!(f, "{}", path),
}
}
}
@ -61,7 +63,7 @@ impl FromStr for PackageIdentifier {
type Err = PackageIdentifierError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let uri_re = regex::Regex::new(r"^[a-zA-Z0-9]+://").unwrap();
let uri_re = Regex::new(r"^[a-zA-Z0-9]+://").unwrap();
if uri_re.is_match(s) {
// there needs to be stuff after the protocol
let split = s.split("://").collect::<Vec<&str>>();
@ -69,6 +71,8 @@ impl FromStr for PackageIdentifier {
return Err(PackageIdentifierError::InvalidURI(s.to_string()));
}
Ok(PackageIdentifier::URI(s.to_string()))
} else if std::path::Path::new(s).exists() {
return Ok(PackageIdentifier::Path(s.to_string()));
} else {
let pl = match PackageLocator::from_str(s) {
Ok(pl) => pl,
@ -84,7 +88,7 @@ impl FromStr for PackageLocator {
fn from_str(s: &str) -> Result<Self, Self::Err> {
#[allow(unused_assignments)] // false positive
let mut name = None;
let mut name = None;
let mut version = None;
let mut tags = None;

View file

@ -1 +1,3 @@
pub mod identifier;
pub mod builder;
pub mod fetch;

48
pkgr/src/tmp.rs Normal file
View file

@ -0,0 +1,48 @@
use std::path::PathBuf;
pub struct TempDir {
path: PathBuf,
}
impl TempDir {
pub fn new(path: PathBuf) -> TempDir {
if !path.exists() {
std::fs::create_dir_all(&path).unwrap();
}
TempDir {
path
}
}
pub fn push<S: Into<String>>(&mut self, path: S) {
self.path.push(path.into());
}
}
impl Default for TempDir {
fn default() -> TempDir {
TempDir::new({
let mut t = std::env::temp_dir();
t.push("pkgr");
t
})
}
}
impl Into<PathBuf> for TempDir {
fn into(self) -> PathBuf {
self.path
}
}
impl Into<String> for TempDir {
fn into(self) -> String {
self.path.to_str().unwrap().to_string()
}
}
impl ToString for TempDir {
fn to_string(&self) -> String {
self.path.to_str().unwrap().to_string()
}
}