cli: Update commands for private repos
Several commands are updated to better support private repos.
This commit is contained in:
parent
257b950d6f
commit
9a54c8da7b
|
|
@ -1,10 +1,11 @@
|
||||||
use std::{ffi::OsString, str::FromStr as _};
|
use std::io::IsTerminal;
|
||||||
|
use std::{ffi::OsString, io, str::FromStr as _};
|
||||||
|
|
||||||
use anyhow::{anyhow, Context as _};
|
use anyhow::{anyhow, Context as _};
|
||||||
|
|
||||||
use radicle::cob::identity::{self, Proposal, Proposals, Revision, RevisionId};
|
use radicle::cob::identity::{self, Proposal, Proposals, Revision, RevisionId};
|
||||||
use radicle::git::Oid;
|
use radicle::git::Oid;
|
||||||
use radicle::identity::Identity;
|
use radicle::identity::{Identity, Visibility};
|
||||||
use radicle::prelude::{Did, Doc};
|
use radicle::prelude::{Did, Doc};
|
||||||
use radicle::storage::ReadStorage as _;
|
use radicle::storage::ReadStorage as _;
|
||||||
use radicle_crypto::Verified;
|
use radicle_crypto::Verified;
|
||||||
|
|
@ -24,14 +25,16 @@ Usage
|
||||||
|
|
||||||
rad id (update|edit) [--title|-t] [--description|-d]
|
rad id (update|edit) [--title|-t] [--description|-d]
|
||||||
[--delegates <did>] [--threshold <num>]
|
[--delegates <did>] [--threshold <num>]
|
||||||
[--no-confirm] [<option>...]
|
[--visibility <private | public>]
|
||||||
|
[--allow <did>] [--no-confirm] [<option>...]
|
||||||
rad id list [<option>...]
|
rad id list [<option>...]
|
||||||
rad id rebase <id> [--rev <revision-id>] [<option>...]
|
rad id rebase <id> [--rev <revision-id>] [<option>...]
|
||||||
rad id show <id> [--rev <revision-id>] [--revisions] [<option>...]
|
rad id show <id> [--rev <revision-id>] [--revisions] [<option>...]
|
||||||
rad id (accept|reject|close|commit) [--rev <revision-id>] [--no-confirm] [<option>...]
|
rad id (accept|reject|close|commit) <id> [--rev <revision-id>] [--no-confirm] [<option>...]
|
||||||
|
|
||||||
Options
|
Options
|
||||||
|
|
||||||
|
--quiet, -q Don't print anything
|
||||||
--help Print help
|
--help Print help
|
||||||
"#,
|
"#,
|
||||||
};
|
};
|
||||||
|
|
@ -67,6 +70,7 @@ pub enum Operation {
|
||||||
title: Option<String>,
|
title: Option<String>,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
delegates: Vec<Did>,
|
delegates: Vec<Did>,
|
||||||
|
visibility: Option<Visibility>,
|
||||||
threshold: Option<usize>,
|
threshold: Option<usize>,
|
||||||
},
|
},
|
||||||
Update {
|
Update {
|
||||||
|
|
@ -114,6 +118,7 @@ pub enum OperationName {
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
pub op: Operation,
|
pub op: Operation,
|
||||||
pub interactive: Interactive,
|
pub interactive: Interactive,
|
||||||
|
pub quiet: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args for Options {
|
impl Args for Options {
|
||||||
|
|
@ -127,9 +132,11 @@ impl Args for Options {
|
||||||
let mut title: Option<String> = None;
|
let mut title: Option<String> = None;
|
||||||
let mut description: Option<String> = None;
|
let mut description: Option<String> = None;
|
||||||
let mut delegates: Vec<Did> = Vec::new();
|
let mut delegates: Vec<Did> = Vec::new();
|
||||||
|
let mut visibility: Option<Visibility> = None;
|
||||||
let mut threshold: Option<usize> = None;
|
let mut threshold: Option<usize> = None;
|
||||||
let mut interactive = Interactive::Yes;
|
let mut interactive = Interactive::from(io::stdout().is_terminal());
|
||||||
let mut show_revisions = false;
|
let mut show_revisions = false;
|
||||||
|
let mut quiet = false;
|
||||||
|
|
||||||
while let Some(arg) = parser.next()? {
|
while let Some(arg) = parser.next()? {
|
||||||
match arg {
|
match arg {
|
||||||
|
|
@ -142,6 +149,9 @@ impl Args for Options {
|
||||||
Long("description") if op == Some(OperationName::Edit) => {
|
Long("description") if op == Some(OperationName::Edit) => {
|
||||||
description = Some(parser.value()?.to_string_lossy().into());
|
description = Some(parser.value()?.to_string_lossy().into());
|
||||||
}
|
}
|
||||||
|
Long("quiet") | Short('q') => {
|
||||||
|
quiet = true;
|
||||||
|
}
|
||||||
Long("no-confirm") => {
|
Long("no-confirm") => {
|
||||||
interactive = Interactive::No;
|
interactive = Interactive::No;
|
||||||
}
|
}
|
||||||
|
|
@ -169,6 +179,21 @@ impl Args for Options {
|
||||||
let did = term::args::did(&parser.value()?)?;
|
let did = term::args::did(&parser.value()?)?;
|
||||||
delegates.push(did);
|
delegates.push(did);
|
||||||
}
|
}
|
||||||
|
Long("allow") => {
|
||||||
|
let value = parser.value()?;
|
||||||
|
let did = term::args::did(&value)?;
|
||||||
|
if let Some(Visibility::Private { allow }) = &mut visibility {
|
||||||
|
allow.insert(did);
|
||||||
|
} else {
|
||||||
|
visibility = Some(Visibility::private([did]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Long("visibility") => {
|
||||||
|
let value = parser.value()?;
|
||||||
|
let value = term::args::parse_value("visibility", value)?;
|
||||||
|
|
||||||
|
visibility = Some(value);
|
||||||
|
}
|
||||||
Long("threshold") => {
|
Long("threshold") => {
|
||||||
threshold = Some(parser.value()?.to_string_lossy().parse()?);
|
threshold = Some(parser.value()?.to_string_lossy().parse()?);
|
||||||
}
|
}
|
||||||
|
|
@ -198,6 +223,7 @@ impl Args for Options {
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
delegates,
|
delegates,
|
||||||
|
visibility,
|
||||||
threshold,
|
threshold,
|
||||||
},
|
},
|
||||||
OperationName::Update => Operation::Update {
|
OperationName::Update => Operation::Update {
|
||||||
|
|
@ -226,7 +252,14 @@ impl Args for Options {
|
||||||
id: id.ok_or_else(|| anyhow!("a proposal must be provided"))?,
|
id: id.ok_or_else(|| anyhow!("a proposal must be provided"))?,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Ok((Options { op, interactive }, vec![]))
|
Ok((
|
||||||
|
Options {
|
||||||
|
op,
|
||||||
|
interactive,
|
||||||
|
quiet,
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,11 +280,15 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
let (rid, revision) = select(&proposal, rev, &previous, interactive)?;
|
let (rid, revision) = select(&proposal, rev, &previous, interactive)?;
|
||||||
warn_out_of_date(revision, &previous);
|
warn_out_of_date(revision, &previous);
|
||||||
let yes = confirm(interactive, "Are you sure you want to accept?");
|
let yes = confirm(interactive, "Are you sure you want to accept?");
|
||||||
|
|
||||||
if yes {
|
if yes {
|
||||||
let (_, signature) = revision.proposed.sign(&signer)?;
|
let (_, signature) = revision.proposed.sign(&signer)?;
|
||||||
proposal.accept(rid, signature, &signer)?;
|
proposal.accept(rid, signature, &signer)?;
|
||||||
term::success!("Accepted proposal ✓");
|
|
||||||
print(&proposal, &previous, None)?;
|
if !options.quiet {
|
||||||
|
term::success!("Accepted proposal ✓");
|
||||||
|
print(&proposal, &previous, None)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Reject { id, rev } => {
|
Operation::Reject { id, rev } => {
|
||||||
|
|
@ -260,22 +297,28 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
let (rid, revision) = select(&proposal, rev, &previous, interactive)?;
|
let (rid, revision) = select(&proposal, rev, &previous, interactive)?;
|
||||||
warn_out_of_date(revision, &previous);
|
warn_out_of_date(revision, &previous);
|
||||||
let yes = confirm(interactive, "Are you sure you want to reject?");
|
let yes = confirm(interactive, "Are you sure you want to reject?");
|
||||||
|
|
||||||
if yes {
|
if yes {
|
||||||
proposal.reject(rid, &signer)?;
|
proposal.reject(rid, &signer)?;
|
||||||
term::success!("Rejected proposal 👎");
|
|
||||||
print(&proposal, &previous, None)?;
|
if !options.quiet {
|
||||||
|
term::success!("Rejected proposal 👎");
|
||||||
|
print(&proposal, &previous, None)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Edit {
|
Operation::Edit {
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
delegates,
|
delegates,
|
||||||
|
visibility,
|
||||||
threshold,
|
threshold,
|
||||||
} => {
|
} => {
|
||||||
let proposed = {
|
let proposed = {
|
||||||
let mut proposed = previous.doc.clone();
|
let mut proposed = previous.doc.clone();
|
||||||
proposed.threshold = threshold.unwrap_or(proposed.threshold);
|
proposed.threshold = threshold.unwrap_or(proposed.threshold);
|
||||||
proposed.delegates.extend(delegates);
|
proposed.delegates.extend(delegates);
|
||||||
|
proposed.visibility = visibility.unwrap_or(proposed.visibility);
|
||||||
proposed
|
proposed
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -296,11 +339,15 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
create.proposed,
|
create.proposed,
|
||||||
&signer,
|
&signer,
|
||||||
)?;
|
)?;
|
||||||
term::success!(
|
if options.quiet {
|
||||||
"Identity proposal '{}' created",
|
term::print(proposal.id);
|
||||||
term::format::highlight(proposal.id)
|
} else {
|
||||||
);
|
term::success!(
|
||||||
print(&proposal, &previous, None)?;
|
"Identity proposal '{}' created",
|
||||||
|
term::format::highlight(proposal.id)
|
||||||
|
);
|
||||||
|
print(&proposal, &previous, None)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Operation::Update {
|
Operation::Update {
|
||||||
id,
|
id,
|
||||||
|
|
@ -342,15 +389,20 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
if yes {
|
if yes {
|
||||||
proposal.edit(update.title, update.description, &signer)?;
|
proposal.edit(update.title, update.description, &signer)?;
|
||||||
let revision = proposal.update(previous.current, update.proposed, &signer)?;
|
let revision = proposal.update(previous.current, update.proposed, &signer)?;
|
||||||
term::success!(
|
|
||||||
"Identity proposal '{}' updated",
|
if options.quiet {
|
||||||
term::format::highlight(proposal.id)
|
term::print(revision.to_string());
|
||||||
);
|
} else {
|
||||||
term::success!(
|
term::success!(
|
||||||
"Revision '{}'",
|
"Identity proposal '{}' updated",
|
||||||
term::format::highlight(revision.to_string())
|
term::format::highlight(proposal.id)
|
||||||
);
|
);
|
||||||
print(&proposal, &previous, None)?;
|
term::success!(
|
||||||
|
"Revision '{}'",
|
||||||
|
term::format::highlight(revision.to_string())
|
||||||
|
);
|
||||||
|
print(&proposal, &previous, None)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Rebase { id, rev } => {
|
Operation::Rebase { id, rev } => {
|
||||||
|
|
@ -362,15 +414,20 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
if yes {
|
if yes {
|
||||||
let revision =
|
let revision =
|
||||||
proposal.update(previous.current, revision.proposed.clone(), &signer)?;
|
proposal.update(previous.current, revision.proposed.clone(), &signer)?;
|
||||||
term::success!(
|
|
||||||
"Identity proposal '{}' rebased",
|
if options.quiet {
|
||||||
term::format::highlight(proposal.id)
|
term::print(revision.to_string());
|
||||||
);
|
} else {
|
||||||
term::success!(
|
term::success!(
|
||||||
"Revision '{}'",
|
"Identity proposal '{}' rebased",
|
||||||
term::format::highlight(revision.to_string())
|
term::format::highlight(proposal.id)
|
||||||
);
|
);
|
||||||
print(&proposal, &previous, None)?;
|
term::success!(
|
||||||
|
"Revision '{}'",
|
||||||
|
term::format::highlight(revision.to_string())
|
||||||
|
);
|
||||||
|
print(&proposal, &previous, None)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::List => {
|
Operation::List => {
|
||||||
|
|
@ -413,21 +470,31 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
let (rid, revision) = commit_select(&proposal, rev, &previous, interactive)?;
|
let (rid, revision) = commit_select(&proposal, rev, &previous, interactive)?;
|
||||||
warn_out_of_date(revision, &previous);
|
warn_out_of_date(revision, &previous);
|
||||||
let yes = confirm(interactive, "Are you sure you want to commit?");
|
let yes = confirm(interactive, "Are you sure you want to commit?");
|
||||||
|
|
||||||
if yes {
|
if yes {
|
||||||
let id = Proposal::commit(&proposal, &rid, signer.public_key(), &repo, &signer)?;
|
let id = Proposal::commit(&proposal, &rid, signer.public_key(), &repo, &signer)?;
|
||||||
proposal.commit(&signer)?;
|
proposal.commit(&signer)?;
|
||||||
term::success!("Committed new identity '{}'", id.current);
|
|
||||||
print(&proposal, &previous, None)?;
|
if options.quiet {
|
||||||
|
term::print(id.current);
|
||||||
|
} else {
|
||||||
|
term::success!("Committed new identity '{}'", id.current);
|
||||||
|
print(&proposal, &previous, None)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Close { id } => {
|
Operation::Close { id } => {
|
||||||
let id = id.resolve(&repo.backend)?;
|
let id = id.resolve(&repo.backend)?;
|
||||||
let mut proposal = proposals.get_mut(&id)?;
|
let mut proposal = proposals.get_mut(&id)?;
|
||||||
let yes = confirm(interactive, "Are you sure you want to close?");
|
let yes = confirm(interactive, "Are you sure you want to close?");
|
||||||
|
|
||||||
if yes {
|
if yes {
|
||||||
proposal.close(&signer)?;
|
proposal.close(&signer)?;
|
||||||
term::success!("Closed identity proposal '{}'", id);
|
|
||||||
print(&proposal, &previous, None)?;
|
if !options.quiet {
|
||||||
|
term::success!("Closed identity proposal '{}'", id);
|
||||||
|
print(&proposal, &previous, None)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Show {
|
Operation::Show {
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,12 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
|
||||||
let interactive = options.interactive;
|
let interactive = options.interactive;
|
||||||
|
|
||||||
term::headline(format!(
|
term::headline(format!(
|
||||||
"Initializing radicle 👾 project in {}",
|
"Initializing{}radicle 👾 project in {}",
|
||||||
|
if !options.visibility.is_public() {
|
||||||
|
term::format::yellow(" private ")
|
||||||
|
} else {
|
||||||
|
term::format::default(" ")
|
||||||
|
},
|
||||||
if path == cwd {
|
if path == cwd {
|
||||||
term::format::highlight(".").to_string()
|
term::format::highlight(".").to_string()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ Options
|
||||||
--id Return the repository identifier (RID)
|
--id Return the repository identifier (RID)
|
||||||
--payload Inspect the repository's identity payload
|
--payload Inspect the repository's identity payload
|
||||||
--refs Inspect the repository's refs on the local device
|
--refs Inspect the repository's refs on the local device
|
||||||
|
--identity Inspect the identity document
|
||||||
--delegates Inspect the repository's delegates
|
--delegates Inspect the repository's delegates
|
||||||
--policy Inspect the repository's tracking policy
|
--policy Inspect the repository's tracking policy
|
||||||
--history Show the history of the repository identity document
|
--history Show the history of the repository identity document
|
||||||
|
|
@ -49,6 +50,7 @@ pub enum Target {
|
||||||
Refs,
|
Refs,
|
||||||
Payload,
|
Payload,
|
||||||
Delegates,
|
Delegates,
|
||||||
|
Identity,
|
||||||
Policy,
|
Policy,
|
||||||
History,
|
History,
|
||||||
#[default]
|
#[default]
|
||||||
|
|
@ -89,6 +91,9 @@ impl Args for Options {
|
||||||
Long("history") => {
|
Long("history") => {
|
||||||
target = Target::History;
|
target = Target::History;
|
||||||
}
|
}
|
||||||
|
Long("identity") => {
|
||||||
|
target = Target::Identity;
|
||||||
|
}
|
||||||
Long("id") => {
|
Long("id") => {
|
||||||
target = Target::Id;
|
target = Target::Id;
|
||||||
}
|
}
|
||||||
|
|
@ -147,6 +152,12 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
colorizer().colorize_json_str(&serde_json::to_string_pretty(&project.payload)?)?
|
colorizer().colorize_json_str(&serde_json::to_string_pretty(&project.payload)?)?
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Target::Identity => {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
colorizer().colorize_json_str(&serde_json::to_string_pretty(&project.doc)?)?
|
||||||
|
);
|
||||||
|
}
|
||||||
Target::Policy => {
|
Target::Policy => {
|
||||||
let tracking = profile.tracking()?;
|
let tracking = profile.tracking()?;
|
||||||
if let Some(repo) = tracking.repo_policy(&id)? {
|
if let Some(repo) = tracking.repo_policy(&id)? {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ Usage
|
||||||
|
|
||||||
Options
|
Options
|
||||||
|
|
||||||
|
--private Show only private repositories
|
||||||
|
--public Show only public repositories
|
||||||
--verbose, -v Verbose output
|
--verbose, -v Verbose output
|
||||||
--help Print help
|
--help Print help
|
||||||
"#,
|
"#,
|
||||||
|
|
@ -23,6 +25,8 @@ Options
|
||||||
|
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
|
public: bool,
|
||||||
|
private: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args for Options {
|
impl Args for Options {
|
||||||
|
|
@ -31,18 +35,33 @@ impl Args for Options {
|
||||||
|
|
||||||
let mut parser = lexopt::Parser::from_args(args);
|
let mut parser = lexopt::Parser::from_args(args);
|
||||||
let mut verbose = false;
|
let mut verbose = false;
|
||||||
|
let mut private = false;
|
||||||
|
let mut public = false;
|
||||||
|
|
||||||
if let Some(arg) = parser.next()? {
|
while let Some(arg) = parser.next()? {
|
||||||
match arg {
|
match arg {
|
||||||
Long("help") | Short('h') => {
|
Long("help") | Short('h') => {
|
||||||
return Err(Error::Help.into());
|
return Err(Error::Help.into());
|
||||||
}
|
}
|
||||||
|
Long("private") => {
|
||||||
|
private = true;
|
||||||
|
}
|
||||||
|
Long("public") => {
|
||||||
|
public = true;
|
||||||
|
}
|
||||||
Long("verbose") | Short('v') => verbose = true,
|
Long("verbose") | Short('v') => verbose = true,
|
||||||
_ => return Err(anyhow::anyhow!(arg.unexpected())),
|
_ => return Err(anyhow::anyhow!(arg.unexpected())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((Options { verbose }, vec![]))
|
Ok((
|
||||||
|
Options {
|
||||||
|
verbose,
|
||||||
|
private,
|
||||||
|
public,
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,6 +71,13 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
let mut table = term::Table::default();
|
let mut table = term::Table::default();
|
||||||
|
|
||||||
for (id, head, doc) in storage.repositories()? {
|
for (id, head, doc) in storage.repositories()? {
|
||||||
|
if doc.visibility.is_public() && options.private && !options.public {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if !doc.visibility.is_public() && !options.private && options.public {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let proj = match doc.verified()?.project() {
|
let proj = match doc.verified()?.project() {
|
||||||
Ok(proj) => proj,
|
Ok(proj) => proj,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
mod id;
|
mod id;
|
||||||
|
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::{Deref, Not};
|
use std::ops::{Deref, Not};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use nonempty::NonEmpty;
|
use nonempty::NonEmpty;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
@ -144,11 +145,27 @@ pub enum Visibility {
|
||||||
Public,
|
Public,
|
||||||
/// Delegates plus the allowed DIDs.
|
/// Delegates plus the allowed DIDs.
|
||||||
Private {
|
Private {
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
|
||||||
allow: Vec<Did>,
|
allow: BTreeSet<Did>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
#[error("'{0}' is not a valid visibility type")]
|
||||||
|
pub struct VisibilityParseError(String);
|
||||||
|
|
||||||
|
impl FromStr for Visibility {
|
||||||
|
type Err = VisibilityParseError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"public" => Ok(Visibility::Public),
|
||||||
|
"private" => Ok(Visibility::private([])),
|
||||||
|
_ => Err(VisibilityParseError(s.to_owned())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Visibility {
|
impl Visibility {
|
||||||
/// Check whether the visibility is public.
|
/// Check whether the visibility is public.
|
||||||
pub fn is_public(&self) -> bool {
|
pub fn is_public(&self) -> bool {
|
||||||
|
|
@ -156,9 +173,9 @@ impl Visibility {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Private visibility with list of allowed DIDs beyond the repository delegates.
|
/// Private visibility with list of allowed DIDs beyond the repository delegates.
|
||||||
pub fn private(allow: impl Into<Vec<Did>>) -> Self {
|
pub fn private(allow: impl IntoIterator<Item = Did>) -> Self {
|
||||||
Self::Private {
|
Self::Private {
|
||||||
allow: allow.into(),
|
allow: BTreeSet::from_iter(allow),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -545,16 +562,14 @@ mod test {
|
||||||
serde_json::json!({ "type": "public" })
|
serde_json::json!({ "type": "public" })
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
serde_json::to_value(Visibility::Private { allow: vec![] }).unwrap(),
|
serde_json::to_value(Visibility::private([])).unwrap(),
|
||||||
serde_json::json!({ "type": "private" })
|
serde_json::json!({ "type": "private" })
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
serde_json::to_value(Visibility::Private {
|
serde_json::to_value(Visibility::private([Did::from_str(
|
||||||
allow: vec![Did::from_str(
|
"did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT"
|
||||||
"did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT"
|
)
|
||||||
)
|
.unwrap()]))
|
||||||
.unwrap()]
|
|
||||||
})
|
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
serde_json::json!({ "type": "private", "allow": ["did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT"] })
|
serde_json::json!({ "type": "private", "allow": ["did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT"] })
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::collections::{BTreeMap, HashSet};
|
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::ops::RangeBounds;
|
use std::ops::RangeBounds;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
@ -122,7 +122,7 @@ impl Arbitrary for Visibility {
|
||||||
Visibility::Public
|
Visibility::Public
|
||||||
} else {
|
} else {
|
||||||
Visibility::Private {
|
Visibility::Private {
|
||||||
allow: Vec::arbitrary(g),
|
allow: BTreeSet::arbitrary(g),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue