feat: added command functionality to install
This commit is contained in:
parent
3a21d12c07
commit
092c616ca4
20 changed files with 464 additions and 76 deletions
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 didier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -5,12 +5,12 @@ use toml;
|
|||
|
||||
use manifest::{self, Manifest};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PKGR {
|
||||
pub bootstrap: Option<Bootstrap>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Bootstrap {
|
||||
pub check_installed_commands: Vec<String>,
|
||||
pub commands: Vec<String>,
|
||||
|
|
|
@ -11,6 +11,9 @@ To find the latest we'll use the `latest` symlink:
|
|||
install_start = "2021-01-01T00:00:00Z" # install_start is the time the pkgr started installing the package
|
||||
install_end = "2021-01-01T00:00:00Z" # install_end is the time the pkgr finished installing the package
|
||||
install_by = "pkgr" # install_by is the name of the package manager that installed the package
|
||||
package_identifier = "uri"
|
||||
package_id = "https://..."
|
||||
|
||||
|
||||
[old_versions] # This logs old versions of the package (that were installed before)
|
||||
9 = "../9/pkgr.pkg"
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Bin {
|
||||
pub root: String,
|
||||
pub checksums: HashMap<String, String>,
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Build {
|
||||
build_script: String,
|
||||
install_script: String,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FS {
|
||||
pub config: Option<String>,
|
||||
pub data: Option<String>,
|
||||
|
|
|
@ -8,9 +8,9 @@ pub mod fs;
|
|||
pub mod package;
|
||||
pub mod pkgr;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct Manifest<P = Option<pkgr::PKGR>> {
|
||||
pub struct Manifest<P: Clone = Option<pkgr::PKGR>> {
|
||||
pub package: package::Package,
|
||||
pub dependencies: HashMap<String, String>,
|
||||
pub fs: fs::FS,
|
||||
|
@ -19,7 +19,7 @@ pub struct Manifest<P = Option<pkgr::PKGR>> {
|
|||
pub pkgr: P,
|
||||
}
|
||||
|
||||
impl<T> Manifest<T> {
|
||||
impl<P: Clone> Manifest<P> {
|
||||
pub fn valid(&self) -> bool {
|
||||
if self.bin.is_none() && self.build.is_none() {
|
||||
return false;
|
||||
|
@ -28,6 +28,13 @@ impl<T> Manifest<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Manifest {
|
||||
type Error = toml::de::Error;
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
toml::from_str(&s)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Manifest {
|
||||
type Err = toml::de::Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
|
@ -36,7 +43,7 @@ impl FromStr for Manifest {
|
|||
}
|
||||
|
||||
impl Default for Manifest {
|
||||
fn default() -> Self {
|
||||
fn default() -> Manifest {
|
||||
Manifest {
|
||||
package: package::Package::default(),
|
||||
dependencies: HashMap::default(),
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::str::FromStr;
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum PackageType {
|
||||
#[serde(rename = "application")]
|
||||
Application,
|
||||
|
@ -12,7 +12,7 @@ pub enum PackageType {
|
|||
Meta,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct Package {
|
||||
pub name: String,
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct PKGR {}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PKGR {}
|
||||
|
||||
impl Display for PKGR {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "")
|
||||
}
|
||||
}
|
107
package.example.toml
Normal file
107
package.example.toml
Normal file
|
@ -0,0 +1,107 @@
|
|||
#* = required.
|
||||
|
||||
[package]
|
||||
name = "packager" #*
|
||||
description = "A package installation tool" #*
|
||||
|
||||
## package.version
|
||||
# needs to be an int and be incremented every time
|
||||
# if you use a custom version system and want to use that, you can use the tag system!
|
||||
# the highest package.version on the server will be given the tag "latest"
|
||||
version = 1 # this can automatically be incremented when publishing by running `pkgr publish -i ...`
|
||||
|
||||
## package.tags
|
||||
# you can add tags to a package to specify kinds
|
||||
# there are some special tags:
|
||||
# - latest
|
||||
# automatically set on the last published version
|
||||
# previous version will lose this tag
|
||||
# - oldest
|
||||
# assigned to the first version.
|
||||
tags = [ "prod", "pkgr-spec-1" ]
|
||||
|
||||
## package.type
|
||||
# Supported types:
|
||||
# - application
|
||||
# this type specifies that this package is an application.
|
||||
# - library
|
||||
# this type specifies that this package is a library.
|
||||
# - meta
|
||||
# this type specifies that this package does not install anything upon the system except for dependencies.
|
||||
type = "application"
|
||||
|
||||
arch = "x86_64" # this is automatically filled by `pkgr publish ...`
|
||||
|
||||
## dependencies
|
||||
# you may use the following syntax
|
||||
# "<version/tag>" or "<version/tag>,<version/tag>,..."
|
||||
[dependencies]
|
||||
#example = { arch = "x86_64", tags = [ "stable" ], version = "v1" }
|
||||
exnmbr2 = "v1,stable"
|
||||
|
||||
## fs
|
||||
# specify some directories for ease of use
|
||||
[fs]
|
||||
## fs.config
|
||||
# specifiy the config path
|
||||
config = "/etc/packager"
|
||||
## fs.data
|
||||
# specify the data path
|
||||
data = "/var/lib/packager"
|
||||
## replace "pacakger" with your package name to find the default value.
|
||||
|
||||
## bin
|
||||
# Used for systems that don't want to build pkgs.
|
||||
[bin] # binary files root
|
||||
|
||||
## bin.root
|
||||
# ** RELATIVE TO PACKAGE ROOT **
|
||||
# bin.root specifies the root of the installed tree.
|
||||
# anything in here will be overlayed on top of the system.
|
||||
root = "/root" #*
|
||||
|
||||
## bin.checksums
|
||||
# ** KEYS are relative to BIN.ROOT **
|
||||
# ** VALUES are relative to PKG ROOT **
|
||||
# checksums is used to perform checksums before install
|
||||
# values may be paths or a uri. You may include variables.
|
||||
# supported variables:
|
||||
# - @name
|
||||
# - @version
|
||||
# - @display_version
|
||||
[bin.checksums]
|
||||
"/usr/bin/pkgr" = "https://ixvd.net/checksums/packager/@version"
|
||||
|
||||
## build
|
||||
# Scripts will be copied to a custom root.
|
||||
# After the pacakge is built, the install script is ran.
|
||||
# After the install script the build directory will be deleted and an CTREE (changed tree) will be generated.
|
||||
# Then the CTREE will be used to copy the files over.
|
||||
[build]
|
||||
build_script = "scripts/build" # relative to pkg
|
||||
install_script = "scripts/install" # relative to pkg
|
||||
|
||||
[build.dependencies]
|
||||
base = "latest,stable" # selected by default
|
||||
|
||||
## pkgr.*
|
||||
# packager is the official client but you may use other clients supporting the "pkgr v1 spec".
|
||||
# other clients may offer extra functionality this must be put under "pkgr.*"
|
||||
[pkgr]
|
||||
|
||||
## pkgr.bootstrap
|
||||
# This section is used for bootpkg. An edition of packager that bootstraps the full version.
|
||||
# This exists so that packager is easy to install on anything!
|
||||
# and only 1 release channel for pkgr
|
||||
[pkgr.bootstrap]
|
||||
## any non-zero = installed
|
||||
check_installed_commands = [
|
||||
"sh scripts/check_installed"
|
||||
]
|
||||
|
||||
# any non-zero = fail
|
||||
commands = [
|
||||
"sh scripts/bootstrap/download_latest @version /tmp/pkgr.pkg",
|
||||
"sh scripts/bootstrap/dirty_install /tmp/pkgr.pkg",
|
||||
"sh scripts/check_installed"
|
||||
]
|
38
package.toml
38
package.toml
|
@ -1,33 +1,8 @@
|
|||
#* = required.
|
||||
|
||||
[package]
|
||||
name = "packager" #*
|
||||
description = "A package installation tool" #*
|
||||
|
||||
## package.version
|
||||
# needs to be an int and be incremented every time
|
||||
# if you use a custom version system and want to use that, you can use the tag system!
|
||||
# the highest package.version on the server will be given the tag "latest"
|
||||
version = 1 # this can automatically be incremented when publishing by running `pkgr publish -i ...`
|
||||
|
||||
## package.tags
|
||||
# you can add tags to a package to specify kinds
|
||||
# there are some special tags:
|
||||
# - latest
|
||||
# automatically set on the last published version
|
||||
# previous version will lose this tag
|
||||
# - oldest
|
||||
# assigned to the first version.
|
||||
tags = [ "prod", "pkgr-spec-1" ]
|
||||
|
||||
## package.type
|
||||
# Supported types:
|
||||
# - application
|
||||
# this type specifies that this package is an application.
|
||||
# - library
|
||||
# this type specifies that this package is a library.
|
||||
# - meta
|
||||
# this type specifies that this package does not install anything upon the system except for dependencies.
|
||||
type = "application"
|
||||
|
||||
arch = "x86_64" # this is automatically filled by `pkgr publish ...`
|
||||
|
@ -36,19 +11,6 @@ arch = "x86_64" # this is automatically filled by `pkgr publish ...`
|
|||
# you may use the following syntax
|
||||
# "<version/tag>" or "<version/tag>,<version/tag>,..."
|
||||
[dependencies]
|
||||
#example = { arch = "x86_64", tags = [ "stable" ], version = "v1" }
|
||||
exnmbr2 = "v1,stable"
|
||||
|
||||
## fs
|
||||
# specify some directories for ease of use
|
||||
[fs]
|
||||
## fs.config
|
||||
# specifiy the config path
|
||||
config = "/etc/packager"
|
||||
## fs.data
|
||||
# specify the data path
|
||||
data = "/var/lib/packager"
|
||||
## replace "pacakger" with your package name to find the default value.
|
||||
|
||||
## bin
|
||||
# Used for systems that don't want to build pkgs.
|
||||
|
|
|
@ -1,8 +1,24 @@
|
|||
#[derive(Debug)]
|
||||
pub struct PKGFile {
|
||||
pub manifest: String,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl PKGFile {
|
||||
pub fn new(manifest: String, data: Vec<u8>) -> PKGFile {
|
||||
PKGFile { manifest, data }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PKGFile {
|
||||
fn default() -> PKGFile {
|
||||
PKGFile {
|
||||
manifest: String::new(),
|
||||
data: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for PKGFile {
|
||||
type Error = ();
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
71
pkgr/src/package/builder/mod.rs
Normal file
71
pkgr/src/package/builder/mod.rs
Normal 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
59
pkgr/src/package/fetch.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
pub mod identifier;
|
||||
pub mod builder;
|
||||
pub mod fetch;
|
48
pkgr/src/tmp.rs
Normal file
48
pkgr/src/tmp.rs
Normal 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()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue