cli: Render `rad issue list` with a nice table

This commit is contained in:
Alexis Sellier 2023-04-21 11:09:02 +02:00
parent 2ff5ad97f3
commit ff56843fcc
No known key found for this signature in database
8 changed files with 72 additions and 20 deletions

View File

@ -17,7 +17,11 @@ The issue is now listed under our project.
```
$ rad issue list
2e8c1bf3fe0532a314778357c886608a966a34bd "flux capacitor underpowered" unassigned
╭───────────────────────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Tags Assignees Opened │
├───────────────────────────────────────────────────────────────────────────────────────────────┤
│ ● 2e8c1bf flux capacitor underpowered z6MknSL…StBU8Vi [ .. ] │
╰───────────────────────────────────────────────────────────────────────────────────────────────╯
```
Show the issue information issue.
@ -48,7 +52,11 @@ It will now show in the list of issues assigned to us.
```
$ rad issue list --assigned
2e8c1bf3fe0532a314778357c886608a966a34bd "flux capacitor underpowered" did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Tags Assignees Opened │
├─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ● 2e8c1bf flux capacitor underpowered z6MknSL…StBU8Vi z6MknSL…StBU8Vi [ .. ] │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯
```
Note: this can always be undone with the `unassign` subcommand.

View File

@ -17,7 +17,11 @@ The issue is now listed under our project.
```
$ rad issue list
b05e945bb63c11bf80320f4e26ad1d1f7c51f755 "flux capacitor underpowered" unassigned
╭───────────────────────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Tags Assignees Opened │
├───────────────────────────────────────────────────────────────────────────────────────────────┤
│ ● b05e945 flux capacitor underpowered z6Mkt67…v4N1tRk [ .. ] │
╰───────────────────────────────────────────────────────────────────────────────────────────────╯
```
Great! Now we've documented the issue for ourselves and others.
@ -35,7 +39,11 @@ It will now show in the list of issues assigned to us.
```
$ rad issue list --assigned
b05e945bb63c11bf80320f4e26ad1d1f7c51f755 "flux capacitor underpowered" did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Tags Assignees Opened │
├─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ● b05e945 flux capacitor underpowered z6Mkt67…v4N1tRk z6Mkt67…v4N1tRk [ .. ] │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯
```
Note: this can always be undone with the `unassign` subcommand.

View File

@ -314,10 +314,22 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
None => None,
};
let mut t = term::Table::new(term::table::TableOptions::default());
let mut t = term::Table::new(term::table::TableOptions::bordered());
t.push([
term::format::dim(String::from("")),
term::format::bold(String::from("ID")),
term::format::bold(String::from("Title")),
term::format::bold(String::from("Author")),
term::format::bold(String::from("Tags")),
term::format::bold(String::from("Assignees")),
term::format::bold(String::from("Opened")),
]);
t.divider();
for result in issues.all()? {
let (id, issue, _) = result?;
let assigned: Vec<_> = issue.assigned().collect();
let state = issue.state();
if Some(true) == assignee.map(|a| !assigned.contains(&Did::from(a))) {
continue;
@ -325,17 +337,28 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
let assigned: String = assigned
.iter()
.map(|p| p.to_string())
.map(|p| term::format::did(p).to_string())
.collect::<Vec<_>>()
.join(", ");
let mut tags = issue.tags().map(|t| t.to_string()).collect::<Vec<_>>();
tags.sort();
t.push([
id.to_string(),
format!("{:?}", issue.title()),
if assigned.is_empty() {
String::from("unassigned")
} else {
assigned.to_string()
match state {
State::Open => term::format::positive("").into(),
State::Closed { .. } => term::format::negative("").into(),
},
term::format::tertiary(term::format::cob(&id)).to_owned(),
term::format::default(issue.title().to_owned()),
term::format::did(&issue.author().id).dim(),
term::format::secondary(tags.join(", ")),
if assigned.is_empty() {
term::format::dim(String::default())
} else {
term::format::default(assigned.to_string())
},
term::format::timestamp(&issue.timestamp()).dim().italic(),
]);
}
t.print();

View File

@ -5,6 +5,7 @@ pub use radicle_term::{style, Paint};
use radicle::cob::{ObjectId, Timestamp};
use radicle::node::NodeId;
use radicle::prelude::Did;
use radicle::profile::Profile;
use crate::terminal as term;
@ -33,13 +34,19 @@ pub fn cob(id: &ObjectId) -> String {
format!("{:.7}", id.to_string())
}
/// Format a DID.
pub fn did(did: &Did) -> Paint<String> {
let nid = did.as_key().to_human();
Paint::new(format!("{}{}", &nid[..7], &nid[nid.len() - 7..]))
}
/// Format a timestamp.
pub fn timestamp(time: &Timestamp) -> String {
pub fn timestamp(time: &Timestamp) -> Paint<String> {
let fmt = timeago::Formatter::new();
let now = Timestamp::now();
let duration = time::Duration::from_secs(now.as_secs() - time.as_secs());
fmt.convert(duration)
Paint::new(fmt.convert(duration))
}
/// Identity formatter that takes a profile and displays it as

View File

@ -257,7 +257,6 @@ fn rad_node() {
}
#[test]
#[ignore]
fn rad_patch() {
let mut environment = Environment::new();
let profile = environment.profile("alice");
@ -641,7 +640,6 @@ fn test_replication_via_seed() {
}
#[test]
#[ignore]
fn rad_workflow() {
let mut environment = Environment::new();
let alice = environment.node("alice");

View File

@ -350,9 +350,7 @@ impl PublicKey {
/// Encode public key in human-readable format.
///
/// We use the format specified by the DID `key` method, which is described as:
///
/// `did:key:MULTIBASE(base58-btc, MULTICODEC(public-key-type, raw-public-key-bytes))`
/// `MULTIBASE(base58-btc, MULTICODEC(public-key-type, raw-public-key-bytes))`
///
pub fn to_human(&self) -> String {
let mut buf = [0; 2 + ed25519::PublicKey::BYTES];

View File

@ -1,4 +1,4 @@
use std::fmt;
use std::fmt::{self, Display};
use std::str::FromStr;
use serde::{Deserialize, Serialize};
@ -139,6 +139,12 @@ impl FromStr for Tag {
}
}
impl Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<Tag> for String {
fn from(Tag(name): Tag) -> Self {
name

View File

@ -19,6 +19,10 @@ pub enum DidError {
pub struct Did(crypto::PublicKey);
impl Did {
/// We use the format specified by the DID `key` method, which is described as:
///
/// `did:key:MULTIBASE(base58-btc, MULTICODEC(public-key-type, raw-public-key-bytes))`
///
pub fn encode(&self) -> String {
format!("did:key:{}", self.0.to_human())
}