Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
cli: Add `--json` option to `rad ls`
Archived did:key:z6MkhacU...vqHM opened 2 years ago

This was inspired by the --json option of the rad node routing command.

1 file changed +99 -37 574ac356 6fe74305
modified radicle-cli/src/commands/ls.rs
@@ -1,6 +1,7 @@
use std::ffi::OsString;

use radicle::storage::{ReadStorage, RepositoryInfo};
+
use radicle_crypto::Verified;

use crate::terminal as term;
use crate::terminal::args::{Args, Error, Help};
@@ -26,6 +27,7 @@ Options
    --seeded, -s    Show all seeded repositories
    --all, -a       Show all repositories in storage
    --verbose, -v   Verbose output
+
    --json          JSON output
    --help          Print help
"#,
};
@@ -37,6 +39,7 @@ pub struct Options {
    private: bool,
    all: bool,
    seeded: bool,
+
    json: bool,
}

impl Args for Options {
@@ -49,6 +52,7 @@ impl Args for Options {
        let mut public = false;
        let mut all = false;
        let mut seeded = false;
+
        let mut json = false;

        while let Some(arg) = parser.next()? {
            match arg {
@@ -68,6 +72,7 @@ impl Args for Options {
                    public = true;
                }
                Long("verbose") | Short('v') => verbose = true,
+
                Long("json") => json = true,
                _ => return Err(anyhow::anyhow!(arg.unexpected())),
            }
        }
@@ -79,6 +84,7 @@ impl Args for Options {
                public,
                all,
                seeded,
+
                json,
            },
            vec![],
        ))
@@ -90,57 +96,77 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
    let storage = &profile.storage;
    let repos = storage.repositories()?;
    let policy = profile.policies()?;
-
    let mut table = term::Table::new(term::TableOptions::bordered());
-
    let mut rows = Vec::new();

-
    if repos.is_empty() {
-
        return Ok(());
-
    }
-

-
    for RepositoryInfo {
-
        rid,
-
        head,
-
        doc,
-
        refs,
-
    } in repos
-
    {
+
    let repos = repos.into_iter().filter_map(move |repo| {
+
        let RepositoryInfo { rid, doc, refs, .. } = &repo;
        if doc.visibility.is_public() && options.private && !options.public {
-
            continue;
+
            return None;
        }
        if !doc.visibility.is_public() && !options.private && options.public {
-
            continue;
+
            return None;
        }
        if refs.is_none() && !options.all && !options.seeded {
-
            continue;
-
        }
-
        let seeded = policy.is_seeding(&rid)?;
-

-
        if !seeded && !options.all {
-
            continue;
+
            return None;
        }
-
        if !seeded && options.seeded {
-
            continue;
+
        match policy.is_seeding(rid) {
+
            Ok(seeded) => {
+
                if !seeded && !options.all {
+
                    return None;
+
                }
+
                if !seeded && options.seeded {
+
                    return None;
+
                }
+
                Some(Ok((repo, seeded)))
+
            }
+
            Err(e) => Some(Err(anyhow::anyhow!(e))),
        }
-
        let proj = doc.project()?;
-
        let head = term::format::oid(head).into();
-

-
        rows.push([
-
            term::format::bold(proj.name().to_owned()),
-
            term::format::tertiary(rid.urn()),
-
            if seeded {
-
                term::format::visibility(&doc.visibility).into()
-
            } else {
-
                term::format::dim("local").into()
-
            },
-
            term::format::secondary(head),
-
            term::format::italic(proj.description().to_owned()),
-
        ]);
+
    });
+

+
    if options.json {
+
        print_json(repos)?;
+
    } else {
+
        print_table(repos)?;
    }
+

+
    Ok(())
+
}
+

+
fn print_table(
+
    repositories: impl Iterator<Item = anyhow::Result<(RepositoryInfo<Verified>, bool)>>,
+
) -> anyhow::Result<()> {
+
    let mut rows = repositories
+
        .map(|repo| {
+
            let (
+
                RepositoryInfo {
+
                    rid,
+
                    head,
+
                    doc,
+
                    refs: _,
+
                },
+
                seeded,
+
            ) = repo?;
+
            let proj = doc.project()?;
+
            let head = term::format::oid(head).into();
+

+
            Ok([
+
                term::format::bold(proj.name().to_owned()),
+
                term::format::tertiary(rid.urn()),
+
                if seeded {
+
                    term::format::visibility(&doc.visibility).into()
+
                } else {
+
                    term::format::dim("local").into()
+
                },
+
                term::format::secondary(head),
+
                term::format::italic(proj.description().to_owned()),
+
            ])
+
        })
+
        .collect::<anyhow::Result<Vec<_>>>()?;
    rows.sort();

    if rows.is_empty() {
        term::print(term::format::italic("Nothing to show."));
    } else {
+
        let mut table = term::Table::new(radicle_term::TableOptions::bordered());
        table.push([
            "Name".into(),
            "RID".into(),
@@ -155,3 +181,39 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {

    Ok(())
}
+

+
fn print_json(
+
    repositories: impl Iterator<Item = anyhow::Result<(RepositoryInfo<Verified>, bool)>>,
+
) -> anyhow::Result<()> {
+
    for repo in repositories {
+
        let (
+
            RepositoryInfo {
+
                rid,
+
                head,
+
                doc,
+
                refs: _,
+
            },
+
            seeded,
+
        ) = repo?;
+
        let proj = doc.project()?;
+
        let visibility = if seeded {
+
            match doc.visibility {
+
                radicle::identity::Visibility::Public => "public",
+
                radicle::identity::Visibility::Private { .. } => "private",
+
            }
+
        } else {
+
            "local"
+
        };
+
        println!(
+
            "{}",
+
            serde_json::json!({
+
                "name": proj.name(),
+
                "rid": rid,
+
                "visibility": visibility,
+
                "head": head,
+
                "description": proj.description()
+
            })
+
        );
+
    }
+
    Ok(())
+
}