Manifest {
@@ -50,7 +50,7 @@ impl Default for Manifest {
fs: fs::FS::default(),
bin: None,
build: None,
- pkgr: None,
+ ext: None,
}
}
}
diff --git a/manifest/src/package.rs b/manifest/src/package.rs
index 233206d..d7c126c 100644
--- a/manifest/src/package.rs
+++ b/manifest/src/package.rs
@@ -12,6 +12,16 @@ pub enum PackageType {
Meta,
}
+impl ToString for PackageType {
+ fn to_string(&self) -> String {
+ match &self {
+ PackageType::Application => "application",
+ PackageType::Library => "library",
+ PackageType::Meta => "meta"
+ }.to_string()
+ }
+}
+
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct Package {
diff --git a/package.example.toml b/package.example.toml
index c99108d..c20aabb 100644
--- a/package.example.toml
+++ b/package.example.toml
@@ -84,16 +84,16 @@ install_script = "scripts/install" # relative to pkg
[build.dependencies]
base = "latest,stable" # selected by default
-## pkgr.*
+## ext.*
# 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]
+# other clients may offer extra functionality this must be put under "ext.*"
+[ext]
-## pkgr.bootstrap
+## ext.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]
+[ext.bootstrap]
## any non-zero = installed
check_installed_commands = [
"sh scripts/check_installed"
diff --git a/package.toml b/package.toml
deleted file mode 100644
index 81240ab..0000000
--- a/package.toml
+++ /dev/null
@@ -1,69 +0,0 @@
-[package]
-name = "packager" #*
-description = "A package installation tool" #*
-version = 1 # this can automatically be incremented when publishing by running `pkgr publish -i ...`
-tags = [ "prod", "pkgr-spec-1" ]
-type = "application"
-
-arch = "x86_64" # this is automatically filled by `pkgr publish ...`
-
-## dependencies
-# you may use the following syntax
-# "" or ",,..."
-[dependencies]
-
-## 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/pkg.py b/pkg.py
new file mode 100644
index 0000000..03e20ab
--- /dev/null
+++ b/pkg.py
@@ -0,0 +1,79 @@
+import glob
+import os
+import sys
+import tarfile
+
+
+def build_package(package_toml_path, output_path, directories_to_include):
+ data = read_package_toml(package_toml_path)
+ header = build_header(data)
+ pkg_path = output_path
+
+ with open(pkg_path, mode='wb') as pkg:
+ pkg.write(header)
+ write_manifest(pkg, data)
+ build_tarball(pkg, directories_to_include)
+
+ return pkg_path
+
+
+def read_package_toml(package_toml_path):
+ with open(package_toml_path, mode='r') as mani:
+ return mani.read()
+
+
+def build_header(data):
+ header = bytes([0x01, (len(data) >> 8) & 0xFF, len(data) & 0xFF])
+ return header
+
+
+def write_manifest(pkg, data):
+ pkg.write(data.encode("utf-8"))
+
+
+def build_tarball(pkg, directories_to_include):
+ with tarfile.TarFile("/tmp/pkgtar", 'w') as pkgtar:
+ for directory in directories_to_include:
+ add_files_to_tar(pkgtar, directory, True)
+
+ append_tar_to_pkg(pkg)
+ cleanup_tmp_files()
+
+
+def add_files_to_tar(pkgtar, directory, skip_target=False):
+ for root, dirs, files in os.walk(directory):
+ for file in files:
+ if skip_target and "target" in root:
+ continue
+ print(f"\33[2Kadd: {os.path.join(root, file)}", end="\r", flush=True)
+ # print()
+ pkgtar.add(os.path.join(root, file))
+ print("\33[2K", end="\r", flush=True)
+
+
+def append_tar_to_pkg(pkg):
+ with open("/tmp/pkgtar", 'rb') as pkgtar:
+ pkg.write(pkgtar.read())
+
+
+def cleanup_tmp_files():
+ print("deleting /tmp/pkgtar...")
+ os.unlink("/tmp/pkgtar")
+
+
+if __name__ == '__main__':
+ if len(sys.argv) < 3:
+ print("Usage: pkg.py [ ...]")
+ sys.exit(1)
+
+ package_toml_path = sys.argv[1]
+ output_path = sys.argv[2]
+ directories_to_include = glob.glob(sys.argv[3]) if len(sys.argv) == 4 else sys.argv[3:]
+
+ try:
+ pkg_path = build_package(package_toml_path, output_path, directories_to_include)
+ print(f"Package created: {pkg_path}")
+ except FileNotFoundError:
+ print("Error: File not found.")
+ except Exception as e:
+ print(f"Error occurred: {e}")
diff --git a/pkg/scripts/bootstrap/dirty_install b/pkg/scripts/bootstrap/dirty_install
deleted file mode 100644
index 41d3f5d..0000000
--- a/pkg/scripts/bootstrap/dirty_install
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-printf ""
\ No newline at end of file
diff --git a/pkg/scripts/bootstrap/download_latest b/pkg/scripts/bootstrap/download_latest
deleted file mode 100644
index 178bd4e..0000000
--- a/pkg/scripts/bootstrap/download_latest
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-fetch_latest_version() {
- printf ""
-}
-
-download_file() {
- printf ""
-}
\ No newline at end of file
diff --git a/pkg/scripts/check_installed b/pkg/scripts/check_installed
deleted file mode 100644
index 41d3f5d..0000000
--- a/pkg/scripts/check_installed
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-printf ""
\ No newline at end of file
diff --git a/pkgfile/src/lib.rs b/pkgfile/src/lib.rs
index 407055c..6a9457d 100644
--- a/pkgfile/src/lib.rs
+++ b/pkgfile/src/lib.rs
@@ -1,4 +1,9 @@
-#[derive(Debug, Clone)]
+use std::error::Error;
+use std::fmt::{Display, Formatter};
+use std::io;
+use std::path::Path;
+
+#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PKGFile {
pub manifest: String,
pub data: Vec,
@@ -23,28 +28,56 @@ impl Default for PKGFile {
}
}
-impl TryFrom> for PKGFile {
- type Error = ();
+#[derive(Debug)]
+pub enum PKGFileError {
+ IOError(io::Error),
+ ParsingError(String)
+}
- fn try_from(value: Vec) -> Result {
+impl Display for PKGFileError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ match self {
+ PKGFileError::IOError(e) => write!(f, "IOError: {}", e),
+ PKGFileError::ParsingError(s) => write!(f, "ParsingError: {}", s)
+ }
+ }
+}
+
+impl Error for PKGFileError {}
+
+impl<'a> TryFrom<&'a Path> for PKGFile {
+ type Error = PKGFileError;
+ fn try_from(path: &'a Path) -> Result {
+ let d = match std::fs::read(path) {
+ Ok(d) => d,
+ Err(e) => return Err(PKGFileError::IOError(e))
+ };
+ PKGFile::try_from(d)
+ }
+}
+
+impl TryFrom> for PKGFile {
+ type Error = PKGFileError;
+
+ fn try_from(value: Vec) -> Result {
match value[0] {
1 => {
let header: Vec = value[..3].iter().map(|v| u32::from(*v)).collect();
let manifest_size: u32 = (header[1] << 8) | header[2];
if manifest_size > value.len() as u32 {
- return Err(());
+ return Err(PKGFileError::ParsingError("Invalid header length".into()));
}
Ok(PKGFile {
manifest: match String::from_utf8(
value[3..(manifest_size as usize + 3)].to_vec(),
) {
Ok(s) => s,
- _ => return Err(()),
+ _ => return Err(PKGFileError::ParsingError("Could not parse manifest".into())),
},
data: value[(manifest_size as usize + 3)..].to_vec(),
})
}
- _ => Err(()),
+ _ => Err(PKGFileError::ParsingError("Unknown pkgfile version".into())),
}
}
}
diff --git a/pkgr/Cargo.toml b/pkgr/Cargo.toml
index cc6e55b..e43b436 100644
--- a/pkgr/Cargo.toml
+++ b/pkgr/Cargo.toml
@@ -23,3 +23,7 @@ serde = { version = "1.0.171", features = ["derive"] }
libc = "0.2.80"
reqwest = { version = "0.11.18", features = ["blocking"] }
tar = "0.4.39"
+humantime = "2.1.0"
+expanduser = "1.2.2"
+url = { version = "2.4.0", features = ["serde"] }
+dns-lookup = "2.0.3"
diff --git a/pkgr/package.toml b/pkgr/package.toml
new file mode 100644
index 0000000..fa95cd9
--- /dev/null
+++ b/pkgr/package.toml
@@ -0,0 +1,32 @@
+[package]
+name = "packager"
+description = "A package installation tool"
+version = 1
+tags = [ "prod", "pkgr-spec-1" ]
+type = "application"
+arch = "x86_64"
+
+[dependencies]
+
+[bin]
+root = "/root"
+
+[bin.checksums]
+"/usr/bin/pkgr" = "https://ixvd.net/checksums/packager/@version"
+
+[build]
+build_script = "scripts/build"
+install_script = "scripts/install"
+
+[build.dependencies]
+
+[ext]
+
+[ext.bootstrap]
+check_installed_commands = [
+ "file /usr/bin/pkgr"
+]
+
+commands = [
+ "cp root/usr/bin/pkgr /usr/bin/pkgr"
+]
diff --git a/pkgr/skel/etc/pkgr.d/repos.toml b/pkgr/skel/etc/pkgr.d/repos.toml
new file mode 100644
index 0000000..2723e7b
--- /dev/null
+++ b/pkgr/skel/etc/pkgr.d/repos.toml
@@ -0,0 +1,4 @@
+[repo.main]
+name = "Main"
+url = "tcp://pkgs.ixvd.net:1050"
+
diff --git a/pkgr/skel/etc/pkgr.toml b/pkgr/skel/etc/pkgr.toml
new file mode 100644
index 0000000..2ee5004
--- /dev/null
+++ b/pkgr/skel/etc/pkgr.toml
@@ -0,0 +1,7 @@
+build_by_default = false
+tmp_dir = "/tmp/pkgr"
+
+[storage]
+repo_file = "/etc/pkgr.d/repos.toml"
+data_dir = "/var/lib/pkgr/packages"
+index_dir = "/var/lib/pkgr/indexes"
\ No newline at end of file
diff --git a/pkgr/skel/var/lib/pkgr/indexes/repo.toml b/pkgr/skel/var/lib/pkgr/indexes/repo.toml
new file mode 100644
index 0000000..47fa234
--- /dev/null
+++ b/pkgr/skel/var/lib/pkgr/indexes/repo.toml
@@ -0,0 +1,20 @@
+[index.main]
+last_update = "1610219288"
+[index.main.packager]
+versions = [1, 2, 3]
+tags = ["tag"]
+
+## rust
+# IndexedPackage
+# - name -> packager
+# - origin_repo -> main
+# - versions -> [1,2,3]
+# - tags -> ["tag"]
+# - get_package() -> Package
+
+## rust
+# Repo
+# - name -> main
+# - last_update -> 1610219288
+# - update_repo() -> io::Result<()>
+# - searchIndex(p: PackageIdentifier) -> IndexedPackage
\ No newline at end of file
diff --git a/pkgr/src/api/client.rs b/pkgr/src/api/client.rs
new file mode 100644
index 0000000..e69de29
diff --git a/pkgr/src/api/mod.rs b/pkgr/src/api/mod.rs
new file mode 100644
index 0000000..1ed3d70
--- /dev/null
+++ b/pkgr/src/api/mod.rs
@@ -0,0 +1,63 @@
+use std::collections::HashMap;
+use serde;
+use serde::{Deserialize, Serialize};
+
+pub mod client;
+
+#[derive(Serialize, Deserialize)]
+pub enum Query {
+ #[serde(rename = "pull")]
+ Pull {
+ name: String,
+ version: Option,
+ tags: Vec
+ },
+ #[serde(rename = "push")]
+ Push {
+ // todo: review me pls
+ _data: Vec
+ },
+ #[serde(rename = "index")]
+ Index {
+ request_update: bool,
+ fetch: bool
+ }
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct Request {
+ version: u32,
+ id: String,
+ token: Option,
+ query: HashMap
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct DataErrorDetails {
+ actor: String,
+ detailed_cause: String,
+ recovery_options: Vec,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct DataError {
+ name: String,
+ cause: Option,
+ details: Option
+}
+
+#[derive(Serialize, Deserialize)]
+pub enum Data {
+ Pull {
+ _data: Option>,
+ }
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct Response {
+ version: u32,
+ id: String,
+ reply_to: String,
+ errors: HashMap,
+ data: HashMap
+}
\ No newline at end of file
diff --git a/pkgr/src/commands.rs b/pkgr/src/commands.rs
index a1904a7..80e9448 100644
--- a/pkgr/src/commands.rs
+++ b/pkgr/src/commands.rs
@@ -1,12 +1,17 @@
-use crate::package::identifier::PackageIdentifier;
+use std::process::exit;
-use crate::package::Package;
use clap::{Parser, Subcommand};
+use colored::Colorize;
use log::{debug, error, info, trace, warn};
-use std::process::exit;
-use crate::CONFIG;
+use manifest::package::PackageType;
+
+use crate::package::identifier::PackageIdentifier;
+use crate::package::Package;
+use crate::package::queue::PackageQueue;
use crate::process::Process;
+use crate::types::fetch::TryFetch;
+use crate::util::prompts::prompt_bool;
#[derive(Parser, Debug)]
#[clap(name = "pkgr", version)]
@@ -21,6 +26,8 @@ pub enum Command {
Install {
#[arg(short, long, default_value_t = false)]
build: bool,
+ #[arg(short, long, default_value_t = false)]
+ ask: bool,
package_identifier: PackageIdentifier,
},
/// Remove a package from the system
@@ -38,7 +45,11 @@ pub enum Command {
},
/// Update packages on the system
Update,
- #[command(hide = true)]
+ /// Get info about a package
+ Info {
+ package_identifier: Option
+ },
+ #[cfg(debug_assertions)]
Debug,
#[command(hide = true)]
None,
@@ -49,6 +60,7 @@ impl Command {
match self {
Command::Install {
build,
+ ask,
package_identifier,
} => {
warn!("Installer does not run in isolation.");
@@ -59,8 +71,7 @@ impl Command {
.unwrap();
info!("Parsing package...");
- trace!("Fetching package: {}", package_identifier);
- let mut pkg = Package::fetch(package_identifier.clone()).unwrap();
+ let mut pkg = Package::try_fetch(package_identifier.clone()).unwrap();
debug!("manifest size: {}kb", pkg.pkgfile.manifest.len() / 1024);
debug!("files size: {}kb", pkg.pkgfile.data.len() / 1024);
@@ -70,14 +81,19 @@ impl Command {
exit(1);
}
- trace!("Starting install...");
- match pkg.install(CONFIG.with(|c| if !*build { c.build_by_default } else { *build })) {
- Ok(_) => (),
- Err(e) => {
- error!("Install failed: {}", e.to_string());
- exit(1);
+ let mut queue = PackageQueue::new();
+ queue.add_package(pkg, *build);
+ trace!("Installing queue...");
+ {
+ if *ask {
+ info!("Install following packages?");
+ info!(target: "item", "{}", queue);
+ if !prompt_bool("Continue?", false) {
+ return;
+ }
}
}
+ queue.install(*build);
let end = std::time::Instant::now();
let _unix_end = std::time::SystemTime::now()
@@ -87,7 +103,7 @@ impl Command {
let duration = end.duration_since(start);
info!("Install complete.");
- info!("Install took {}ms.", duration.as_nanos() as f64 / 1000000.0);
+ info!("Install took {}.", humantime::format_duration(duration));
}
Command::Remove {
package_identifier,
@@ -106,6 +122,74 @@ impl Command {
Command::Update => {
error!("Update is not yet implemented.");
}
+ Command::Info {
+ package_identifier
+ } => {
+ if let Some(p) = package_identifier {
+ if let Ok(mut pkg) = Package::try_fetch(p.clone()) {
+ trace!("{}", pkg.pkgfile.manifest);
+ info!(target: "item", "Identifier: {:?}", pkg.identifier);
+ info!(target: "item", "");
+ let mani = pkg.manifest();
+ info!(target: "item", "Package name: {}", mani.package.name);
+ info!(target: "item", "Package description: {}", mani.package.description);
+ info!(target: "item", "Package version: {}", mani.package.version);
+ info!(target: "item", "Package tags: {}", mani.package.tags.join(", "));
+ info!(target: "item", "Package type: {}", mani.package.package_type.to_string());
+ info!(target: "item", "");
+ info!(target: "item", "Supported install types: {}", {
+ let mut types = vec![];
+ if let Some(_) = mani.bin { types.push("bin") }
+ if let Some(_) = mani.build { types.push("build") }
+ if let PackageType::Meta = mani.package.package_type { types.push("meta") }
+ types.join(", ")
+ });
+ info!(target: "item", "");
+ info!(target: "item", "Dependencies: {}", {
+ let deps = pkg.dependencies();
+ if deps.len() == 0 {
+ String::from("None")
+ } else {
+ deps
+ .iter()
+ .map(|p| {
+ p.identifier.to_string()
+ })
+ .collect::>()
+ .join(", ")
+ }
+ });
+ info!(target: "item", "Build Dependencies: {}", {
+ let deps = pkg.build_dependencies();
+ if deps.len() == 0 {
+ String::from("None")
+ } else {
+ deps
+ .iter()
+ .map(|p| {
+ p.identifier.to_string()
+ })
+ .collect::>()
+ .join(", ")
+ }
+ });
+ } else {
+ error!("Could not find {p}");
+ }
+ } else {
+ info!("Welcome to pkgr!\n\
+ {}\n\
+ To get help please run \"{} -h\"", env!("CARGO_PKG_DESCRIPTION"), std::env::args().nth(0).unwrap());
+ info!("");
+ info!("version: {}", env!("CARGO_PKG_VERSION"));
+ info!("authors: {}", env!("CARGO_PKG_AUTHORS"));
+
+ info!("");
+ info!("If you can't seem to figure something out, use environment variable: PKGR_LOG_LEVEL=debug");
+ trace!("{}", "The trace log level should really only be used by PKGR devs.".red());
+ }
+ }
+ #[cfg(debug_assertions)]
Command::Debug => {
trace!("Trace message.\nWith newline.");
debug!("Debug message.\nWith newline.");
@@ -123,10 +207,6 @@ impl Command {
.unwrap();
}
- 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: {}",
diff --git a/pkgr/src/config/mod.rs b/pkgr/src/config/mod.rs
index 9a932d2..4c6876e 100644
--- a/pkgr/src/config/mod.rs
+++ b/pkgr/src/config/mod.rs
@@ -1,4 +1,8 @@
use serde::{Deserialize, Serialize};
+use storage::Storage;
+
+mod storage;
+pub mod repos;
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
@@ -6,13 +10,16 @@ pub struct Config {
pub build_by_default: bool,
#[serde(default)]
pub tmp_dir: Option,
+ #[serde(default)]
+ pub storage: Storage
}
impl Default for Config {
fn default() -> Config {
Config {
build_by_default: false,
- tmp_dir: Some(String::from("/tmp/pkgr"))
+ tmp_dir: Some(String::from("/tmp/pkgr")),
+ storage: Storage::default()
}
}
}
diff --git a/pkgr/src/config/repos.rs b/pkgr/src/config/repos.rs
new file mode 100644
index 0000000..8594a25
--- /dev/null
+++ b/pkgr/src/config/repos.rs
@@ -0,0 +1,37 @@
+use std::collections::HashMap;
+use std::str::FromStr;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Serialize, Deserialize)]
+pub struct Repo {
+ #[serde(default)]
+ pub name: String,
+ pub uri: Url
+}
+
+impl Default for Repo {
+ fn default() -> Self {
+ Repo {
+ name: String::from("Repo"),
+ uri: Url::parse("tcp://0.0.0.0:0000").unwrap()
+ }
+ }
+}
+
+#[derive(Clone, Serialize, Deserialize)]
+pub struct RepoFile {
+ pub repos: HashMap
+}
+
+impl RepoFile {
+ pub fn from_path(path: &str) -> Result {
+ match std::fs::read_to_string(path) {
+ Ok(s) => match toml::from_str(&s) {
+ Ok(c) => Ok(c),
+ Err(e) => Err(format!("failed to parse config: {}", e)),
+ },
+ Err(e) => Err(format!("failed to read config: {}", e)),
+ }
+ }
+}
\ No newline at end of file
diff --git a/pkgr/src/config/storage.rs b/pkgr/src/config/storage.rs
new file mode 100644
index 0000000..5fa8fa1
--- /dev/null
+++ b/pkgr/src/config/storage.rs
@@ -0,0 +1,24 @@
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Storage {
+ /// Where the repositories are defined.
+ #[serde(default)]
+ pub repo_file: String,
+ /// Where to store pkgs data
+ #[serde(default)]
+ pub data_dir: String,
+ /// Where to store repo indexes
+ #[serde(default)]
+ pub index_dir: String,
+}
+
+impl Default for Storage {
+ fn default() -> Self {
+ Storage {
+ repo_file: String::from("/etc/pkgr.d/repos.toml"),
+ data_dir: String::from("/var/lib/pkgr/packages"),
+ index_dir: String::from("/var/lib/pkgr/indexes"),
+ }
+ }
+}
diff --git a/pkgr/src/logging.rs b/pkgr/src/logging.rs
index 47a66bc..daef3eb 100644
--- a/pkgr/src/logging.rs
+++ b/pkgr/src/logging.rs
@@ -1,18 +1,19 @@
+use std::env;
+
use colored::Colorize;
use fern::Dispatch;
use log::{Record, SetLoggerError};
-use std::env;
fn format_regular>(log: S, record: &Record) -> String {
let log = log.into();
let line_prefix = |line: String, extend: bool| {
let prefix = if extend {
match record.level() {
- log::Level::Trace => " :".bright_blue(),
- log::Level::Debug => " :".green(),
- log::Level::Info => " :".blue(),
- log::Level::Warn => " :".yellow(),
- log::Level::Error => " :".red(),
+ log::Level::Trace => " ]".bright_blue(),
+ log::Level::Debug => " ?".green(),
+ log::Level::Info => " >".blue(),
+ log::Level::Warn => " #".yellow(),
+ log::Level::Error => " !".red(),
}.to_string()
} else {
match record.level() {
@@ -43,14 +44,12 @@ pub fn setup_logger() -> Result<(), SetLoggerError> {
Dispatch::new()
.format(|out, message, record| {
match record.metadata().target() {
- "command:stdout" => {
- out.finish(format_args!("{} {}", ">>".cyan(), message.to_string()));
- return;
- }
- "command:stderr" => {
- out.finish(format_args!("{} {}", ">>".red(), message.to_string()));
- return;
- }
+ // command output logging
+ "command:stdout" => out.finish(format_args!("{} {}", ">>".cyan(), message.to_string())),
+ "command:stderr" => out.finish(format_args!("{} {}", ">>".red(), message.to_string())),
+ // this target means, it's an item and not a log.
+ "item" => out.finish(format_args!("{} {}", "*".blue(), message.to_string())),
+ // default logging
_ => out.finish(format_args!("{}", format_regular(message.to_string(), record))),
}
})
diff --git a/pkgr/src/main.rs b/pkgr/src/main.rs
index 0191923..3aff896 100644
--- a/pkgr/src/main.rs
+++ b/pkgr/src/main.rs
@@ -1,16 +1,27 @@
-use crate::commands::Cli;
-
use clap::Parser;
-
use log::trace;
+use crate::commands::Cli;
+/// pkgr's commands.
mod commands;
+/// Logging implementations for pkgr.
mod logging;
+/// Package and helpers.
mod package;
+/// Repo and helpers
+mod repo;
+/// Process wrapper with logging wrapper.
mod process;
+/// tmpfs wrapper.
mod tmpfs;
+/// pkgr's optional config.
mod config;
+/// custom types used by pkgr
+mod types;
+/// utils
+mod util;
+mod api;
thread_local! {
static CONFIG: config::Config = config::Config::from_path("/etc/pkgr.toml")
@@ -18,7 +29,8 @@ thread_local! {
}
fn main() {
- logging::setup_logger().expect("Unable to setup logger.");
+ logging::setup_logger()
+ .expect("unable to set logger.");
#[cfg(not(debug_assertions))]
{
diff --git a/pkgr/src/package/fetch.rs b/pkgr/src/package/fetch.rs
deleted file mode 100644
index bc385ea..0000000
--- a/pkgr/src/package/fetch.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-use crate::package::identifier::PackageLocator;
-use pkgfile::PKGFile;
-use reqwest::blocking::get;
-use std::error::Error;
-use std::fmt::Display;
-use std::io::Read;
-
-#[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_by_path>(path: S) -> Result {
- std::fs::read(path.into())
- .map_err(|e| FetchError::IOError(e))
- .and_then(|bytes| PKGFile::try_from(bytes).map_err(|_| FetchError::ParseError))
-}
-
-pub fn fetch_by_uri>(uri: S) -> Result {
- // get file contents as bytes
- let mut bytes = Vec::new();
- match get(uri.into()) {
- Ok(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),
- }
-}
-
-pub fn fetch_by_package_locator(_package_locator: PackageLocator) -> Result {
- // TODO: search index for package locator
- Ok(PKGFile::default())
-}
diff --git a/pkgr/src/package/identifier.rs b/pkgr/src/package/identifier.rs
index 3fde5e7..e39ac87 100644
--- a/pkgr/src/package/identifier.rs
+++ b/pkgr/src/package/identifier.rs
@@ -1,7 +1,9 @@
use std::error::Error;
+use std::fmt::Formatter;
use std::str::FromStr;
use regex::Regex;
+use serde::Serializer;
#[derive(Debug, Clone)]
pub enum PackageIdentifierError {
@@ -22,11 +24,29 @@ impl std::fmt::Display for PackageIdentifierError {
impl Error for PackageIdentifierError {}
-#[derive(Debug, Clone)]
+#[derive(Clone, Eq, PartialEq)]
pub enum PackageIdentifier {
PackageLocator(PackageLocator),
URI(String),
- Path(String),
+ Path(String)
+}
+
+impl std::fmt::Debug for PackageIdentifier {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ if f.alternate() {
+ match self {
+ PackageIdentifier::PackageLocator(pl) => write!(f, "PackageLocator({:#?})", pl),
+ PackageIdentifier::URI(uri) => write!(f, "URI({:?})", uri),
+ PackageIdentifier::Path(path) => write!(f, "Path({:?})", path),
+ }
+ } else {
+ match self {
+ PackageIdentifier::PackageLocator(pl) => write!(f, "PL: {:?}", pl),
+ PackageIdentifier::URI(uri) => write!(f, "URI: {:?}", uri),
+ PackageIdentifier::Path(path) => write!(f, "Path: {:?}", path),
+ }
+ }
+ }
}
impl std::fmt::Display for PackageIdentifier {
@@ -39,7 +59,7 @@ impl std::fmt::Display for PackageIdentifier {
}
}
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PackageLocator {
pub name: String,
pub version: Option,
@@ -140,8 +160,9 @@ impl FromStr for PackageLocator {
}
}
-impl From<(String, String)> for PackageLocator {
- fn from((name, locate_str): (String, String)) -> Self {
+impl TryFrom<(String, String)> for PackageLocator {
+ type Error = PackageIdentifierError;
+ fn try_from((name, locate_str): (String, String)) -> Result {
// name = "pkg"
// locate_str = "1.0.0:tag1,tag2" or "1.0.0" or "tag1,tag2"
let mut version = None;
@@ -165,10 +186,10 @@ impl From<(String, String)> for PackageLocator {
);
}
- PackageLocator {
+ Ok(PackageLocator {
name,
version,
tags,
- }
+ })
}
}
diff --git a/pkgr/src/package/installer/errors.rs b/pkgr/src/package/installer/errors.rs
index 9e86f1b..332a06f 100644
--- a/pkgr/src/package/installer/errors.rs
+++ b/pkgr/src/package/installer/errors.rs
@@ -1,21 +1,26 @@
use std::fmt::Display;
+use std::io;
#[derive(Debug)]
pub enum BinError {
UnpackError(String),
+ IOError(io::Error),
+ Cancelled,
}
impl Display for BinError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BinError::UnpackError(e) => write!(f, "Unpack error: {}", e),
+ BinError::IOError(e) => write!(f, "IO error: {}", e),
+ BinError::Cancelled => write!(f, "Cancelled by user"),
}
}
}
#[derive(Debug)]
pub enum BuildError {
- InvalidManifest,
+ InvalidManifest(String),
}
impl Display for BuildError {
@@ -30,17 +35,15 @@ impl Display for BuildError {
pub enum InstallError {
BuildError(BuildError),
BinError(BinError),
- InvalidManifest,
- Generic,
+ InvalidManifest(String),
}
impl ToString for InstallError {
fn to_string(&self) -> String {
match self {
- InstallError::BuildError(e) => format!("Build error: \n{}", e),
- InstallError::BinError(e) => format!("Bin error: \n{}", e),
- InstallError::InvalidManifest => "Invalid manifest".to_string(),
- InstallError::Generic => "Install error".to_string(),
+ InstallError::BuildError(e) => format!("{}", e),
+ InstallError::BinError(e) => format!("{}", e),
+ InstallError::InvalidManifest(s) => format!("{}", s),
}
}
}
diff --git a/pkgr/src/package/installer/mod.rs b/pkgr/src/package/installer/mod.rs
index a28f849..4dbeab0 100644
--- a/pkgr/src/package/installer/mod.rs
+++ b/pkgr/src/package/installer/mod.rs
@@ -1,13 +1,17 @@
-use crate::tmpfs::TempDir;
+use std::io;
+use std::fs::remove_dir_all;
+use std::path::Path;
+
+use log::{debug, trace};
+
use errors::{BinError, BuildError, InstallError};
-use log::{debug, error, info, trace};
use manifest::Manifest;
use pkgfile::PKGFile;
-
-use std::process::exit;
use crate::CONFIG;
-use crate::package::identifier::{PackageIdentifier, PackageLocator};
-use crate::package::Package;
+
+use crate::tmpfs::TempDir;
+use crate::types::fetch::TryFetch;
+use crate::util::fs::copy_recursively;
pub mod errors;
@@ -37,11 +41,14 @@ impl PackageInstaller {
if !self.pkgfile.has_data() {
return Err(BinError::UnpackError("package has no data".to_string()));
}
- if std::path::Path::new(&path).exists() {
- return Err(BinError::UnpackError(format!(
- "path already exists: {}",
- path
- )));
+ let path = std::path::Path::new(&path);
+ if path.exists() {
+ trace!("cache already exists..");
+ debug!("removing cache dir...");
+ match remove_dir_all(path) {
+ Ok(_) => debug!("removed cache directory"),
+ Err(e) => return Err(BinError::UnpackError(format!("unable to remove directory ({}): {}", path.to_str().unwrap_or("unknown"), e.to_string())))
+ }
}
tar::Archive::new(self.pkgfile.data.as_slice())
.unpack(&path)
@@ -52,39 +59,32 @@ impl PackageInstaller {
let mut tmpdir = TempDir::default();
tmpdir.push(&self.manifest.package.name);
trace!("extracting package into: {}", tmpdir.to_string());
- match self.extract_to(tmpdir.to_string()) {
- Ok(_) => {}
- Err(e) => return Err(e),
+ if let Err(e) = self.extract_to(tmpdir.to_string()) {
+ return Err(e);
}
debug!("extracted package in: {}", tmpdir.to_string());
+ match self.apply_overlay() {
+ Ok(_) => Ok(()),
+ Err(e) => Err(BinError::IOError(e))
+ }
+ }
+
+ fn apply_overlay(&self) -> Result<(), io::Error> {
+ let mut tmpdir = TempDir::default();
+ tmpdir.push(&self.manifest.package.name);
+ tmpdir.push(&self.manifest.bin.clone().unwrap().root);
+ if let Err(e) = copy_recursively(&tmpdir.path(), Path::new("/")) {
+ return Err(e);
+ }
Ok(())
}
fn build(&self) -> Result<(), BuildError> {
if let None = self.manifest.build.clone() {
- return Err(BuildError::InvalidManifest);
+ return Err(BuildError::InvalidManifest(String::from("No build manifest")));
}
let build_manifest = self.manifest.build.clone().unwrap();
// TODO: Check dependencies
- for pkg_tuple in build_manifest.dependencies {
- let mut pkg = Package::fetch(
- PackageIdentifier::PackageLocator(
- PackageLocator::from(pkg_tuple)
- )
- ).expect("no pkg");
-
- if !pkg.is_installed() {
- match pkg.install(CONFIG.with(|c| {
- c.build_by_default
- })) {
- Ok(_) => { info!("Installed dependency: \"{}\"", pkg.manifest().package.name) },
- Err(_) => {
- error!("Could not install dependency: \"{}\"", pkg.identifier);
- exit(1);
- }
- }
- }
- }
Ok(())
}
@@ -95,7 +95,7 @@ impl PackageInstaller {
if let None = self.manifest.bin {
self.install_type = InstallType::Build;
if let None = self.manifest.build {
- return Err(InstallError::InvalidManifest);
+ return Err(InstallError::InvalidManifest(String::from("no bin or build manifest")));
}
}
}
@@ -103,17 +103,23 @@ impl PackageInstaller {
if let None = self.manifest.build {
self.install_type = InstallType::Bin;
if let None = self.manifest.bin {
- return Err(InstallError::InvalidManifest);
+ return Err(InstallError::InvalidManifest(String::from("no build or bin manifest")));
}
}
}
}
- match self.install_type {
+ let r = match self.install_type {
InstallType::Bin => self.bin()
.map_err(|e| InstallError::BinError(e)),
InstallType::Build => self.build()
.map_err(|e| InstallError::BuildError(e)),
+ };
+
+ if let Err(e) = r {
+ return Err(e);
}
+
+ Ok(())
}
}
diff --git a/pkgr/src/package/mod.rs b/pkgr/src/package/mod.rs
index 79de18d..6378cef 100644
--- a/pkgr/src/package/mod.rs
+++ b/pkgr/src/package/mod.rs
@@ -1,19 +1,31 @@
-use log::{info, trace};
+use std::fmt::Display;
+use std::path::Path;
+use std::process::exit;
+
+use log::{debug, error, info, trace};
+use reqwest::blocking::get;
+
+use manifest::build::Build;
+use pkgfile::PKGFile;
+
+use crate::package::identifier::{PackageIdentifier, PackageLocator};
+use crate::types::fetch::TryFetch;
-pub mod fetch;
pub mod identifier;
pub mod installer;
+pub mod queue;
+#[derive(Eq, PartialEq)]
pub struct Package {
- pub identifier: identifier::PackageIdentifier,
- pub pkgfile: pkgfile::PKGFile,
+ pub identifier: PackageIdentifier,
+ pub pkgfile: PKGFile,
is_installed: bool,
is_indexed: bool,
}
impl Package {
/// Create a new package from a package identifier and a package file.
- pub fn new(identifier: identifier::PackageIdentifier, pkgfile: pkgfile::PKGFile) -> Package {
+ pub fn new(identifier: PackageIdentifier, pkgfile: PKGFile) -> Package {
Package {
identifier,
pkgfile,
@@ -22,34 +34,60 @@ impl Package {
}
}
- /// Fetch a package from a package identifier.
- pub fn fetch(
- package_identifier: identifier::PackageIdentifier,
- ) -> Result {
- match &package_identifier {
- identifier::PackageIdentifier::Path(path) => {
- trace!("fetching package from path: {}", path);
- let pkgfile = fetch::fetch_by_path(path).unwrap();
- Ok(Package::new(package_identifier, pkgfile))
- }
- identifier::PackageIdentifier::URI(url) => {
- trace!("fetching package from uri: {}", url);
- let pkgfile = fetch::fetch_by_uri(url).unwrap();
- Ok(Package::new(package_identifier, pkgfile))
- }
- identifier::PackageIdentifier::PackageLocator(locator) => {
- trace!("fetching package from locator: {}", locator);
- let pkgfile = fetch::fetch_by_package_locator(locator.clone()).unwrap();
- Ok(Package::new(package_identifier, pkgfile))
- }
- }
- }
-
/// Get the package manifest.
pub fn manifest(&self) -> manifest::Manifest {
manifest::Manifest::try_from(self.pkgfile.manifest.clone()).unwrap()
}
+ /// Get package dependencies
+ pub fn dependencies(&self) -> Vec {
+ let mut dependencies = vec![];
+ for dependency in self.manifest().dependencies {
+ let pkglocate = if let Ok(pl) = PackageLocator::try_from(dependency.clone()) {
+ trace!("parsed pl successfully...");
+ pl
+ } else {
+ error!("Could not parse package locator: {:?} in dependencies.", &dependency);
+ exit(1);
+ };
+ let pkg = match Package::try_fetch(PackageIdentifier::PackageLocator(pkglocate)) {
+ Ok(p) => p,
+ Err(e) => {
+ error!("Could not fetch dependency: {}", e);
+ exit(1);
+ }
+ };
+ dependencies.push(pkg);
+ }
+ dependencies
+ }
+
+ pub fn build_dependencies(&self) -> Vec {
+ let mut dependencies = vec![];
+ for dependency in self
+ .manifest()
+ .build
+ .unwrap_or(Build::default())
+ .dependencies {
+ let pkglocate = if let Ok(pl) = PackageLocator::try_from(dependency.clone()) {
+ trace!("parsed pl successfully...");
+ pl
+ } else {
+ error!("Could not parse package locator: {:?} in dependencies.", &dependency);
+ exit(1);
+ };
+ let pkg = match Package::try_fetch(PackageIdentifier::PackageLocator(pkglocate)) {
+ Ok(p) => p,
+ Err(e) => {
+ error!("Could not fetch dependency: {}", e);
+ exit(1);
+ }
+ };
+ dependencies.push(pkg);
+ }
+ dependencies
+ }
+
/// Install the package.
pub fn install(&mut self, build: bool) -> Result<(), installer::errors::InstallError> {
let manifest = self.manifest();
@@ -96,3 +134,57 @@ impl Package {
unimplemented!();
}
}
+
+#[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 TryFetch for Package {
+ type Error = FetchError;
+
+ /// Fetch a package from a package identifier.
+ fn try_fetch(query: PackageIdentifier) -> Result {
+ trace!("Fetching: {query:#?}");
+ let pkgfile = match &query {
+ PackageIdentifier::Path(s) => match PKGFile::try_from(Path::new(&s)) {
+ Ok(p) => Ok(p),
+ Err(e) => Err(FetchError::ParseError)
+ },
+ PackageIdentifier::URI(s) => {
+ let mut bytes = Vec::new();
+ debug!("sending GET request...");
+ match get::(s.into()) {
+ Ok(response) => {
+ debug!("Got response!");
+ if let Ok(b) = response.bytes() {
+ bytes.extend(b);
+ }
+ }
+ 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(l) => unimplemented!()
+ };
+
+ pkgfile
+ .map(|p| Package::new(query, p))
+ }
+}
\ No newline at end of file
diff --git a/pkgr/src/package/queue.rs b/pkgr/src/package/queue.rs
new file mode 100644
index 0000000..601d8da
--- /dev/null
+++ b/pkgr/src/package/queue.rs
@@ -0,0 +1,51 @@
+use std::fmt::{Display, Formatter};
+
+use log::trace;
+
+use crate::package::Package;
+
+pub struct PackageQueue {
+ packages: Vec,
+}
+
+impl PackageQueue {
+ pub fn new() -> Self {
+ PackageQueue {
+ packages: vec![]
+ }
+ }
+
+ pub fn add_package(&mut self, package: Package, build: bool) {
+ let dependencies = package.dependencies();
+ for dependency in dependencies {
+ trace!("Checking package: {}", &dependency.identifier);
+ if self.packages.contains(&dependency) {
+ continue;
+ }
+ trace!("Adding package: {}", &dependency.identifier);
+ self.packages.push(dependency);
+ }
+ if !self.packages.contains(&package) {
+ self.packages.push(package);
+ }
+ }
+
+ pub fn install(&mut self, build: bool) {
+ self.packages
+ .iter_mut()
+ .for_each(|pkg| {
+ pkg.install(build).expect("TODO: panic message");
+ });
+ }
+}
+
+impl Display for PackageQueue {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ for pkg in &self.packages {
+ if let Err(e) = write!(f, "{}", pkg.identifier) {
+ return Err(e);
+ }
+ }
+ write!(f, "")
+ }
+}
\ No newline at end of file
diff --git a/pkgr/src/process.rs b/pkgr/src/process.rs
index f6f4e21..fbaf567 100644
--- a/pkgr/src/process.rs
+++ b/pkgr/src/process.rs
@@ -1,7 +1,7 @@
use std::io::{BufRead, BufReader};
use std::process::Command;
-use log::{info, trace};
+use log::{info, trace};
pub struct Process {
pub command: Vec,
diff --git a/pkgr/src/repo/index/index_package.rs b/pkgr/src/repo/index/index_package.rs
new file mode 100644
index 0000000..ca60160
--- /dev/null
+++ b/pkgr/src/repo/index/index_package.rs
@@ -0,0 +1,18 @@
+use std::io;
+use std::path::Path;
+use serde::{Deserialize, Serialize};
+use url::Url;
+use pkgfile::PKGFile;
+use crate::api::Query;
+use crate::CONFIG;
+use crate::package::Package;
+use crate::repo::index::RepoIndex;
+
+/// This struct solely exists for indexing and has no real functionality.
+#[derive(Serialize, Deserialize)]
+pub struct IndexPackage {
+ name: String,
+ versions: Vec,
+ tags: Vec,
+ uri: Url
+}
\ No newline at end of file
diff --git a/pkgr/src/repo/index/mod.rs b/pkgr/src/repo/index/mod.rs
new file mode 100644
index 0000000..fc516b6
--- /dev/null
+++ b/pkgr/src/repo/index/mod.rs
@@ -0,0 +1,58 @@
+use std::collections::HashMap;
+use std::io;
+use std::path::Path;
+use serde::{Deserialize, Serialize};
+use crate::api::Request;
+use crate::CONFIG;
+use crate::repo::index::index_package::IndexPackage;
+use crate::repo::Repo;
+use crate::types::fetch::{Fetch, TryFetch};
+use crate::util::create_uuid;
+
+pub mod index_package;
+
+#[derive(Serialize, Deserialize)]
+pub struct RepoIndex {
+ origin_repo: String,
+ packages: HashMap
+}
+
+impl RepoIndex {
+ // /// Fetch existing index or create a new one.
+ // pub fn from_repo(repo: Repo) -> Self {
+ //
+ // }
+ //
+ // /// Create new index.
+ // pub fn create_with_repo(repo: Repo) -> Self {
+ // }
+
+ /// Get repo
+ pub fn get_repo(&self) -> io::Result {
+ Ok(Repo::from_name(&self.origin_repo)?)
+ }
+
+ pub fn from_path(path: &str) -> Result {
+ match std::fs::read_to_string(path) {
+ Ok(s) => match toml::from_str(&s) {
+ Ok(c) => Ok(c),
+ Err(e) => Err(format!("failed to parse config: {}", e)),
+ },
+ Err(e) => Err(format!("failed to read config: {}", e)),
+ }
+ }
+}
+
+impl TryFetch for RepoIndex {
+ type Error = ();
+ /// Fetch
+ fn try_fetch(query: Repo) -> Result {
+ let path = CONFIG.with(|c| c.storage.index_dir.clone()) + query.uri.as_str() + ".toml";
+ if Path::new(path.as_str()).exists() {
+ RepoIndex::from_path(path.as_str())
+ .map_err(|_| ())
+ } else {
+ Err(())
+ }
+ }
+}
\ No newline at end of file
diff --git a/pkgr/src/repo/mod.rs b/pkgr/src/repo/mod.rs
new file mode 100644
index 0000000..d0c3e88
--- /dev/null
+++ b/pkgr/src/repo/mod.rs
@@ -0,0 +1,42 @@
+use std::io;
+use std::io::{Error, ErrorKind};
+use std::path::Path;
+use url::Url;
+use crate::CONFIG;
+use crate::config::repos::RepoFile;
+
+/// Indexed repos
+pub mod index;
+
+pub struct Repo {
+ name: String,
+ uri: Url,
+}
+
+impl Repo {
+ pub fn from_name(name: &String) -> io::Result {
+ let r = RepoFile::from_path(CONFIG.with(|c| c.storage.repo_file.clone()).as_str())
+ .map_err(|e| Error::new(ErrorKind::Other, e))?;
+ let r = r
+ .repos
+ .get(name)
+ .ok_or(Error::new(ErrorKind::InvalidData, "Could not get repo"))?;
+ Ok(Repo {
+ name: r.name.clone(),
+ uri: r.uri.clone()
+ })
+ }
+
+ pub fn get_name(&self) -> String {
+ self.name.clone()
+ }
+
+ pub fn get_uri(&self) -> Url {
+ self.uri.clone()
+ }
+
+ /// Fetch indexed repo
+ pub fn get_index(&self) {
+
+ }
+}
\ No newline at end of file
diff --git a/pkgr/src/tmpfs.rs b/pkgr/src/tmpfs.rs
index d21aff7..7d68892 100644
--- a/pkgr/src/tmpfs.rs
+++ b/pkgr/src/tmpfs.rs
@@ -1,20 +1,32 @@
use std::path::PathBuf;
+
use crate::CONFIG;
+#[derive(Clone)]
pub struct TempDir {
path: PathBuf,
}
impl TempDir {
pub fn new(path: PathBuf) -> TempDir {
+ let pbs: String = path.to_str().unwrap().into();
+ let path = expanduser::expanduser(&pbs).unwrap();
if !path.exists() {
std::fs::create_dir_all(&path).unwrap();
}
TempDir { path }
}
+ pub fn path(&self) -> PathBuf {
+ self.path.clone()
+ }
+
pub fn push>(&mut self, path: S) {
- self.path.push(path.into());
+ let mut path_str = path.into();
+ if path_str.starts_with('/') {
+ path_str = path_str[1..].to_string();
+ }
+ self.path.push(path_str);
}
}
diff --git a/pkgr/src/types/fetch.rs b/pkgr/src/types/fetch.rs
new file mode 100644
index 0000000..d3b7eac
--- /dev/null
+++ b/pkgr/src/types/fetch.rs
@@ -0,0 +1,10 @@
+/// Get a result from an external source
+pub trait Fetch {
+ fn fetch(query: Q) -> R;
+}
+
+/// Try to get a result from an external source
+pub trait TryFetch {
+ type Error;
+ fn try_fetch(query: Q) -> Result;
+}
\ No newline at end of file
diff --git a/pkgr/src/types/mod.rs b/pkgr/src/types/mod.rs
new file mode 100644
index 0000000..c798af5
--- /dev/null
+++ b/pkgr/src/types/mod.rs
@@ -0,0 +1 @@
+pub mod fetch;
\ No newline at end of file
diff --git a/pkgr/src/util/fs.rs b/pkgr/src/util/fs.rs
new file mode 100644
index 0000000..815b950
--- /dev/null
+++ b/pkgr/src/util/fs.rs
@@ -0,0 +1,41 @@
+use std::{fs, io};
+use std::fs::DirEntry;
+use std::path::Path;
+
+use log::trace;
+
+pub fn visit_dirs(dir: &Path, cb: &dyn Fn(&DirEntry)) -> std::io::Result<()> {
+ if dir.is_dir() {
+ for entry in std::fs::read_dir(dir)? {
+ let entry = entry?;
+ let path = entry.path();
+ if path.is_dir() {
+ visit_dirs(&path, cb)?;
+ } else {
+ cb(&entry);
+ }
+ }
+ }
+ Ok(())
+}
+
+pub fn copy_recursively(source: &Path, target: &Path) -> io::Result<()> {
+ if source.is_file() {
+ trace!("source: {:?}, target: {:?}", source, target);
+ fs::copy(source, target)?;
+ } else if source.is_dir() {
+ if !target.exists() {
+ fs::create_dir(target)?;
+ }
+
+ for entry in fs::read_dir(source)? {
+ let entry = entry?;
+ let file_name = entry.file_name();
+ let source_path = entry.path();
+ let target_path = target.join(&file_name);
+
+ copy_recursively(&source_path, &target_path)?;
+ }
+ }
+ Ok(())
+}
\ No newline at end of file
diff --git a/pkgr/src/util/mod.rs b/pkgr/src/util/mod.rs
new file mode 100644
index 0000000..25c313c
--- /dev/null
+++ b/pkgr/src/util/mod.rs
@@ -0,0 +1,9 @@
+pub mod prompts;
+/// Helpers for fs
+pub mod fs;
+
+/// Create a UUID
+pub fn create_uuid() -> String {
+ // TODO
+ String::from("rand")
+}
\ No newline at end of file
diff --git a/pkgr/src/util/prompts.rs b/pkgr/src/util/prompts.rs
new file mode 100644
index 0000000..9dcae5d
--- /dev/null
+++ b/pkgr/src/util/prompts.rs
@@ -0,0 +1,33 @@
+use std::io::Write;
+
+use log::trace;
+
+pub fn is_noninteractive() -> bool {
+ if let Ok(v) = std::env::var("PKGR_NON_INTERACTIVE") {
+ trace!("PKGR_NON_INTERACTIVE={}", v);
+ match v.as_str() {
+ "1" => true,
+ "true" => true,
+ _ => false
+ }
+ } else { false }
+}
+
+pub fn prompt_bool>(prompt: S, default: bool) -> bool {
+ if is_noninteractive() { return default; }
+ print!("{} [{}]: ", prompt.into(), if default { "Y/n" } else { "y/N" });
+ match std::io::stdout().flush() {
+ Ok(_) => (),
+ _ => println!()
+ };
+
+ let mut input = String::new();
+ std::io::stdin().read_line(&mut input).expect("Failed to read input.");
+ let answer = input.trim().to_lowercase();
+
+ if answer.is_empty() {
+ default
+ } else {
+ answer == "y" || answer == "yes"
+ }
+}
\ No newline at end of file