use crate::{cache::MprCache, message};
use serde::{Deserialize, Serialize};
use std::{
io::Write,
process::{Command, ExitStatus, Stdio},
str,
};
#[derive(Deserialize, Serialize)]
struct AuthenticationError {
#[serde(rename = "type")]
pub resp_type: String,
pub code: String,
}
pub struct AuthenticatedRequest<'a> {
api_token: &'a str,
mpr_url: &'a str,
}
impl<'a> AuthenticatedRequest<'a> {
pub fn new(api_token: &'a str, mpr_url: &'a str) -> Self {
Self { api_token, mpr_url }
}
fn handle_response(&self, resp: reqwest::Result<reqwest::blocking::Response>) -> String {
let resp = match resp {
Ok(resp) => resp,
Err(err) => {
message::error(&format!("Failed to make request [{}]", err));
quit::with_code(exitcode::UNAVAILABLE);
}
};
let resp_text = resp.text().unwrap();
if let Ok(json) = serde_json::from_str::<AuthenticationError>(&resp_text) {
if json.resp_type == "error" && json.code == "err_invalid_api_key" {
message::error("Invalid API key was passed in.");
quit::with_code(exitcode::USAGE);
}
}
resp_text
}
pub fn get(&self, path: &str) -> String {
let client = reqwest::blocking::Client::new();
let resp = client
.get(format!("{}/api/{}", self.mpr_url, path))
.header("Authorization", self.api_token)
.send();
self.handle_response(resp)
}
pub fn post(&self, path: &str, body: String) -> String {
let client = reqwest::blocking::Client::new();
let resp = client
.post(format!("{}/api/{}", self.mpr_url, path))
.body(body)
.header("Authorization", self.api_token)
.send();
self.handle_response(resp)
}
}
pub struct CommandInfo<'a> {
pub args: &'a Vec<&'a str>,
pub capture: bool,
pub stdin: Option<&'a str>,
}
pub struct CommandResult {
pub stdout: Vec<u8>,
pub stderr: Vec<u8>,
pub exit_status: ExitStatus,
}
pub fn run_command(cmd: &CommandInfo) -> CommandResult {
let cmd_name = cmd.args[0];
let cmd_args = &cmd.args[1..];
let mut _result = Command::new(cmd_name);
let mut result = &mut _result;
result = result.args(cmd_args);
if cmd.stdin.is_some() {
result = result.stdin(Stdio::piped());
}
if cmd.capture {
result = result.stdout(Stdio::piped());
result = result.stderr(Stdio::piped());
}
let mut result = match result.spawn() {
Ok(child) => child,
Err(err) => {
message::error(&format!(
"Failed to run command. [{:?}] [{}]",
cmd.args, err
));
quit::with_code(exitcode::UNAVAILABLE);
}
};
if let Some(stdin) = cmd.stdin {
result
.stdin
.take()
.unwrap()
.write_all(stdin.as_bytes())
.unwrap();
}
let prog_exit = result.wait_with_output().unwrap();
CommandResult {
stdout: prog_exit.stdout,
stderr: prog_exit.stderr,
exit_status: prog_exit.status,
}
}
pub fn find_pkgbase<'a>(pkgname: &'a str, package_cache: &'a MprCache) -> Option<&'a str> {
for pkg in &package_cache.packages {
if pkg.pkgname == pkgname {
return Some(pkg.pkgbase.as_str());
}
}
None
}