use std::collections::BTreeMap; use std::path::PathBuf; use clap::Parser; use imbl_value::InternedString; use itertools::Itertools; use rpc_toolkit::{Context, Empty, HandlerArgs, HandlerExt, ParentHandler, from_fn_async}; use serde::{Deserialize, Serialize}; use ts_rs::TS; use crate::context::CliContext; use crate::prelude::*; use crate::registry::context::RegistryContext; use crate::registry::package::index::Category; use crate::util::DataUrl; use crate::util::serde::{HandlerExtSerde, WithIoFormat}; pub fn info_api() -> ParentHandler> { ParentHandler::>::new() .root_handler( from_fn_async(get_info) .with_metadata("authenticated", Value::Bool(false)) .with_display_serializable() .with_about("about.display-registry-info") .with_call_remote::(), ) .subcommand( "set-name", from_fn_async(set_name) .with_metadata("admin", Value::Bool(true)) .no_display() .with_about("about.set-registry-name") .with_call_remote::(), ) .subcommand( "set-icon", from_fn_async(set_icon) .with_metadata("admin", Value::Bool(true)) .no_cli(), ) .subcommand( "set-icon", from_fn_async(cli_set_icon) .no_display() .with_about("about.set-registry-icon"), ) } #[derive(Debug, Default, Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export)] pub struct RegistryInfo { pub name: Option, pub icon: Option>, pub categories: BTreeMap, } pub async fn get_info(ctx: RegistryContext) -> Result { let peek = ctx.db.peek().await.into_index(); Ok(RegistryInfo { name: peek.as_name().de()?, icon: peek.as_icon().de()?, categories: peek.as_package().as_categories().de()?, }) } #[derive(Debug, Deserialize, Serialize, Parser, TS)] #[group(skip)] #[command(rename_all = "kebab-case")] #[serde(rename_all = "camelCase")] #[ts(export)] pub struct SetNameParams { #[arg(help = "help.arg.registry-name")] pub name: String, } pub async fn set_name( ctx: RegistryContext, SetNameParams { name }: SetNameParams, ) -> Result<(), Error> { ctx.db .mutate(|db| db.as_index_mut().as_name_mut().ser(&Some(name))) .await .result } #[derive(Debug, Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export)] pub struct SetIconParams { pub icon: DataUrl<'static>, } pub async fn set_icon( ctx: RegistryContext, SetIconParams { icon }: SetIconParams, ) -> Result<(), Error> { ctx.db .mutate(|db| db.as_index_mut().as_icon_mut().ser(&Some(icon))) .await .result } #[derive(Debug, Deserialize, Serialize, Parser, TS)] #[group(skip)] #[command(rename_all = "kebab-case")] #[serde(rename_all = "camelCase")] #[ts(export)] pub struct CliSetIconParams { #[arg(help = "help.arg.icon-path")] pub icon: PathBuf, } pub async fn cli_set_icon( HandlerArgs { context: ctx, parent_method, method, params: CliSetIconParams { icon }, .. }: HandlerArgs, ) -> Result<(), Error> { let data_url = DataUrl::from_path(icon).await?; ctx.call_remote::( &parent_method.into_iter().chain(method).join("."), imbl_value::json!({ "icon": data_url, }), ) .await?; Ok(()) }