semora-cli 1.1.0

Semora CLI: publish Markdown and HTML, and hear back from the page
Documentation
//! `whoami` and `keys`.

use semora_proto::{CreateKeyRequest, Scope};

use crate::args::KeysCmd;
use crate::{Ctx, exit, output};

pub async fn whoami(ctx: &Ctx) -> anyhow::Result<i32> {
    let client = ctx.client()?;
    let w = client.whoami().await?;
    if ctx.json {
        output::json(&w);
        return Ok(exit::OK);
    }
    println!("server  {}", w.server);
    if let Some(id) = &w.server_id {
        println!("id      {id}");
    }
    println!(
        "user    {} ({}){}",
        w.user.email,
        w.user.display_name,
        if w.user.is_admin { ", admin" } else { "" }
    );
    if let Some(k) = &w.key {
        println!(
            "key     {} (sm_{}_…) {}",
            k.name,
            k.prefix,
            Scope::join(&k.scopes)
        );
    }
    Ok(exit::OK)
}

pub async fn keys(ctx: &Ctx, cmd: KeysCmd) -> anyhow::Result<i32> {
    let client = ctx.client()?;
    match cmd {
        KeysCmd::Ls => {
            let keys = client.keys().await?;
            if ctx.json {
                output::json(&keys);
                return Ok(exit::OK);
            }
            for k in keys {
                let state = if k.revoked_at.is_some() {
                    "revoked".to_string()
                } else if let Some(e) = k.expires_at {
                    format!("expires {}", output::ts(e))
                } else {
                    "active".to_string()
                };
                let used = k
                    .last_used_at
                    .map(output::ts)
                    .unwrap_or_else(|| "never".into());
                println!(
                    "{:>4}  sm_{}_…  {:<24} {:<8} last used {:<16}  {}",
                    k.id, k.prefix, k.name, k.created_via, used, state
                );
            }
        }
        KeysCmd::Create {
            name,
            scopes,
            expires_in_days,
        } => {
            let scopes = scopes
                .as_deref()
                .map(Scope::parse_list)
                .transpose()
                .map_err(anyhow::Error::msg)?;
            let created = client
                .create_key(&CreateKeyRequest {
                    name,
                    scopes,
                    expires_in_days,
                })
                .await?;
            if ctx.json {
                output::json(&created);
            } else {
                println!("{}", created.secret);
                let done = output::err().done;
                anstream::eprintln!(
                    "{done}Created{done:#} key {} (id {}). You can copy it again from your account page.",
                    created.key.name,
                    created.key.id
                );
            }
        }
        KeysCmd::Revoke { id } => {
            client.revoke_key(id).await?;
            if !ctx.json {
                let done = output::out().done;
                anstream::println!("{done}Revoked{done:#} key {id}");
            }
        }
    }
    Ok(exit::OK)
}