relay-cli 0.1.2

CLI interface for Relay Agents.
use anyhow::Result;
use serde::Serialize;

use crate::{
    args::Args, parsers::parse_address, progress::Progress, records::get_agent_record,
    sign::sign_request, storage::Storage, transport::agent_base_url,
};
use relay_lib::prelude::Address;

#[derive(Serialize)]
struct ClaimInbloxReq {
    pub id: String,
}

#[derive(Debug, Clone, clap::Parser)]
#[clap(about = "Claim an inbox for an existing identity")]
pub struct ClaimInboxCmd {
    #[arg(value_parser = parse_address)]
    pub address: Address,
}

impl ClaimInboxCmd {
    pub fn execute(&self, _: Args, storage: &mut Storage) -> Result<()> {
        let mut progress = Progress::new(false);

        if self.address.inbox().is_none() {
            progress.abort("Cannot claim an empty inbox.");
        }
        let inbox = self.address.inbox().unwrap().clone();
        let mut address = self.address.clone();
        address.inbox = None;

        progress.step(
            &format!("Checking existing identity for `{}`", address),
            "Checked existing identity",
        );
        let identity = match storage.root.get_identity(&self.address) {
            Some(identity) => {
                if identity.inboxes.contains(&inbox) {
                    progress.abort(&format!("Inbox for address `{}` already exists.", address));
                }
                identity.clone()
            }
            None => {
                progress.abort(&format!(
                    "No identity found for address `{}`. Please claim the base identity first using the `claim` command.",
                    address,
                ));
            }
        };

        progress.step("Fetching agent keys", "Fetched agent keys");
        let agent_record = get_agent_record(self.address.agent()).map_err(|_| {
            progress.abort("Failed to fetch agent record");
        })?;

        progress.step("Requsting inbox claim", "Inbox claimed");
        let request = sign_request(
            &address.canonical(),
            ClaimInbloxReq {
                id: inbox.canonical().to_string(),
            },
            &agent_record,
            &identity.signing_key(),
        )
        .map_err(|_| {
            progress.abort("Failed to sign inbox claim request");
        })?;
        let resp = reqwest::blocking::Client::new()
            .post(agent_base_url(address.agent())?.join("/user/claim/inbox")?)
            .json(&request)
            .send()
            .map_err(|_| {
                progress.abort("Failed to send inbox claim request");
            })?;

        if resp.status().as_u16() == 409 {
            progress.commit();
            progress.nested_section(0, "Inbox already claimed on the Agent");
            progress.nested(1, &format!("Inbox added locally: `{}`", address));
            storage
                .root
                .identities
                .get_mut(&address)
                .unwrap()
                .inboxes
                .push(inbox.clone());
        }

        if !resp.status().is_success() {
            let status = resp.status();
            progress.abort(&format!("Failed to claim inbox. Status: {}", status));
        }

        progress.step("Storing inbox locally", "Inbox stored locally");
        storage
            .root
            .identities
            .get_mut(&address)
            .unwrap()
            .inboxes
            .push(inbox.clone());

        progress.commit();
        progress.arrow(&format!("Successfully claimed inbox `{}`", self.address));
        Ok(())
    }
}