Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cli: Render `rad issue list` with a nice table
Alexis Sellier committed 3 years ago
commit ff56843fcc37310a66a307d5d5319ee6cf592376
parent 2ff5ad97f3ceccd6309488ac94fd2f99a4ad2c68
8 files changed +70 -18
modified radicle-cli/examples/rad-issue.md
@@ -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.
modified radicle-cli/examples/workflow/3-issues.md
@@ -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.
modified radicle-cli/src/commands/issue.rs
@@ -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()),
+
                    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() {
-
                        String::from("❲unassigned❳")
+
                        term::format::dim(String::default())
                    } else {
-
                        assigned.to_string()
+
                        term::format::default(assigned.to_string())
                    },
+
                    term::format::timestamp(&issue.timestamp()).dim().italic(),
                ]);
            }
            t.print();
modified radicle-cli/src/terminal/format.rs
@@ -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
modified radicle-cli/tests/commands.rs
@@ -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");
modified radicle-crypto/src/lib.rs
@@ -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];
modified radicle/src/cob/common.rs
@@ -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
modified radicle/src/identity/did.rs
@@ -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())
    }