cli: add `rad patch show`

Show patch information with `rad patch show`.

Signed-off-by: Slack Coder <slackcoder@server.ky>
This commit is contained in:
Slack Coder 2023-01-16 12:22:19 -05:00 committed by Alexis Sellier
parent f5cb86dabd
commit 2295bb4ea4
No known key found for this signature in database
5 changed files with 104 additions and 29 deletions

View File

@ -62,4 +62,16 @@ define power requirements b9bb418c6f5 R0 9dad201 (flux-capacitor-power) ahead 1,
Nothing to show. Nothing to show.
$ rad patch show b9bb418c6f504ee91e54c555bdc8fc37b4d9b28b
patch b9bb418c6f504ee91e54c555bdc8fc37b4d9b28b
╭─ define power requirements ───────
No description provided.
╰───────────────────────────────────
9dad201 Define power requirements
``` ```

View File

@ -4,6 +4,8 @@ mod common;
mod create; mod create;
#[path = "patch/list.rs"] #[path = "patch/list.rs"]
mod list; mod list;
#[path = "patch/show.rs"]
mod show;
use std::ffi::OsString; use std::ffi::OsString;
use std::str::FromStr; use std::str::FromStr;
@ -50,9 +52,19 @@ pub enum OptPatch {
Patch(PatchId), Patch(PatchId),
} }
impl From<OptPatch> for Option<PatchId> {
fn from(opt: OptPatch) -> Self {
match opt {
OptPatch::Patch(patch_id) => Some(patch_id),
_ => None,
}
}
}
#[derive(Debug, Default, PartialEq, Eq)] #[derive(Debug, Default, PartialEq, Eq)]
pub enum OperationName { pub enum OperationName {
Open, Open,
Show,
Update, Update,
#[default] #[default]
List, List,
@ -63,6 +75,9 @@ pub enum Operation {
Open { Open {
message: Comment, message: Comment,
}, },
Show {
patch_id: PatchId,
},
Update { Update {
patch_id: OptPatch, patch_id: OptPatch,
message: Comment, message: Comment,
@ -79,6 +94,14 @@ pub struct Options {
pub verbose: bool, pub verbose: bool,
} }
fn parse_patch_id(val: OsString) -> Result<OptPatch, anyhow::Error> {
let val = val
.to_str()
.ok_or_else(|| anyhow!("patch id specified is not UTF-8"))?;
let patch_id = PatchId::from_str(val).map_err(|_| anyhow!("invalid patch id '{}'", val))?;
Ok(OptPatch::Patch(patch_id))
}
impl Args for Options { impl Args for Options {
fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> { fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
use lexopt::prelude::*; use lexopt::prelude::*;
@ -88,7 +111,7 @@ impl Args for Options {
let mut op: Option<OperationName> = None; let mut op: Option<OperationName> = None;
let mut verbose = false; let mut verbose = false;
let mut sync = true; let mut sync = true;
let mut id = OptPatch::default(); let mut patch_id = OptPatch::default();
let mut message = Comment::default(); let mut message = Comment::default();
let mut push = true; let mut push = true;
@ -137,19 +160,16 @@ impl Args for Options {
Value(val) if op.is_none() => match val.to_string_lossy().as_ref() { Value(val) if op.is_none() => match val.to_string_lossy().as_ref() {
"l" | "list" => op = Some(OperationName::List), "l" | "list" => op = Some(OperationName::List),
"o" | "open" => op = Some(OperationName::Open), "o" | "open" => op = Some(OperationName::Open),
"s" | "show" => op = Some(OperationName::Show),
"u" | "update" => op = Some(OperationName::Update), "u" | "update" => op = Some(OperationName::Update),
unknown => anyhow::bail!("unknown operation '{}'", unknown), unknown => anyhow::bail!("unknown operation '{}'", unknown),
}, },
Value(val) if op == Some(OperationName::Update) && id == OptPatch::Any => { Value(val) if op == Some(OperationName::Show) && patch_id == OptPatch::Any => {
let val = val patch_id = parse_patch_id(val)?;
.to_str() }
.ok_or_else(|| anyhow!("patch id specified is not UTF-8"))?; Value(val) if op == Some(OperationName::Update) && patch_id == OptPatch::Any => {
patch_id = parse_patch_id(val)?;
id = OptPatch::Patch(
PatchId::from_str(val)
.map_err(|_| anyhow!("invalid patch id '{}'", val))?,
);
} }
_ => return Err(anyhow::anyhow!(arg.unexpected())), _ => return Err(anyhow::anyhow!(arg.unexpected())),
} }
@ -158,10 +178,11 @@ impl Args for Options {
let op = match op.unwrap_or_default() { let op = match op.unwrap_or_default() {
OperationName::Open => Operation::Open { message }, OperationName::Open => Operation::Open { message },
OperationName::List => Operation::List, OperationName::List => Operation::List,
OperationName::Update => Operation::Update { OperationName::Show => Operation::Show {
patch_id: id, patch_id: Option::from(patch_id)
message, .ok_or_else(|| anyhow!("a patch id must be provided"))?,
}, },
OperationName::Update => Operation::Update { patch_id, message },
}; };
Ok(( Ok((
@ -198,6 +219,9 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
Operation::List => { Operation::List => {
list::run(&storage, &profile, Some(workdir), options)?; list::run(&storage, &profile, Some(workdir), options)?;
} }
Operation::Show { ref patch_id } => {
show::run(&storage, &profile, &workdir, patch_id)?;
}
Operation::Update { Operation::Update {
ref patch_id, ref patch_id,
ref message, ref message,

View File

@ -238,23 +238,8 @@ pub fn run(
anyhow::bail!("a title must be given"); anyhow::bail!("a title must be given");
} }
let title_pretty = &term::format::dim(format!("╭─ {} ───────", title));
term::blank(); term::blank();
term::print(title_pretty); term::patch::print_title_desc(title, &description);
term::blank();
if description.is_empty() {
term::print(term::format::italic("No description provided."));
} else {
term::markdown(&description);
}
term::blank();
term::print(term::format::dim(format!(
"╰{}",
"".repeat(term::text_width(title_pretty) - 1)
)));
term::blank(); term::blank();
if !confirm("Create patch?", &options) { if !confirm("Create patch?", &options) {

View File

@ -0,0 +1,35 @@
use super::common::*;
use super::*;
use crate::terminal as term;
use radicle::cob::patch;
use radicle::git;
use radicle::prelude::*;
use radicle::storage::git::Repository;
pub fn run(
storage: &Repository,
profile: &Profile,
workdir: &git::raw::Repository,
patch_id: &PatchId,
) -> anyhow::Result<()> {
let patches = patch::Patches::open(profile.public_key, storage)?;
let Some(patch) = patches.get(patch_id)? else {
anyhow::bail!("Patch `{}` not found", patch_id);
};
term::blank();
term::print(format!("patch {}", patch_id));
term::blank();
term::patch::print_title_desc(patch.title(), patch.description().unwrap_or(""));
term::blank();
let target_head = patch_merge_target_oid(patch.target(), storage)?;
let base_oid = workdir.merge_base(target_head, **patch.head())?;
let commits = patch_commits(workdir, &base_oid, patch.head())?;
term::patch::list_commits(&commits)?;
term::blank();
Ok(())
}

View File

@ -84,3 +84,22 @@ pub fn print_commits_ahead_behind(
); );
Ok(()) Ok(())
} }
/// Print title and description in a text box.
pub fn print_title_desc(title: &str, description: &str) {
let title_pretty = &term::format::dim(format!("╭─ {} ───────", title));
term::print(title_pretty);
term::blank();
if description.is_empty() {
term::print(term::format::italic("No description provided."));
} else {
term::markdown(description);
}
term::blank();
term::print(term::format::dim(format!(
"╰{}",
"".repeat(term::text_width(title_pretty) - 1)
)));
}