umshctl/command/
tables.rs

1//! `dev-channel` / `dev-peer` / `dev-admin`: the device identity's own
2//! key tables.
3//!
4//! The digest form differs by table (a 2-byte channel id versus the
5//! 32-byte peer key itself), so listings and mutation reports print
6//! whatever digest the device quotes — a channel id as hex, a key as
7//! the base58 address it is everywhere else.
8
9use anyhow::{Result, bail};
10
11use umsh::ulcp_wire::ids::prop;
12
13use super::persist;
14use super::values::KeyArg;
15use crate::App;
16use crate::output::{address, hex};
17
18#[derive(Debug, clap::Subcommand)]
19pub enum TableOp {
20    /// List the entries the device holds, in digest form.
21    List,
22    /// Add an entry.
23    Add {
24        #[arg(value_name = "KEY")]
25        key: KeyArg,
26    },
27    /// Remove an entry.
28    Remove {
29        #[arg(value_name = "KEY")]
30        key: KeyArg,
31    },
32}
33
34/// How the device's quoted digest for `prop` reads back to the user.
35/// The channel table quotes a two-byte identifier, which has no address
36/// form; every other table quotes the public key itself.
37fn digest_text(prop: u32, digest: &[u8]) -> String {
38    if prop == prop::DEV_CHANNEL_KEYS {
39        hex(digest)
40    } else {
41        address(digest)
42    }
43}
44
45pub async fn run(app: &mut App, key: u32, noun: &str, op: Option<TableOp>) -> Result<()> {
46    let no_save = app.no_save;
47    let device = app.device()?;
48    match op.unwrap_or(TableOp::List) {
49        TableOp::List => {
50            let value = device.get_prop(key).await?;
51            let digest_len = if key == prop::DEV_CHANNEL_KEYS { 2 } else { 32 };
52            if value.is_empty() {
53                println!("no device {noun}s provisioned");
54            } else if !value.len().is_multiple_of(digest_len) {
55                bail!("malformed device {noun} listing");
56            } else {
57                for digest in value.chunks(digest_len) {
58                    println!("{}", digest_text(key, digest));
59                }
60            }
61            Ok(())
62        }
63        TableOp::Add { key: item } => {
64            let digest = device.insert_prop_item(key, &item.0).await?;
65            println!("device {noun} added (digest {})", digest_text(key, &digest));
66            persist(device, no_save).await
67        }
68        TableOp::Remove { key: item } => {
69            let digest = device.remove_prop_item(key, &item.0).await?;
70            println!(
71                "device {noun} removed (digest {})",
72                digest_text(key, &digest)
73            );
74            persist(device, no_save).await
75        }
76    }
77}