Skip to content

Commit

Permalink
Release 0.2.0
Browse files Browse the repository at this point in the history
- Also added 'clone' command.
  • Loading branch information
hwittenborn committed Jun 12, 2022
1 parent b12e84b commit 697a925
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 26 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.2.0] - 2022-06-11
### Added
- Add `clone` command.

## [0.1.1] - 2022-06-06
### Fixed
- Recursively created cache directory if it doesn't exist.
Expand Down
2 changes: 1 addition & 1 deletion makedeb/PKGBUILD
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Maintainer: Hunter Wittenborn <[email protected]>
pkgname=mpr-cli
pkgver=0.1.1
pkgver=0.2.0
pkgrel=1
pkgdesc='The official command-line interface for the makedeb Package Repository'
arch=('any')
Expand Down
52 changes: 52 additions & 0 deletions src/clone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use crate::{message, mpr_cache, util};
use std::collections::HashMap;

pub fn clone(args: &clap::ArgMatches) {
let pkg = args.value_of("pkg").unwrap();
let mpr_url = args.value_of("mpr-url").unwrap();
let cache = mpr_cache::new(mpr_url);

let mut pkgbases: Vec<&str> = Vec::new();
let mut pkgbase_mappings: HashMap<&str, &str> = HashMap::new();

// Get a list of package bases.
for pkg in &cache {
pkgbases.push(&pkg.pkgbase);
pkgbase_mappings.insert(&pkg.pkgname, &pkg.pkgbase);
}

// Abort if the package base doesn't exist.
if !pkgbases.contains(&pkg) {
message::error(&format!("Package base '{}' doesn't exist on the MPR.", pkg));

// If the specified package doesn't exist, but another package base builds the requested
// package, the user probably wants to clone that instead (i.e. 'rustc' for `cargo` on the MPR).
if pkgbase_mappings.contains_key(&pkg) {
message::error(
&format!(
"Package base '{}' exists on the MPR though, which builds '{}'. You probably want to clone that instead:",
pkgbase_mappings[&pkg],
&pkg
)
);

message::error_bold(&format!(
" {} clone '{}'",
clap::crate_name!(),
pkgbase_mappings[&pkg]
));
}

quit::with_code(exitcode::USAGE);
}

// Clone the package.
let pkg_url = format!("{}/{}", mpr_url, pkg);
let cmd = vec!["git", "clone", "--h", &pkg_url];
let exit_code = util::run_command(&cmd);

if !exit_code.success() {
message::error("Failed to clone package.");
quit::with_code(exitcode::UNAVAILABLE);
};
}
22 changes: 22 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod clone;
mod message;
mod mpr_cache;
mod search;
Expand All @@ -22,12 +23,32 @@ fn main() {
.global(true)
.takes_value(true)
)
.arg(
Arg::new("mpr-url")
.help("URL to access the MPR from")
.long("mpr-url")
.env("MPR_URL")
.hide_env_values(true)
.global(true)
.takes_value(true)
.default_value("https://mpr.makedeb.org")
)
.subcommand(
Command::new("clone")
.about("Clone a package base from the MPR")
.arg(
Arg::new("pkg")
.help("The package to clone")
.required(true)
)
)
.subcommand(
Command::new("search")
.about("Search the MPR for a package")
.arg_required_else_help(true)
.arg(
Arg::new("pkg")
.required(true)
.help("The query to search for")
.multiple_values(true)
),
Expand All @@ -39,6 +60,7 @@ fn main() {
.get_matches();

match cmd.subcommand() {
Some(("clone", args)) => clone::clone(args),
Some(("search", args)) => search::search(args),
Some(("whoami", args)) => whoami::whoami(args),
_ => {},
Expand Down
10 changes: 9 additions & 1 deletion src/message.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ansi_term::Colour;
use ansi_term::{Colour, Style};

pub fn info(str: &str) {
println!("{}", str);
Expand All @@ -7,3 +7,11 @@ pub fn info(str: &str) {
pub fn error(str: &str) {
println!("{} {}", Colour::Red.paint("Err:"), str);
}

pub fn error_bold(str: &str) {
println!(
"{} {}",
Colour::Red.paint("Err:"),
Style::new().bold().paint(str)
);
}
16 changes: 6 additions & 10 deletions src/mpr_cache.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use crate::{message, util};
use crate::message;
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
use serde::{Deserialize, Serialize};
use std::{fs, time::SystemTime};

// REMOVE LATER!
use std::io::prelude::*;
use std::{fs, time::SystemTime};

#[derive(Deserialize, Serialize, PartialEq)]
pub struct MprCache {
Expand All @@ -26,7 +24,7 @@ pub struct MprCache {
pub ood: Option<u32>,
}

pub fn new() -> Vec<MprCache> {
pub fn new(mpr_url: &str) -> Vec<MprCache> {
// Get the XDG cache directory.
let cache_dir = match dirs::cache_dir() {
Some(dir) => dir,
Expand Down Expand Up @@ -113,10 +111,8 @@ pub fn new() -> Vec<MprCache> {
// If we need to, update the cache file.
if update_cache {
// Download the archive.
let resp = match reqwest::blocking::get(format!(
"https://{}/packages-meta-ext-v2.json.gz",
util::MPR_URL
)) {
let resp = match reqwest::blocking::get(format!("{}/packages-meta-ext-v2.json.gz", mpr_url))
{
Ok(resp) => resp,
Err(err) => {
message::error(&format!("Unable to make request. [{}]", err));
Expand Down Expand Up @@ -186,7 +182,7 @@ pub fn new() -> Vec<MprCache> {
// On an error, let's just remove the cache file and regenerate it by recalling
// this function.
fs::remove_file(mpr_cache_file).unwrap();
self::new()
self::new(mpr_url)
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use chrono::{TimeZone, Utc};

pub fn search(args: &clap::ArgMatches) {
let pkglist: Vec<&str> = args.values_of("pkg").unwrap().collect();
let cache = mpr_cache::new();
let mpr_url = args.value_of("mpr-url").unwrap();
let cache = mpr_cache::new(mpr_url);
let mut matches: Vec<&mpr_cache::MprCache> = Vec::new();

// Get matches.
Expand Down
38 changes: 28 additions & 10 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
use crate::message;
use serde::{Deserialize, Serialize};
use std::str;
use std::{
process::{Command, ExitStatus},
str,
};

#[derive(Deserialize, Serialize)]
pub struct Authenticated {
struct AuthenticationError {
#[serde(rename = "type")]
pub resp_type: String,
pub msg: String,
pub code: String,
}

pub static MPR_URL: &str = "mpr.makedeb.org";

// Struct to handle API-authenticated requests to the MPR.
pub struct AuthenticatedRequest<'a> {
api_token: &'a str,
mpr_url: &'a str,
}

impl<'a> AuthenticatedRequest<'a> {
pub fn new(api_token: &'a str) -> Self {
Self { api_token }
pub fn new(api_token: &'a str, mpr_url: &'a str) -> Self {
Self { api_token, mpr_url }
}

pub fn get(&self, path: &str) -> String {
// Make the request.
let client = reqwest::blocking::Client::new();
let resp = match client
.get(format!("https://{}/api/{}", self::MPR_URL, path))
.get(format!("{}/api/{}", self.mpr_url, path))
.header("Authorization", self.api_token)
.send()
{
Expand All @@ -40,10 +42,10 @@ impl<'a> AuthenticatedRequest<'a> {
// abort the program.
let resp_text = resp.text().unwrap();

if let Ok(json) = serde_json::from_str::<Authenticated>(&resp_text) {
if let Ok(json) = serde_json::from_str::<AuthenticationError>(&resp_text) {
// TODO: We need to define a more suitable way for machine parsing of errors in the
// MPR. Maybe something like '{"err_type": "invalid_api_key"}'.
if json.resp_type == "error" && json.msg == "Invalid API key." {
if json.resp_type == "error" && json.code == "err_invalid_api_key" {
message::error("Invalid API key was passed in.");
quit::with_code(exitcode::USAGE);
}
Expand All @@ -52,3 +54,19 @@ impl<'a> AuthenticatedRequest<'a> {
resp_text
}
}

// Function to run a command, and abort if it fails.
pub fn run_command(args: &Vec<&str>) -> ExitStatus {
let cmd = args[0];
let cmd_args = &args[1..];

let result = Command::new(cmd).args(cmd_args).spawn();

match result {
Ok(mut child) => child.wait().unwrap(),
Err(err) => {
message::error(&format!("Failed to run command {:?} [{}]", args, err));
quit::with_code(exitcode::UNAVAILABLE);
}
}
}
13 changes: 10 additions & 3 deletions src/whoami.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
use crate::{message, util};
use serde::Deserialize;

#[derive(Deserialize)]
struct Authenticated {
user: String,
}

pub fn whoami(args: &clap::ArgMatches) {
let api_token = match args.value_of("token") {
Expand All @@ -8,10 +14,11 @@ pub fn whoami(args: &clap::ArgMatches) {
quit::with_code(exitcode::USAGE);
}
};
let mpr_url = args.value_of("mpr-url").unwrap();

let request = util::AuthenticatedRequest::new(api_token);
let request = util::AuthenticatedRequest::new(api_token, mpr_url);
let resp_text = request.get("test");
let json = serde_json::from_str::<util::Authenticated>(&resp_text).unwrap();
let json = serde_json::from_str::<Authenticated>(&resp_text).unwrap();

println!("{}", json.msg);
println!("Authenticated to the MPR as {}.", json.user);
}

0 comments on commit 697a925

Please sign in to comment.