cli: Remove rad-inspect `tree' dependency

Hard code output similar to the 'tree' command in rust to avoid having
to shell out.
This commit is contained in:
Slack Coder 2023-07-12 06:15:30 -05:00 committed by cloudhead
parent f91ef2963e
commit 67a8ee9e86
No known key found for this signature in database
5 changed files with 191 additions and 91 deletions

View File

@ -31,28 +31,27 @@ Let's check that we have all the namespaces in storage:
```
$ rad inspect --refs
.
|-- z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
| `-- refs
| |-- heads
| | `-- master
| `-- rad
| |-- id
| `-- sigrefs
|-- z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
| `-- refs
| |-- heads
| | `-- master
| `-- rad
| |-- id
| `-- sigrefs
`-- z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z
`-- refs
|-- heads
| `-- master
`-- rad
|-- id
`-- sigrefs
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
└── refs
   ├── heads
   │   └── master
   └── rad
      ├── id
      └── sigrefs
z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
└── refs
   ├── heads
   │   └── master
   └── rad
      ├── id
      └── sigrefs
z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z
└── refs
   ├── heads
   │   └── master
   └── rad
      ├── id
      └── sigrefs
```
We can then setup a git remote for `bob`:

View File

@ -5,14 +5,13 @@ NID. This is demonstrated below where our NID is
```
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --refs
.
`-- z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
`-- refs
|-- heads
| `-- master
`-- rad
|-- id
`-- sigrefs
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
└── refs
   ├── heads
   │   └── master
   └── rad
      ├── id
      └── sigrefs
```
To remedy this, we can use the `rad fork` command for the project we
@ -28,21 +27,20 @@ have a copy of the main set of refs:
```
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --refs
.
|-- z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
| `-- refs
| |-- heads
| | `-- master
| `-- rad
| |-- id
| `-- sigrefs
`-- z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
`-- refs
|-- heads
| `-- master
`-- rad
|-- id
`-- sigrefs
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
└── refs
   ├── heads
   │   └── master
   └── rad
      ├── id
      └── sigrefs
z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
└── refs
   ├── heads
   │   └── master
   └── rad
      ├── id
      └── sigrefs
```
We are now able to setup a remote in our own working copy of the

View File

@ -17,14 +17,13 @@ It's also possible to display all of the repository's git references:
```
$ rad inspect --refs
.
`-- z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
`-- refs
|-- heads
| `-- master
`-- rad
|-- id
`-- sigrefs
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
└── refs
   ├── heads
   │   └── master
   └── rad
      ├── id
      └── sigrefs
```
Or display the repository identity's payload:

View File

@ -30,21 +30,20 @@ And some remote refs:
```
$ rad inspect --refs
.
`-- z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
`-- refs
|-- cobs
| `-- xyz.radicle.patch
| |-- 0ec956c94256fa101db4c32956ce195a1aa0edf2
| `-- 928d76e22ef98a8406f2e4e4bcc8878533bbdfe0
|-- heads
| |-- master
| `-- patches
| |-- 0ec956c94256fa101db4c32956ce195a1aa0edf2
| `-- 928d76e22ef98a8406f2e4e4bcc8878533bbdfe0
`-- rad
|-- id
`-- sigrefs
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
└── refs
   ├── cobs
   │   └── xyz.radicle.patch
   │      ├── 0ec956c94256fa101db4c32956ce195a1aa0edf2
   │      └── 928d76e22ef98a8406f2e4e4bcc8878533bbdfe0
   ├── heads
   │   ├── master
   │   └── patches
   │      ├── 0ec956c94256fa101db4c32956ce195a1aa0edf2
   │      └── 928d76e22ef98a8406f2e4e4bcc8878533bbdfe0
   └── rad
      ├── id
      └── sigrefs
```
Then let's merge the changes into `master`.
@ -86,16 +85,15 @@ And so were the remote branches:
```
$ rad inspect --refs
.
`-- z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
`-- refs
|-- cobs
| `-- xyz.radicle.patch
| |-- 0ec956c94256fa101db4c32956ce195a1aa0edf2
| `-- 928d76e22ef98a8406f2e4e4bcc8878533bbdfe0
|-- heads
| `-- master
`-- rad
|-- id
`-- sigrefs
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
└── refs
   ├── cobs
   │   └── xyz.radicle.patch
   │      ├── 0ec956c94256fa101db4c32956ce195a1aa0edf2
   │      └── 928d76e22ef98a8406f2e4e4bcc8878533bbdfe0
   ├── heads
   │   └── master
   └── rad
      ├── id
      └── sigrefs
```

View File

@ -1,7 +1,7 @@
#![allow(clippy::or_fun_call)]
use std::collections::HashMap;
use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::str::FromStr;
use anyhow::{anyhow, Context as _};
@ -11,6 +11,7 @@ use json_color::{Color, Colorizer};
use radicle::crypto::{Unverified, Verified};
use radicle::identity::Untrusted;
use radicle::identity::{Doc, Id};
use radicle::storage::git::Storage;
use radicle::storage::{ReadRepository, ReadStorage};
use crate::terminal as term;
@ -34,7 +35,7 @@ Options
--id Return the repository identifier (RID)
--payload Inspect the repository's identity payload
--refs Inspect the repository's refs on the local device (requires `tree`)
--refs Inspect the repository's refs on the local device
--history Show the history of the repository identity document
--help Print help
"#,
@ -127,15 +128,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
match options.target {
Target::Refs => {
let path = storage.path_of(&id).join("refs").join("namespaces");
Command::new("tree")
.current_dir(path)
.args(["--noreport", "--prune"])
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()?
.wait()?;
refs(storage, id)?;
}
Target::Payload => {
println!(
@ -216,3 +209,116 @@ fn colorizer() -> Colorizer {
.key(Color::Blue)
.build()
}
fn refs(storage: &Storage, id: Id) -> anyhow::Result<()> {
let repo = storage.repository(id)?;
let mut refs = Vec::new();
for r in repo.references()? {
let r = r?;
if let Some(namespace) = r.namespace {
refs.push(format!("{}/{}", namespace, r.name));
}
}
print!("{}", tree(refs));
Ok(())
}
/// Show the list of given git references as a newline terminated tree `String` similar to the tree command.
fn tree(mut refs: Vec<String>) -> String {
refs.sort();
// List of references with additional unique entries for each 'directory'.
//
// i.e. "refs/heads/master" becomes ["refs"], ["refs", "heads"], and ["refs", "heads",
// "master"].
let mut refs_expanded: Vec<Vec<String>> = Vec::new();
// Number of entries per Git 'directory'.
let mut ref_entries: HashMap<Vec<String>, usize> = HashMap::new();
let mut last: Vec<String> = Vec::new();
for r in refs {
let r: Vec<String> = r.split('/').map(|s| s.to_string()).collect();
for (i, v) in r.iter().enumerate() {
let last_v = last.get(i);
if Some(v) != last_v {
last = r.clone().iter().take(i + 1).map(String::from).collect();
refs_expanded.push(last.clone());
let mut dir = last.clone();
dir.pop();
if dir.is_empty() {
continue;
}
if let Some(num) = ref_entries.get_mut(&dir) {
*num += 1;
} else {
ref_entries.insert(dir, 1);
}
}
}
}
let mut tree = String::default();
for mut ref_components in refs_expanded {
// Better to explode when things do not go as expected.
let name = ref_components.pop().expect("non-empty vector");
if ref_components.is_empty() {
tree.push_str(&format!("{name}\n"));
continue;
}
for i in 1..ref_components.len() {
let parent: Vec<String> = ref_components.iter().take(i).cloned().collect();
let num = ref_entries.get(&parent).unwrap_or(&0);
if *num == 0 {
tree.push_str("    ");
} else {
tree.push_str("│   ");
}
}
if let Some(num) = ref_entries.get_mut(&ref_components) {
if *num == 1 {
tree.push_str(&format!("└── {name}\n"));
} else {
tree.push_str(&format!("├── {name}\n"));
}
*num -= 1;
}
}
tree
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_tree() {
let arg = vec![
String::from("z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/refs/heads/master"),
String::from("z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/refs/rad/id"),
String::from("z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/refs/rad/sigrefs"),
];
let exp = r#"
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
refs
   heads
      master
   rad
      id
      sigrefs
"#
.trim_start();
assert_eq!(tree(arg), exp);
assert_eq!(tree(vec![String::new()]), "\n");
}
}