diff --git a/README.md b/README.md index 6a30638cfe32a61bb4834e226f61040841f8fb0d..e2623325ed8123cd1ef2d77a0184bea56eed1e34 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,22 @@ # Redox OS pkgutils -This repository contains utilities for package management on Redox. Additional related tools are included. +This repository contains utilities for package management on Redox. Currently, only `pkg` is included. [](https://travis-ci.org/redox-os/pkgutils) [](./LICENSE) -**Currently included:** +## `pkg` +The command `pkg` is the primary package management utility for Redox OS. In its current state, `pkg` supports the following commands: -- `pkg` +| Command | Functionality | +|-----------|--------------------------------| +| `clean` | Clean an extracted package | +| `create` | Create a package | +| `extract` | Extract a package | +| `fetch` | Download a package | +| `install` | Install a package | +| `list` | List package contents | +| `sign` | Get a file signature | +| `upgrade` | Upgrade all installed packages | + +For more detailed information on how to invoke these subcommands, please run `pkg help <SUBCOMMAND>` in your terminal. diff --git a/build.rs b/build.rs index 97131b906894f49e3f1e75ccb0b95daf7ec32a80..ad970ecf9aaa0069afac6c4b8aa39227b8b14844 100644 --- a/build.rs +++ b/build.rs @@ -1,5 +1,8 @@ use std::env; fn main() { - println!("cargo:rustc-env=PKG_DEFAULT_TARGET={}", env::var("TARGET").unwrap()); + println!( + "cargo:rustc-env=PKG_DEFAULT_TARGET={}", + env::var("TARGET").unwrap() + ); } diff --git a/src/bin/pkg.rs b/src/bin/pkg.rs index 0579c5c00f247ad4398da19649095ae513c104e7..68166e9fe0fc32b98e6c8daabf4f730a8de97971 100644 --- a/src/bin/pkg.rs +++ b/src/bin/pkg.rs @@ -1,12 +1,12 @@ -use pkgutils::{Database, Repo, Package, PackageDepends, PackageMeta, PackageMetaList}; -use std::{env, process}; +use clap::{App, Arg, SubCommand}; +use ordermap::OrderMap; +use pkgutils::{Database, Package, PackageDepends, PackageMeta, PackageMetaList, Repo}; use std::error::Error; use std::fs::{self, File}; use std::io::{self, Read}; use std::path::Path; -use version_compare::{VersionCompare, CompOp}; -use clap::{App, SubCommand, Arg}; -use ordermap::OrderMap; +use std::{env, process}; +use version_compare::{CompOp, VersionCompare}; fn upgrade(repo: Repo) -> io::Result<()> { let mut local_list = PackageMetaList::new(); @@ -39,11 +39,14 @@ fn upgrade(repo: Repo) -> io::Result<()> { Ok(cmp) => match cmp { CompOp::Lt => { upgrades.push((package.clone(), version.clone(), remote_version.to_string())); - }, - _ => () + } + _ => (), }, Err(_err) => { - println!("{}: version parsing error when comparing {} and {}", package, version, remote_version); + println!( + "{}: version parsing error when comparing {} and {}", + package, version, remote_version + ); } } } @@ -58,10 +61,7 @@ fn upgrade(repo: Repo) -> io::Result<()> { let line = liner::Context::new().read_line( "Do you want to upgrade these packages? (Y/n) ", None, - &mut liner::BasicCompleter::new(vec![ - "yes", - "no" - ]) + &mut liner::BasicCompleter::new(vec!["yes", "no"]), )?; match line.to_lowercase().as_str() { "" | "y" | "yes" => { @@ -75,7 +75,7 @@ fn upgrade(repo: Repo) -> io::Result<()> { for mut package in packages { package.install("/")?; } - }, + } _ => { println!("Cancelling upgrade."); } @@ -87,51 +87,95 @@ fn upgrade(repo: Repo) -> io::Result<()> { fn main() { let matches = App::new("pkg") - .arg(Arg::with_name("target") - .long("target") - .takes_value(true) - ).subcommand(SubCommand::with_name("clean") - .arg(Arg::with_name("package") - .multiple(true) - .required(true) - ) - ).subcommand(SubCommand::with_name("create") - .arg(Arg::with_name("package") - .multiple(true) - .required(true) - ) - ).subcommand(SubCommand::with_name("extract") - .arg(Arg::with_name("package") - .multiple(true) - .required(true) - ) - ).subcommand(SubCommand::with_name("fetch") - .arg(Arg::with_name("package") - .multiple(true) - .required(true) - ) - ).subcommand(SubCommand::with_name("install") - .arg(Arg::with_name("package") - .multiple(true) - .required(true) - ).arg(Arg::with_name("root") - .long("root") - .takes_value(true) - ) - ).subcommand(SubCommand::with_name("list") - .arg(Arg::with_name("package") - .multiple(true) - .required(true) - ) - ).subcommand(SubCommand::with_name("sign") - .arg(Arg::with_name("file") - .multiple(true) - .required(true) - ) - ).subcommand(SubCommand::with_name("upgrade") - ).get_matches(); + .about("A package management utility for Redox OS") + .arg( + Arg::with_name("target") + .help("The target architecture") + .long("target") + .takes_value(true), + ) + .subcommand( + SubCommand::with_name("clean") + .about("Clean an extracted package") + .arg( + Arg::with_name("package") + .help("The name of the package") + .multiple(true) + .required(true), + ), + ) + .subcommand( + SubCommand::with_name("create") + .about("Create a package") + .arg( + Arg::with_name("package") + .help("The name of the package") + .multiple(true) + .required(true), + ), + ) + .subcommand( + SubCommand::with_name("extract") + .about("Extract a package") + .arg( + Arg::with_name("package") + .help("The name of the package") + .multiple(true) + .required(true), + ), + ) + .subcommand( + SubCommand::with_name("fetch") + .about("Download a package") + .arg( + Arg::with_name("package") + .help("The name of the package") + .multiple(true) + .required(true), + ), + ) + .subcommand( + SubCommand::with_name("install") + .about("Install a package") + .arg( + Arg::with_name("package") + .help("The name of the package") + .multiple(true) + .required(true), + ) + .arg( + Arg::with_name("root") + .help("The root package directory") + .long("root") + .takes_value(true), + ), + ) + .subcommand( + SubCommand::with_name("list") + .about("List package contents") + .arg( + Arg::with_name("package") + .help("The name of the package") + .multiple(true) + .required(true), + ), + ) + .subcommand( + SubCommand::with_name("sign") + .about("Get a file signature") + .arg( + Arg::with_name("file") + .help("The file to obtain the signature of") + .multiple(true) + .required(true), + ), + ) + .subcommand(SubCommand::with_name("upgrade").about("Upgrade all installed packages")) + .get_matches(); - let target = matches.value_of("target").unwrap_or(env!("PKG_DEFAULT_TARGET")); + let target = matches + .value_of("target") + .unwrap_or(env!("PKG_DEFAULT_TARGET")); let repo = Repo::new(target); let database = Database::open("/pkg", PackageDepends::Repository(Repo::new(target))); @@ -195,7 +239,7 @@ fn main() { match Package::from_path(&path) { Ok(p) => { tar_gz_pkgs.push(p); - }, + } Err(e) => { eprintln!("error during package open: {}", e); if let Some(cause) = e.source() { @@ -210,14 +254,14 @@ fn main() { match database.calculate_depends(package, &mut dependencies) { Ok(_) => { dependencies.insert(package.to_string(), ()); - }, + } Err(e) => { eprintln!("error during dependency calculation: {}", e); if let Some(cause) = e.source() { eprintln!("cause: {}", cause); } success = false; - }, + } } } } diff --git a/src/database.rs b/src/database.rs index c4305b92da1d0a56c9e025516c3e0ab4774af60d..4f2d84361b9941a432e7d9dcd8a6431dd39e21c5 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,9 +1,9 @@ +use std::error; +use std::fmt; use std::fs::File; -use std::path::{Path, PathBuf}; use std::io; use std::io::Read; -use std::error; -use std::fmt; +use std::path::{Path, PathBuf}; use petgraph; use petgraph::graphmap::DiGraphMap; @@ -82,15 +82,12 @@ impl PackageDepends { let path = pathbuf.as_path().join(format!("{}.toml", pkg_name)); let mut input = String::new(); - File::open(path.as_path().to_str().unwrap()).and_then(|mut f| { - f.read_to_string(&mut input) - })?; + File::open(path.as_path().to_str().unwrap()) + .and_then(|mut f| f.read_to_string(&mut input))?; Ok(PackageMeta::from_toml(&input)?.depends) - }, - PackageDepends::Repository(ref repo) => { - Ok(repo.fetch_meta(pkg_name)?.depends) } + PackageDepends::Repository(ref repo) => Ok(repo.fetch_meta(pkg_name)?.depends), } } } @@ -125,7 +122,10 @@ impl Database { /// Checks if a package is installed pub fn is_pkg_installed(&self, pkg_name: &str) -> bool { - let pkg_path_buf = self.installed_path.as_path().join(format!("{}.toml", pkg_name)); + let pkg_path_buf = self + .installed_path + .as_path() + .join(format!("{}.toml", pkg_name)); let installed = pkg_path_buf.as_path().exists(); installed } @@ -138,7 +138,11 @@ impl Database { /// Calculates the dependencies of the specified package, and appends them to /// `ordered_dependencies`. - pub fn calculate_depends(&self, pkg_name: &str, ordered_dependencies: &mut OrderMap<String, ()>) -> Result<(), DatabaseError> { + pub fn calculate_depends( + &self, + pkg_name: &str, + ordered_dependencies: &mut OrderMap<String, ()>, + ) -> Result<(), DatabaseError> { let mut graph = DiGraphMap::new(); // Use bimap to intern strings and use integers for keys in graph because @@ -154,7 +158,9 @@ impl Database { // There was a cyclic dependency. Since the graph is made up of numbers, the // name of the package that caused the cyclic dependency must be retrieved for // human readability. - Err(DatabaseError::Cycle(map.get_by_second(&err.node_id()).unwrap().to_string())) + Err(DatabaseError::Cycle( + map.get_by_second(&err.node_id()).unwrap().to_string(), + )) })?; for i in dependency_ids { @@ -169,7 +175,12 @@ impl Database { } /// Helper function to calculate package dependencies. - fn calculate_depends_rec(&self, pkg_name: &str, map: &mut BidirMap<String, usize>, graph: &mut DiGraphMap<usize, u8>) -> Result<(), DatabaseError> { + fn calculate_depends_rec( + &self, + pkg_name: &str, + map: &mut BidirMap<String, usize>, + graph: &mut DiGraphMap<usize, u8>, + ) -> Result<(), DatabaseError> { let curr_node = *map.get_by_first(pkg_name).unwrap(); let mut depends = self.get_pkg_depends(pkg_name)?; diff --git a/src/download.rs b/src/download.rs index ee4c6b37862cde898eddb7223ba053abe98f3524..79c70082bc171a859c318e5330ce0cfec951fcb8 100644 --- a/src/download.rs +++ b/src/download.rs @@ -1,14 +1,14 @@ +use std::error::Error; use std::fs::File; use std::io::{self, stderr, Read, Write}; -use std::error::Error; use std::time::Duration; +use hyper::error::Error as HyperError; +use hyper::header::ContentLength; +use hyper::net::HttpsConnector; use hyper::status::StatusCode; use hyper::Client; -use hyper::net::HttpsConnector; use hyper_rustls::TlsClient; -use hyper::error::Error as HyperError; -use hyper::header::ContentLength; use pbr::{ProgressBar, Units}; @@ -23,13 +23,16 @@ pub fn download(remote_path: &str, local_path: &str) -> io::Result<()> { let mut response = match client.get(remote_path).send() { Ok(response) => response, Err(HyperError::Io(err)) => return Err(err), - Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err.description())) + Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err.description())), }; match response.status { StatusCode::Ok => { let mut count = 0; - let length = response.headers.get::<ContentLength>().map_or(0, |h| h.0 as usize); + let length = response + .headers + .get::<ContentLength>() + .map_or(0, |h| h.0 as usize); let mut file = File::create(&local_path)?; let mut pb = ProgressBar::new(length as u64); @@ -40,7 +43,7 @@ pub fn download(remote_path: &str, local_path: &str) -> io::Result<()> { if res == 0 { break; } - count += file.write(&buf[.. res])?; + count += file.write(&buf[..res])?; pb.set(count as u64); } let _ = write!(stderr, "\n"); @@ -48,11 +51,14 @@ pub fn download(remote_path: &str, local_path: &str) -> io::Result<()> { file.sync_all()?; Ok(()) - }, + } _ => { let _ = write!(stderr, "* Failure {}\n", response.status); - Err(io::Error::new(io::ErrorKind::NotFound, format!("{} not found", remote_path))) + Err(io::Error::new( + io::ErrorKind::NotFound, + format!("{} not found", remote_path), + )) } } } diff --git a/src/lib.rs b/src/lib.rs index 23ca4db1d99d47e8965e9ae3d5f7c4d3a7709f54..a02973f94978b8feb7deaa99d09ecfe07d7429e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,19 +1,19 @@ use libflate::gzip::Encoder; use sha3::{Digest, Sha3_512}; -use std::str; use std::fs::{self, File}; -use std::io::{self, stderr, Read, Write, BufWriter}; +use std::io::{self, stderr, BufWriter, Read, Write}; use std::path::Path; +use std::str; +pub use crate::database::{Database, PackageDepends}; pub use crate::download::download; -pub use crate::packagemeta::{PackageMeta, PackageMetaList}; pub use crate::package::Package; -pub use crate::database::{Database, PackageDepends}; +pub use crate::packagemeta::{PackageMeta, PackageMetaList}; +mod database; mod download; -mod packagemeta; mod package; -mod database; +mod packagemeta; #[derive(Debug)] pub struct Repo { @@ -49,7 +49,7 @@ impl Repo { let mut data = String::new(); if let Ok(_) = file.read_to_string(&mut data) { for line in data.lines() { - if ! line.starts_with('#') { + if !line.starts_with('#') { remotes.push(line.to_string()); } } @@ -61,7 +61,7 @@ impl Repo { Repo { local: format!("/tmp/pkg"), remotes: remotes, - target: target.to_string() + target: target.to_string(), } } @@ -72,7 +72,10 @@ impl Repo { fs::create_dir_all(parent)?; } - let mut res = Err(io::Error::new(io::ErrorKind::NotFound, format!("no remote paths"))); + let mut res = Err(io::Error::new( + io::ErrorKind::NotFound, + format!("no remote paths"), + )); for remote in self.remotes.iter() { let remote_path = format!("{}/{}/{}", remote, self.target, file); res = download(&remote_path, &local_path).map(|_| local_path.clone()); @@ -107,8 +110,11 @@ impl Repo { } pub fn create(&self, package: &str) -> io::Result<String> { - if ! Path::new(package).is_dir() { - return Err(io::Error::new(io::ErrorKind::NotFound, format!("{} not found", package))); + if !Path::new(package).is_dir() { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("{} not found", package), + )); } let sigfile = format!("{}.sig", package); @@ -165,8 +171,11 @@ impl Repo { let tarfile = self.sync(&format!("{}.tar.gz", package))?; - if self.signature(&tarfile)? != expected { - return Err(io::Error::new(io::ErrorKind::InvalidData, format!("{} not valid", package))); + if self.signature(&tarfile)? != expected { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("{} not valid", package), + )); } Package::from_path(tarfile) diff --git a/src/package.rs b/src/package.rs index d6273f3f644b151a36e61056861e8cb6ffcb7de7..c241bacfb1ec456f964d64dda6e2b0400a7c0d48 100644 --- a/src/package.rs +++ b/src/package.rs @@ -1,10 +1,10 @@ use libflate::gzip::Decoder; use std::fs::File; +use std::io::BufReader; +use std::io::{self, Error, ErrorKind, Read}; use std::path::Path; use std::path::PathBuf; -use std::io::{self, Error, ErrorKind, Read}; use tar::{Archive, EntryType}; -use std::io::BufReader; use crate::packagemeta::PackageMeta; @@ -21,10 +21,14 @@ impl Package { let mut ar = Archive::new(decoder); ar.set_preserve_permissions(true); - Ok(Package{archive: ar, path: path.as_ref().to_path_buf(), meta: None}) + Ok(Package { + archive: ar, + path: path.as_ref().to_path_buf(), + meta: None, + }) } - pub fn install<P: AsRef<Path>>(&mut self, dest: P)-> io::Result<()> { + pub fn install<P: AsRef<Path>>(&mut self, dest: P) -> io::Result<()> { self.archive.unpack(dest)?; Ok(()) } @@ -49,21 +53,31 @@ impl Package { let mut toml = None; for entry in self.archive.entries()? { let mut entry = entry?; - if entry.header().entry_type() != EntryType::Directory && entry.path()?.starts_with("pkg") { + if entry.header().entry_type() != EntryType::Directory + && entry.path()?.starts_with("pkg") + { if toml.is_none() { let mut text = String::new(); entry.read_to_string(&mut text)?; toml = Some(text); } else { - return Err(Error::new(ErrorKind::Other, "Multiple metadata files in package")); + return Err(Error::new( + ErrorKind::Other, + "Multiple metadata files in package", + )); } } } if let Some(toml) = toml { - self.meta = Some(PackageMeta::from_toml(&toml).map_err(|e| Error::new(ErrorKind::Other, e))?); + self.meta = Some( + PackageMeta::from_toml(&toml).map_err(|e| Error::new(ErrorKind::Other, e))?, + ); } else { - return Err(Error::new(ErrorKind::NotFound, "Package metadata not found")); + return Err(Error::new( + ErrorKind::NotFound, + "Package metadata not found", + )); } } diff --git a/src/packagemeta.rs b/src/packagemeta.rs index 135b30cd7ec3f45bf05409b9bf064d943182b341..120a146c4a83b0d970985b0c3c3c9940135ad86a 100644 --- a/src/packagemeta.rs +++ b/src/packagemeta.rs @@ -1,6 +1,6 @@ +use serde_derive::{Deserialize, Serialize}; use std::collections::BTreeMap; -use toml::{self, to_string, from_str}; -use serde_derive::{Serialize, Deserialize}; +use toml::{self, from_str, to_string}; #[derive(Serialize, Deserialize)] pub struct PackageMeta { @@ -21,7 +21,7 @@ impl PackageMeta { } pub fn from_toml(text: &str) -> Result<Self, toml::de::Error> { - from_str(text) + from_str(text) } pub fn to_toml(&self) -> String { @@ -38,12 +38,12 @@ pub struct PackageMetaList { impl PackageMetaList { pub fn new() -> Self { PackageMetaList { - packages: BTreeMap::new() + packages: BTreeMap::new(), } } pub fn from_toml(text: &str) -> Result<Self, toml::de::Error> { - from_str(text) + from_str(text) } pub fn to_toml(&self) -> String { diff --git a/tests/common/mod.rs b/tests/common/mod.rs index f90af3dcff0b4ec6dccbe99d760ce33994bdbc15..e679c9af890beeb230e2cd0c7beb808bf21804a2 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -8,8 +8,6 @@ pub fn get_db() -> pkgutils::Database { let path = db_location(); pkgutils::Database::open( format!("{}/pkg", path), - pkgutils::PackageDepends::Directory( - PathBuf::from(format!("{}/etc/pkg.d/pkglist", path)) - ) + pkgutils::PackageDepends::Directory(PathBuf::from(format!("{}/etc/pkg.d/pkglist", path))), ) }