From d9d4728dd68a06ff7b2d53398777d457bc05507c Mon Sep 17 00:00:00 2001 From: Didier Date: Mon, 17 Jul 2023 17:47:42 +0200 Subject: [PATCH] feat: added command functionality to install --- LICENSE | 21 +++++++ bootpkg/src/prelude.rs | 4 +- docs/pkgr/installedmanifest.md | 3 + manifest/src/bin.rs | 2 +- manifest/src/build.rs | 2 +- manifest/src/fs.rs | 2 +- manifest/src/lib.rs | 15 +++-- manifest/src/package.rs | 4 +- manifest/src/pkgr.rs | 11 +++- package.example.toml | 107 ++++++++++++++++++++++++++++++++ package.toml | 38 ------------ pkgfile/src/lib.rs | 16 +++++ pkgr/Cargo.toml | 13 +++- pkgr/src/commands/mod.rs | 68 ++++++++++++++++---- pkgr/src/main.rs | 46 ++++++++++---- pkgr/src/package/builder/mod.rs | 71 +++++++++++++++++++++ pkgr/src/package/fetch.rs | 59 ++++++++++++++++++ pkgr/src/package/identifier.rs | 8 ++- pkgr/src/package/mod.rs | 2 + pkgr/src/tmp.rs | 48 ++++++++++++++ 20 files changed, 464 insertions(+), 76 deletions(-) create mode 100644 LICENSE create mode 100644 package.example.toml create mode 100644 pkgr/src/package/builder/mod.rs create mode 100644 pkgr/src/package/fetch.rs create mode 100644 pkgr/src/tmp.rs diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5c3335d --- /dev/null +++ b/LICENSE @@ -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. diff --git a/bootpkg/src/prelude.rs b/bootpkg/src/prelude.rs index b82b62a..9406cdb 100644 --- a/bootpkg/src/prelude.rs +++ b/bootpkg/src/prelude.rs @@ -5,12 +5,12 @@ use toml; use manifest::{self, Manifest}; -#[derive(Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct PKGR { pub bootstrap: Option, } -#[derive(Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Bootstrap { pub check_installed_commands: Vec, pub commands: Vec, diff --git a/docs/pkgr/installedmanifest.md b/docs/pkgr/installedmanifest.md index 48f7b7b..04a28ad 100644 --- a/docs/pkgr/installedmanifest.md +++ b/docs/pkgr/installedmanifest.md @@ -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" diff --git a/manifest/src/bin.rs b/manifest/src/bin.rs index e30b528..f55a8e5 100644 --- a/manifest/src/bin.rs +++ b/manifest/src/bin.rs @@ -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, diff --git a/manifest/src/build.rs b/manifest/src/build.rs index ac223d2..161b785 100644 --- a/manifest/src/build.rs +++ b/manifest/src/build.rs @@ -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, diff --git a/manifest/src/fs.rs b/manifest/src/fs.rs index e02312f..743bc88 100644 --- a/manifest/src/fs.rs +++ b/manifest/src/fs.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct FS { pub config: Option, pub data: Option, diff --git a/manifest/src/lib.rs b/manifest/src/lib.rs index 8a943fa..0d52840 100644 --- a/manifest/src/lib.rs +++ b/manifest/src/lib.rs @@ -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

> { +pub struct Manifest> { pub package: package::Package, pub dependencies: HashMap, pub fs: fs::FS, @@ -19,7 +19,7 @@ pub struct Manifest

> { pub pkgr: P, } -impl Manifest { +impl Manifest

{ pub fn valid(&self) -> bool { if self.bin.is_none() && self.build.is_none() { return false; @@ -28,6 +28,13 @@ impl Manifest { } } +impl TryFrom for Manifest { + type Error = toml::de::Error; + fn try_from(s: String) -> Result { + toml::from_str(&s) + } +} + impl FromStr for Manifest { type Err = toml::de::Error; fn from_str(s: &str) -> Result { @@ -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(), diff --git a/manifest/src/package.rs b/manifest/src/package.rs index 017271d..233206d 100644 --- a/manifest/src/package.rs +++ b/manifest/src/package.rs @@ -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, diff --git a/manifest/src/pkgr.rs b/manifest/src/pkgr.rs index 8c4ce55..700c6cb 100644 --- a/manifest/src/pkgr.rs +++ b/manifest/src/pkgr.rs @@ -1,4 +1,11 @@ +use std::fmt::{Display, Formatter}; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize)] -pub struct PKGR {} \ No newline at end of file +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PKGR {} + +impl Display for PKGR { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "") + } +} \ No newline at end of file diff --git a/package.example.toml b/package.example.toml new file mode 100644 index 0000000..c99108d --- /dev/null +++ b/package.example.toml @@ -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 +# "" or ",,..." +[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" +] diff --git a/package.toml b/package.toml index c99108d..81240ab 100644 --- a/package.toml +++ b/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 # "" or ",,..." [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. diff --git a/pkgfile/src/lib.rs b/pkgfile/src/lib.rs index bc26f99..15249e7 100644 --- a/pkgfile/src/lib.rs +++ b/pkgfile/src/lib.rs @@ -1,8 +1,24 @@ +#[derive(Debug)] pub struct PKGFile { pub manifest: String, pub data: Vec, } +impl PKGFile { + pub fn new(manifest: String, data: Vec) -> PKGFile { + PKGFile { manifest, data } + } +} + +impl Default for PKGFile { + fn default() -> PKGFile { + PKGFile { + manifest: String::new(), + data: Vec::new(), + } + } +} + impl TryFrom> for PKGFile { type Error = (); diff --git a/pkgr/Cargo.toml b/pkgr/Cargo.toml index 6e94865..cc6e55b 100644 --- a/pkgr/Cargo.toml +++ b/pkgr/Cargo.toml @@ -1,14 +1,25 @@ [package] name = "pkgr" +description = "A package manager and build tool." version = "0.1.0" edition = "2021" +authors = [ + "Didier " +] +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" diff --git a/pkgr/src/commands/mod.rs b/pkgr/src/commands/mod.rs index 8697d80..bcbef89 100644 --- a/pkgr/src/commands/mod.rs +++ b/pkgr/src/commands/mod.rs @@ -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."); } diff --git a/pkgr/src/main.rs b/pkgr/src/main.rs index e8c878a..ba5f97d 100644 --- a/pkgr/src/main.rs +++ b/pkgr/src/main.rs @@ -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() } diff --git a/pkgr/src/package/builder/mod.rs b/pkgr/src/package/builder/mod.rs new file mode 100644 index 0000000..50166e1 --- /dev/null +++ b/pkgr/src/package/builder/mod.rs @@ -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>(&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)) + } + } +} \ No newline at end of file diff --git a/pkgr/src/package/fetch.rs b/pkgr/src/package/fetch.rs new file mode 100644 index 0000000..0e68045 --- /dev/null +++ b/pkgr/src/package/fetch.rs @@ -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 { + 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()) + } + } +} \ No newline at end of file diff --git a/pkgr/src/package/identifier.rs b/pkgr/src/package/identifier.rs index 07b2bcc..fa457d8 100644 --- a/pkgr/src/package/identifier.rs +++ b/pkgr/src/package/identifier.rs @@ -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 { - 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::>(); @@ -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 { #[allow(unused_assignments)] // false positive - let mut name = None; + let mut name = None; let mut version = None; let mut tags = None; diff --git a/pkgr/src/package/mod.rs b/pkgr/src/package/mod.rs index 34cdf0a..5c559fc 100644 --- a/pkgr/src/package/mod.rs +++ b/pkgr/src/package/mod.rs @@ -1 +1,3 @@ pub mod identifier; +pub mod builder; +pub mod fetch; \ No newline at end of file diff --git a/pkgr/src/tmp.rs b/pkgr/src/tmp.rs new file mode 100644 index 0000000..509d3f7 --- /dev/null +++ b/pkgr/src/tmp.rs @@ -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>(&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 for TempDir { + fn into(self) -> PathBuf { + self.path + } +} + +impl Into 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() + } +} \ No newline at end of file