use anyhow::Error;
use std::process::{Command, ExitStatus};
pub fn channel(exe: &std::path::Path, repo: &std::path::Path) -> Result<ChannelsList, Error> {
pub fn channel(exe: &std::path::Path, repo: &std::path::Path) -> Result<ChannelsList, Error> {
let mut cmd = Command::new(exe);
let cmd = cmd
.arg("channel")
.arg("--no-prompt")
.arg("--repository")
.arg(repo);
let output = cmd.output()?;
check_exit_status(cmd, output.status)?;
parse_channel_output(output.stdout)
}
fn parse_channel_output(output: Vec<u8>) -> Result<ChannelsList, Error> {
let channels_str = String::from_utf8(output)?;
let channels_list = channels_str.split('\n').map(|substr| substr.trim()).fold(
ChannelsList::new(),
|mut channels_list, ch_str| {
if ch_str.starts_with("* ") {
channels_list.active = channels_list.entries.len();
channels_list.entries.push(ch_str[2..].to_string());
} else {
channels_list.entries.push(ch_str.to_string());
}
channels_list
},
);
Ok(channels_list)
parse_channel_output(output.stdout)
}
fn parse_channel_output(output: Vec<u8>) -> Result<ChannelsList, Error> {
let channels_str = String::from_utf8(output)?;
let channels_list = channels_str.split('\n').map(|substr| substr.trim()).fold(
ChannelsList::new(),
|mut channels_list, ch_str| {
if ch_str.starts_with("* ") {
channels_list.active = channels_list.entries.len();
channels_list.entries.push(ch_str[2..].to_string());
} else {
channels_list.entries.push(ch_str.to_string());
}
channels_list
},
);
Ok(channels_list)
}
pub fn log(
exe: &std::path::Path,
repo: &std::path::Path,
ch_name_maybe: Option<&str>,
) -> Result<Vec<ChangelogEntry>, Error> {
let mut cmd = Command::new(exe);
cmd.arg("log")
.arg("--no-prompt")
.arg("--repository")
.arg(repo)
.arg("--output-format")
.arg("json");
if let Some(ch_name) = ch_name_maybe {
cmd.arg("--channel").arg(ch_name);
}
let output = cmd.output()?;
check_exit_status(&cmd, output.status)?;
Ok(if output.stdout.is_empty() {
Vec::new()
} else {
serde_json::from_slice(&output.stdout)?
})
}
pub fn change(exe: &std::path::Path, repo: &std::path::Path, hash: &str) -> Result<String, Error> {
let mut cmd = Command::new(exe);
let cmd = cmd
.arg("change")
.arg("--no-prompt")
.arg("--repository")
.arg(repo)
.arg(hash);
let output = cmd.output()?;
check_exit_status(cmd, output.status)?;
Ok(String::from_utf8(output.stdout)?)
}
pub fn diff(
exe: &std::path::Path,
repo: &std::path::Path,
ch_name_maybe: Option<&str>,
) -> Result<String, Error> {
let mut cmd = Command::new(exe);
let cmd = cmd
.arg("diff")
.arg("--no-prompt")
.arg("--repository")
.arg(repo);
if let Some(ch_name) = ch_name_maybe {
cmd.arg("--channel").arg(ch_name);
}
let output = cmd.output()?;
check_exit_status(cmd, output.status)?;
Ok(String::from_utf8(output.stdout)?)
}
pub fn diff_short(
exe: &std::path::Path,
repo: &std::path::Path,
ch_name_maybe: Option<&str>,
) -> Result<serde_json::Map<String, serde_json::Value>, Error> {
let mut cmd = Command::new(exe);
let cmd = cmd
.arg("diff")
.arg("--no-prompt")
.arg("--repository")
.arg(repo)
.arg("--short")
.arg("--json");
if let Some(ch_name) = ch_name_maybe {
cmd.arg("--channel").arg(ch_name);
}
let output = cmd.output()?;
check_exit_status(cmd, output.status)?;
Ok(if output.stdout.is_empty() {
serde_json::Map::new()
} else {
serde_json::from_slice(&output.stdout)?
})
}
fn check_exit_status(cmd: &Command, exit_status: ExitStatus) -> Result<(), Error> {
if exit_status.success() {
Ok(())
} else {
Err(Error::from(ExecutionError {
command: format!("{:?}", cmd),
exit_code: exit_status.code(),
}))
}
}
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
#[error("Received an exit code of {} from {}", exit_code.unwrap_or(1), command)]
pub struct ExecutionError {
pub command: String,
pub exit_code: Option<i32>,
}
#[derive(Debug)]
pub struct ChannelsList {
pub(crate) entries: Vec<String>,
pub(crate) active: usize,
}
#[derive(Debug)]
pub struct ChannelsList {
pub(crate) entries: Vec<String>,
pub(crate) active: usize,
}
impl ChannelsList {
pub(crate) fn new() -> Self {
Self {
entries: Vec::new(),
active: usize::MAX,
}
}
}
impl ChannelsList {
pub(crate) fn new() -> Self {
Self {
entries: Vec::new(),
active: usize::MAX,
}
}
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct ChangelogEntry {
pub(crate) hash: String,
pub(crate) authors: Vec<String>,
pub(crate) timestamp: String,
pub(crate) message: String,
}