Add install_local function to cli app

This commit is contained in:
Nathan Fisher 2023-04-15 01:21:12 -04:00
parent 7fe884cd3e
commit f479dd28ad
3 changed files with 73 additions and 32 deletions

View File

@ -178,6 +178,7 @@ pub fn install() -> Command {
.help("install packages into a different root") .help("install packages into a different root")
.short('r') .short('r')
.long("root") .long("root")
.default_value("/")
.value_hint(ValueHint::DirPath) .value_hint(ValueHint::DirPath)
.num_args(1), .num_args(1),
]) ])

View File

@ -1,15 +1,17 @@
#![warn(clippy::all, clippy::pedantic)] #![warn(clippy::all, clippy::pedantic)]
mod cli; mod cli;
use { use {
clap::ArgMatches, clap::ArgMatches,
cli::cli, cli::cli,
hpk::{CreationError, Creator, Dependency, Message, Specs, Version}, hpk::{CreationError, Creator, Dependency, InstallMessage, Installer, Message, Specs, Version},
indicatif::{ProgressBar, ProgressStyle}, indicatif::{ProgressBar, ProgressStyle},
ron::ser::{to_writer_pretty, PrettyConfig}, ron::ser::{to_writer_pretty, PrettyConfig},
std::{ std::{
env, env,
error::Error, error::Error,
ffi::OsStr, ffi::OsStr,
fmt,
fs::File, fs::File,
io::{self, BufWriter, ErrorKind}, io::{self, BufWriter, ErrorKind},
path::PathBuf, path::PathBuf,
@ -107,6 +109,42 @@ fn create(matches: &ArgMatches) -> Result<(), Box<dyn Error>> {
} }
} }
fn install_local<P: AsRef<OsStr> + fmt::Display>(
archive: P,
root: P,
) -> Result<(), Box<dyn Error>> {
let (sender, receiver) = mpsc::channel();
let pb = ProgressBar::new(0);
pb.set_style(ProgressStyle::with_template(TEMPLATE).unwrap());
pb.println(format!("Installing package {}", archive,));
let installer = Installer::new_for_file(root, archive)?;
let handle = thread::spawn(move || {
for msg in receiver.iter() {
match msg {
InstallMessage::ArchiveLen(len) => pb.set_length(len.try_into().unwrap()),
InstallMessage::LinkCreated(link) => {
pb.inc(1);
}
InstallMessage::DirectoryCreated(dir) => {
pb.inc(1);
}
InstallMessage::FileInstalled(file) => {
pb.inc(1);
}
_ => {}
}
}
});
installer.install(sender)?;
match handle.join() {
Ok(hooks) => {
println!("hooks: {hooks:?}");
Ok(())
}
Err(_) => Err(io::Error::new(ErrorKind::Other, "Unknown thread error").into()),
}
}
fn init(matches: &ArgMatches) -> Result<PathBuf, Box<dyn Error>> { fn init(matches: &ArgMatches) -> Result<PathBuf, Box<dyn Error>> {
let specsfile = PathBuf::from("package.specs"); let specsfile = PathBuf::from("package.specs");
let cfg = PrettyConfig::new().struct_names(true); let cfg = PrettyConfig::new().struct_names(true);

View File

@ -57,37 +57,27 @@ pub struct Installer<T: io::Read> {
/// The filesystem root under which to install this package. /// The filesystem root under which to install this package.
/// Normally this is "/". /// Normally this is "/".
root: PathBuf, root: PathBuf,
package: Package,
reader: T, reader: T,
} }
impl Installer<Decoder<'_, BufReader<File>>> { impl Installer<Decoder<'_, BufReader<File>>> {
/// creates a new installer from an archive path /// creates a new installer from an archive path
pub fn new_for_file<P: AsRef<OsStr>>( pub fn new_for_file<P: AsRef<OsStr>>(root: P, archive: P) -> Result<Self, io::Error> {
root: P,
package: Package,
archive: P,
) -> Result<Self, io::Error> {
let root = Path::new(&root).to_path_buf(); let root = Path::new(&root).to_path_buf();
let fd = File::open(Path::new(&archive))?; let fd = File::open(Path::new(&archive))?;
let decoder = Decoder::new(fd)?; let decoder = Decoder::new(fd)?;
Ok(Self::new(root, package, decoder)) Ok(Self::new(root, decoder))
} }
} }
impl<T: io::Read> Installer<T> { impl<T: io::Read> Installer<T> {
/// Creates a new installer from an open archive stream /// Creates a new installer from an open archive stream
pub fn new<P: AsRef<OsStr>>(root: P, package: Package, reader: T) -> Self { pub fn new<P: AsRef<OsStr>>(root: P, reader: T) -> Self {
let root = Path::new(&root).to_path_buf(); let root = Path::new(&root).to_path_buf();
Self { Self { root, reader }
root,
package,
reader,
}
} }
pub fn install(mut self, sender: Sender<InstallMessage>) -> Result<Vec<Hooks>, InstallError> { pub fn install(self, sender: Sender<InstallMessage>) -> Result<Vec<Hooks>, InstallError> {
let mut hooks = self.init_hooks();
let reader = Decoder::new(self.reader)?; let reader = Decoder::new(self.reader)?;
let mut archive = Archive::read(reader)?; let mut archive = Archive::read(reader)?;
sender.send(InstallMessage::ArchiveRead)?; sender.send(InstallMessage::ArchiveRead)?;
@ -95,15 +85,19 @@ impl<T: io::Read> Installer<T> {
Some(node) => node, Some(node) => node,
None => return Err(InstallError::MissingManifest), None => return Err(InstallError::MissingManifest),
}; };
let mut buf = vec![];
pr_node.write(&mut buf)?;
let package: Package = ron::de::from_bytes(&buf)?;
let mut hooks = init_hooks(&package, &self.root);
let len = archive.nodes.len(); let len = archive.nodes.len();
sender.send(InstallMessage::ArchiveLen(len))?; sender.send(InstallMessage::ArchiveLen(len))?;
let mut db_pkgdir = crate::get_dbdir(Some(self.root.clone())); let mut db_pkgdir = crate::get_dbdir(Some(self.root.clone()));
db_pkgdir.push(&self.package.name); db_pkgdir.push(&package.name);
if !db_pkgdir.exists() { if !db_pkgdir.exists() {
fs::create_dir_all(&db_pkgdir)?; fs::create_dir_all(&db_pkgdir)?;
} }
pop_appstream(&mut archive, &db_pkgdir)?; pop_appstream(&mut archive, &db_pkgdir)?;
pop_pinstall(&mut archive, &mut hooks, &self.package.name, &self.root)?; pop_pinstall(&mut archive, &mut hooks, &package.name, &self.root)?;
let mut db_file = db_pkgdir; let mut db_file = db_pkgdir;
db_file.push("package.ron"); db_file.push("package.ron");
if db_file.exists() { if db_file.exists() {
@ -113,7 +107,6 @@ impl<T: io::Read> Installer<T> {
let writer = BufWriter::new(fd); let writer = BufWriter::new(fd);
pr_node.write(writer)?; pr_node.write(writer)?;
let root = &self.root; let root = &self.root;
let package = &self.package;
let hooks = Mutex::new(hooks); let hooks = Mutex::new(hooks);
let s = sender.clone(); let s = sender.clone();
archive archive
@ -175,22 +168,22 @@ impl<T: io::Read> Installer<T> {
})?; })?;
Ok(hooks.into_inner()?) Ok(hooks.into_inner()?)
} }
}
fn init_hooks(&mut self) -> Vec<Hooks> { fn init_hooks(package: &Package, root: &Path) -> Vec<Hooks> {
let mut hooks = Vec::<Hooks>::new(); let mut hooks = Vec::<Hooks>::new();
if let Some(ref users) = self.package.users { if let Some(ref users) = package.users {
users users
.iter() .iter()
.for_each(|u| hooks.push((u.clone(), Some(self.root.clone())).into())); .for_each(|u| hooks.push((u.clone(), Some(root.to_path_buf())).into()));
} }
if let Some(ref groups) = self.package.groups { if let Some(ref groups) = package.groups {
groups groups
.iter() .iter()
.for_each(|g| hooks.push((g.clone(), Some(self.root.clone())).into())); .for_each(|g| hooks.push((g.clone(), Some(root.to_path_buf())).into()));
} }
hooks hooks
} }
}
fn extract_entry( fn extract_entry(
entry: &Entry, entry: &Entry,
@ -272,6 +265,7 @@ mod error {
use super::InstallMessage; use super::InstallMessage;
use crate::Hooks; use crate::Hooks;
use hpk_package::tar; use hpk_package::tar;
use ron::error::SpannedError;
use std::{ use std::{
error::Error, error::Error,
fmt, io, fmt, io,
@ -286,6 +280,7 @@ mod error {
pub enum InstallError { pub enum InstallError {
Fmt(fmt::Error), Fmt(fmt::Error),
IO(io::Error), IO(io::Error),
RonError(SpannedError),
SendError(SendError<InstallMessage>), SendError(SendError<InstallMessage>),
Tar(tar::Error), Tar(tar::Error),
ChecksumMismatch, ChecksumMismatch,
@ -307,6 +302,7 @@ mod error {
Self::IO(e) => Some(e), Self::IO(e) => Some(e),
Self::Tar(e) => Some(e), Self::Tar(e) => Some(e),
Self::SendError(e) => Some(e), Self::SendError(e) => Some(e),
Self::RonError(e) => Some(e),
_ => None, _ => None,
} }
} }
@ -342,6 +338,12 @@ mod error {
} }
} }
impl From<SpannedError> for InstallError {
fn from(value: SpannedError) -> Self {
Self::RonError(value)
}
}
impl From<FromUtf8Error> for InstallError { impl From<FromUtf8Error> for InstallError {
fn from(_value: FromUtf8Error) -> Self { fn from(_value: FromUtf8Error) -> Self {
Self::Utf8 Self::Utf8