use std::path::PathBuf;
use anyhow::Result;
use clap::{ArgGroup, Subcommand};
use crate::registry::{Registry, OPEN_RING_NAME};
use crate::util::parse_peer_id;
#[derive(Subcommand)]
pub enum Cmd {
#[command(subcommand)]
Ring(RingCmd),
#[command(subcommand)]
Blob(BlobCmd),
Import {
path: PathBuf,
#[arg(long, conflicts_with = "open")]
tag: Option<String>,
#[arg(long, conflicts_with = "tag")]
open: bool,
},
Serve,
Receive {
ticket: String,
#[arg(long, default_value = ".")]
dest: PathBuf,
},
#[command(group(ArgGroup::new("access").required(true).args(["rings", "open"])))]
Tag {
target: String,
#[arg(long = "ring", conflicts_with = "open")]
rings: Vec<String>,
#[arg(long, conflicts_with = "rings")]
open: bool,
},
Tags {
target: String,
},
Id,
}
#[derive(Subcommand)]
pub enum BlobCmd {
Import {
path: PathBuf,
#[arg(long, conflicts_with = "open")]
tag: Option<String>,
#[arg(long, conflicts_with = "tag")]
open: bool,
},
Remove {
target: String,
},
List,
}
#[derive(Subcommand)]
pub enum RingCmd {
New {
name: String,
},
List,
Add {
ring: String,
#[arg(value_name = "PEER-ID")]
peer: String,
#[arg(long)]
nickname: Option<String>,
},
Remove {
ring: String,
#[arg(value_name = "PEER-ID")]
peer: String,
},
Members { ring: String },
}
pub fn run_ring(cmd: RingCmd, registry: Registry) -> Result<()> {
match cmd {
RingCmd::New { name } => {
registry.create_ring(&name)?;
println!("Ring created: {name}");
println!("Add peers: rdrop ring add {name} <peer-id>");
}
RingCmd::List => {
let rings = registry.list_rings()?;
println!("{} ring(s):", rings.len());
for r in rings {
if r.is_open() {
println!(
" {} — publicly accessible (no membership required)",
r.as_str()
);
} else {
let members = registry.list_members(r.as_str())?;
println!(" {} ({} member(s))", r.as_str(), members.len());
}
}
}
RingCmd::Add {
ring,
peer,
nickname,
} => {
if ring == OPEN_RING_NAME {
println!("The open ring has no membership list — everyone is welcome.");
return Ok(());
}
let peer = parse_peer_id(&peer)?;
registry.add_member(&ring, peer, nickname.as_deref())?;
match &nickname {
Some(nick) => println!("Added {peer} ({nick}) to ring {ring}"),
None => println!("Added {peer} to ring {ring}"),
}
}
RingCmd::Remove { ring, peer } => {
if ring == OPEN_RING_NAME {
println!("The open ring has no membership list to remove from.");
return Ok(());
}
let peer = parse_peer_id(&peer)?;
registry.remove_member(&ring, peer)?;
println!("Removed {peer} from ring {ring}");
}
RingCmd::Members { ring } => {
if ring == OPEN_RING_NAME {
println!("The open ring is public — any peer may access blobs tagged with it.");
return Ok(());
}
let members = registry.list_members(&ring)?;
if members.is_empty() {
println!("Ring '{ring}' has no members yet.");
println!("Add peers: rdrop ring add {ring} <peer-id>");
println!("Peers print their PeerId with: rdrop id");
} else {
println!("Ring '{ring}' — {} member(s):", members.len());
for (peer, nick) in members {
match nick {
Some(n) => println!(" {peer} ({n})"),
None => println!(" {peer}"),
}
}
}
}
}
Ok(())
}